Wiley.Games.on.Symbian.OS.A.Handbook.for.Mobile.Development.Apr.2008 (779888), страница 26
Текст из файла (страница 26)
The call to _Color16MU works in this instance, butwill produce garbled colors on 16 bpp displays.CFbsBitmap::GetScanLine() and CFbsBitmap::SetScanLine()Manipulating scan lines is simple, safe, and fairly efficient (it’s essentiallyjust a Mem::Copy() when the display modes match, which is anoptimized memory copy). The approach uses an intermediate buffer tostore a copy of a bitmap’s scan line, which can be manipulated at theapplication’s leisure (no need to keep a lock), and then returned to thebitmap.In order to modify a scan line, an application must first create an 8-bitbuffer that is large enough to take a full scan line (this will depend on thescreen mode).void GetScanLine(TDes8& aBuf, const TPoint& aPixel, TInt aLength,TDisplayMode aDispMode) const;The method gets the bitmap’s scan line for a line specified by a point,aPixel, and by retrieving aLength pixels into the buffer aBuf, thenconverting it (if necessary) to display mode aDispMode.
Once the scanline has been processed, it can be returned with a call to the following:void SetScanLine(TDes8& aBuf, TInt aY) const;LOADING AND MANIPULATING IMAGES97CFbsBitmap::DataAddress()CFbsBitmap::DataAddress() can be used to read and write pixeldata in a similar way to the DSA examples shown in section 3.6. As withDSA, the layout of the data must either be assumed or can be inferredfrom the display mode. Direct manipulation of a bitmap’s pixel datashould be protected, by locking the heap, using the following boilerplatecode:CFbsBitmap* myBitmap;myBitmap->LockHeapLC() // Push a lock onto the cleanup stackTUint32* dataAddress = myBitmap->DataAddress();// manipulate bitmap data calling leaving code// ...CleanupStack::PopAndDestroy(); // Unlock heapIf the manipulation code doesn’t leave, then LockHeap() andUnlockHeap() may be used instead of the leaving versions shownabove.Whenever the bitmap is read from or written to, you should ensurethat:• the code between lock and unlock is short, since other parts of thesystem will be waiting on the release of the heap lock in order to drawbitmaps• no nested calls are made to LockHeap().
This can happen whenother TBitmapUtil, CFbsBitmap, or icon drawing methods arecalled within the same thread• when using LockHeap(), if a leave can occur, UnlockHeap()must be called where it is handled, since if it is not, the heap will staylocked. This can be effected using the cleanup stack.Note that system bitmaps (usually located in the ROM) may be compressed, which means that the data will be run-length encoded (RLE).Knowing this may save you time, if you are using a system bitmapfor convenience in prototype code. Application and game bitmaps areuncompressed in RAM unless Compress() is explicitly called on thebitmap.3.9 Loading and Manipulating ImagesImages (often called bitmaps) are hugely important to games. They providethe frames of the animations, and the fonts, backdrops, and textures that98GRAPHICS ON SYMBIAN OSmake up the look and design of the game.
Images are often created by adedicated graphics artist and are usually tuned to the screen resolutionand depth of the target platform. Symbian smartphones support many ofthe popular image formats; Table 3.3 compares the compression modeand utility of the common formats.Table 3.3 Compression ratios for popular web graphics formatsFormatTypical compressionratiosMBMVery dependenton bitmap data.DescriptionMBM supports RLE encodingfor up to 32 bpp bitmaps,which works well for simplebitmaps and masks, where runsof a similar color are common.The MBM is only compressedif a ratio of 1.25:1 can beachieved.MBM is a very convenientformat for icons, color font,game tiles, sprites andanimations (with masks).MBM supports multiple imagesper file which makes the formatsuitable for simple animation.GIF4:1–10:1Lossless for images ≤ 256colors.
Works best for flatcolor, sharp-edged comic oricon art, but more compleximages tend to requiredithering.Animated GIFs are very usefulfor cartoon sequences ingames.JPEG (High)10:1–20:1High quality, with little or noloss in image quality withcontinuous tone originals.
Infact, there is usually little needfor such high quality on smallscreens.LOADING AND MANIPULATING IMAGES99Table 3.3 (continued )FormatTypical compressionratiosDescriptionJPEG(Medium)30:1–50:1This range is ideal for gamescreens, backdrops and photos.JPEG (Low)60:1–100:1Suitable for thumbnails andpreviews. Visible blockingartefacts.PNG10–30 % smallerthan GIFsPNGs behave similarly to GIFs,with up to 32-bit color and atransparency channel. Theywork best with flat-color,sharp-edged art. PNGscompress both horizontallyand vertically, so solid blocksof color generally compressbest.PNG is best suited for titles,large game graphics, detailedanimation frames, andtypefaces stored as bitmaptiles.3.9.1 Loading Native BitmapsMBMs are the most convenient graphics format on Symbian OS sincethey support multiple images per file and can be generated as part ofthe build.
Convenient functions exist in the singleton CEikonEnv (partof the application environment) to load MBM images synchronously intoCFbsBitmap objects. In order to generate an MBM, ready for use in agame, a declaration such as the following must be added to the game’sMMP file.START BITMAP nebula.mbmHEADERTARGETPATH\Resource\AppsSOURCEPATH imagesSOURCE c24 nebula.bmpENDThe order of the list of SOURCE bitmaps is significant and is preservedin the MBM. When the project is built, it will generate two files:• epoc32\resource\apps\nebula.mbm (the binary)100GRAPHICS ON SYMBIAN OS• epoc32\include\nebula.mbg (a C++ header file used to reference the contents of the MBM in source and resource files).Loading the bitmap is as simple as calling with the correct enumerationvalue as it is defined in nebula.mbg._LIT(KNebulaMbmFilename, "\\resource\\apps\\nebula.mbm");CWsBitmap* neb = iEikonEnv->CreateBitmapL(KNebulaMbmFilename(),EMbmNebulaNebula);The CWsBitmap object can then be used to draw to the SystemGc()graphics context.Although this is very useful for development, the technique only worksfor MBM files which tend to produce large files.
To load and display JPG,PNG or GIF files, they must first be decoded using the image conversionlibrary (ICL), which is the subject of the next section.3.9.2 Image Conversion Library (ICL)ICL API OverviewLibrary to link againstimageconversion.libHeader to includeimageConversion.hRequired platform security capabilities NoneKey classesCImageDecoder,CBufferedImageDecoderSymbian supports the encoding and decoding of images using the ICL.
Thelibrary is designed for converting graphics formats into CFbsBitmapsand vice versa.Multithreaded SupportThe ICL supports decoding in a separate thread in order to reduce latencyof active objects in the client’s thread. This may or may not be importantto a game. An example of where background thread decoding wouldbe beneficial is in the case of a game using the Google Maps API,where the map can be dragged around and images updated as they arereceived over the network.
If the codec processes chunks of the imagein an active object then it may cause unpredictable delays in servicingother active objects such as pointer or redraw events. This can cause anerratic, ‘jittery’ user experience. Moving the decoding into a dedicatedLOADING AND MANIPULATING IMAGES101thread would allow it to continue in the background without impactingthe latency of UI events.The ICL is a multimedia framework which depends on plug-ins assembled by the handset manufacturer.
Some of the plug-ins may use hardwareacceleration, but that’s less common, since hardware may limit thenumber of simultaneously decoding JPEGs (which happens when webbrowsing).Loading ImagesThe ICL encodes and decodes images asynchronously, so clients musthandle a completion event when an image has been decoded. Theexample code shows how to wrap up an image decoding operation in anactive object class called CGfxImageDecoder.class CGfxImageDecoder : public CActive{public:CGfxImageDecoder(CFbsBitmap* aBitmap, CFbsBitmap* aMask,MGfxImageDecoderHandler& aHandler);∼CGfxImageDecoder();void LoadImageL(const TDesC& aFilename);protected:void RunL();void DoCancel();private:CImageDecoder* iDecoder;CFbsBitmap* iBitmap; // not ownedMGfxImageDecoderHandler& iHandler;RFs iFs;};The decoder is constructed by passing a pointer to an empty bitmaphandle (one which has not had Create() called on it yet).
The examplecode uses a MGfxImageDecoderHandler mixin to signify imageloading completion via a callback.CGfxImageDecoder::CGfxImageDecoder(CFbsBitmap* aBitmap, CFbsBitmap*aMask, MGfxImageDecoderHandler& aHandler): CActive(0),iBitmap(aBitmap),iHandler(aHandler){CActiveScheduler::Add(this);}In order to load an image, the decoder is instantiated with the imagefile using CImageDecoder::FileNewL() and queried in order to getthe size and depth of the image as a TFrameInfo object.102GRAPHICS ON SYMBIAN OSvoid CGfxImageDecoder::LoadImageL(const TDesC& aFilename){User::LeaveIfError(iFs.Connect());iDecoder = CImageDecoder::FileNewL(iFs, aFilename);TFrameInfo info = iDecoder->FrameInfo();// Match the format with an in memory bitmapUser::LeaveIfError( iBitmap->Create(info.iOverallSizeInPixels,info.iFrameDisplayMode) );iDecoder->Convert(&iStatus, *iBitmap);SetActive();}The TFrameInfo values are used to create a new bitmap to matchthe size and depth of the image.
It is possible to supply different sizes anddepths, but this will cause the conversion process to reduce the numberof colors and/or scale down the image to fit the supplied bitmap. If theimage needs to be converted to the current display mode anyway, thenit’s best to supply for the current screen mode when creating the bitmap.The final step is to initiate the Convert() and wait for the operationto complete asynchronously. On completion (successful or otherwise),RunL() is called and, in this example, the status is passed directly ontothe handler.void CGfxImageDecoder::RunL(){iHandler.GfxImageLoadedCallBack(iStatus.Int());}Using the Graphic Loading WrapperThe CGfxImageDecoder described above is trivial to use. A view orCoeControl-derived object can use the following code to start loadingthe image (assuming the image file robot.jpg exists on the C: drive).void CGfxIclView::StartL(){iLoadingBmp = new(ELeave) CFbsBitmap();iImageDecoder = new(ELeave) CGfxImageDecoder(iLoadingBmp, NULL,*this);_LIT(KRobotFileName, "c:\\data\\images\\robot.jpg");iImageDecoder->LoadImageL(KRobotFileName);}The following method is called on completion:void CGfxIclView::GfxImageLoadedCallBack(TInt aError){LOADING AND MANIPULATING IMAGES103iBitmap = iLoadingBmp;iLoadingBmp = NULL;DrawNow();}The Draw() function only attempts to draw the bitmap when it hassuccessfully loaded (hence the use of iLoadBmp as a temporary handle).This way Draw() can be safely called while the JPG is loading – thecontrol will be cleared as seen in the code below.void CGfxIclView::Draw( const TRect& /*aRect*/ ) const{// Get the standard graphics contextCWindowGc& gc = SystemGc();// Get the control's extentTRect drawRect( Rect());// Clear the screengc.Clear( drawRect );if(iBitmap){gc.BitBlt(TPoint(0,0), iBitmap);}}Figure 3.16 shows a JPG loaded using the sample code above.Figure 3.16 A JPG image of a robot loaded using the ICL as shown in the sample codePre-loading Multiple ImagesGames tend to contain a lot of graphics, and processing all the graphicsfiles is usually a large contributor to the time taken to load a game (or a104GRAPHICS ON SYMBIAN OSlevel in a game).