quick_recipes (779892), страница 37
Текст из файла (страница 37)
If, for example, you have aneditor inside your own control, then you need to forward all necessarykey events in your container control’s OfferKeyEventL() function.208SYMBIAN C++ RECIPES4.5.3 Advanced Recipes4.5.3.1Use Off-Screen Images for DrawingAmount of time required: 15 minutesLocation of example code: \Graphics_ImagesRequired library(s): efsrv.lib, bitgdi.lib, ws32.libRequired header file(s): f32file.h, bitstd.h, bitdev.h,w32std.hRequired platform security capability(s): NoneProblem: You want to implement double-buffering to ensure flicker-freegraphics.Solution: Double-buffering is an established technique for drawing flickerfree graphics. It is an essential idiom used for rendering fluid motionin anything from drag and drop puzzle games to high-speed shoot‘em ups.Double-buffering makes use of an offscreen bitmap.
The first thing todo is construct a bitmap, and then create a graphics context from whichwe can use the actual drawing functions. To get a graphics context,we need to have a screen device made. The code for this part is asfollows:iBitmap = new(ELeave)CFbsBitmap();iBitmap->Create(ImgSize, EColor16M);iBitmapDevice = CFbsBitmapDevice::NewL(iBitmap);User::LeaveIfError(iBitmapDevice->CreateContext(iGraphicsContext));After these four lines are executed, we can use the function providedby iGraphicsContext (which is CFbsBitmapDevice) to draw intothe iBitmap (which is CFbsBitmap), and then just call DrawNow().WSERV will generate a redraw event and the container’s Draw() functionis called, in which you can use graphics context as usual to draw theiBitmap image onto the screen.Discussion: The most usual cause of graphics flickering is drawing thesame pixels multiple times over one draw event.
The easiest way to avoidflickering is to make the draw function code in such a way that all pixelsare drawn a minimum time over one draw event – basically what thismeans is not calling Clear(), DrawRect(), etc. for areas that will bedrawn later on in the same draw event.As can be seen in the example code in AnimationContainer.cpp,off-screen bitmaps can be used to draw GIF animation frames into animage that can be shown on the screen.GRAPHICS AND DRAWING209Tip: To avoid distortion in the image as well as to make the actualscreen drawing code faster, you should construct the off-screen imageto be the same size as the actual physical screen size in pixels.4.5.3.2Load GIF Animation ImagesAmount of time required: 25 minutesLocation of example code: \Graphics_ImagesRequired library(s): bitgdi.lib, ws32.lib, imageconversion.lib, apgrfx.lib, apmime.libRequired header file(s): bitstd.h, w32std.h, imagedata.h,imageconversion.h, apgcli.h, apmrec.hRequired platform security capability(s): NoneProblem: You want to load and draw all frames from a GIF image.Solution: You can use the CImageDecoder class also for loading GIFfiles, and other images that have multiple frames in them.
Sometimes itmight even be better for the application architecture to load MBM/MIFfiles with active object loops to enable event receiving while loadingmultiple frames.To load GIF images, you first need to construct a CImageDecoderinstance, as you did in Recipe 4.5.2.1. You should then check the amountof frames on it:iImageDecoder =CImageDecoder::FileNewL(iFsSession,aFileName,KtxTypeImageGif_8);iCurrCount = iImageDecoder->FrameCount();And then start looping the frame reading:void CAniFrame_Reader::NextImageL(void){if(iCurrCount > 0 && !IsActive()){iCurrImg++;// add 1 to the index, so we can read the next frameif(iCurrImg >= iCurrCount || iCurrImg < 0){// in case the index is non-valid, we set it to zero// should only happen after reading the last frameiCurrImg = 0;}delete iFrameImg;iFrameImg = NULL;iFrameImg = new(ELeave) CFbsBitmap();iFrameImg->Create(iImageDecoder->FrameInfo(iCurrImg).iOverallSizeInPixels,210SYMBIAN C++ RECIPESiImageDecoder->FrameInfo(iCurrImg).iFrameDisplayMode);delete iFrameMsk;iFrameMsk = NULL;iFrameMsk = new(ELeave) CFbsBitmap();iFrameMsk->Create(iImageDecoder->FrameInfo(iCurrImg).iOverallSizeInPixels, EGray2);iImageDecoder->Convert(&iStatus, *iFrameImg,*iFrameMsk,iCurrImg);SetActive();}}Since the Convert() function is asynchronous, RunL() will becalled when it has finished decoding the current frame.
With GIF imageframes, some frames are not full frames, but actually only have the pixelsthat have changed since the previous frame. Also, the frame might needto be drawn in another position than the full image rectangle. To makesure of what is drawn and where, you need to check the informationstored in the frame information object for the currently loaded frame:void CAniFrame_Reader::RunL(){if(iFrameMsk && iFrameImg){if(iFrameMsk->Handle() && iFrameImg->Handle()){TBool reDraw(EFalse);TRect drawArea(TPoint(0,0),iFrameImg->SizeInPixels());if(TFrameInfo::ERestoreToBackground &iImageDecoder->FrameInfo(iCurrImg).iFlags){reDraw = ETrue;}else{drawArea = iImageDecoder->FrameInfo(iCurrImg).iFrameCoordsInPixels;}iCallBack.AppendFrameL(*iFrameImg,*iFrameMsk,DrawArea,ReDraw);}}}With this example, the code for the frame and the instructions onwhere to draw it are directed to another level which, for example, can beimplemented with off-screen bitmap drawing:void CAnimationContainer::AppendFrameL(CFbsBitmap& aImage, CFbsBitmap&aMask, TRect aArea, TBool aReDraw){GRAPHICS AND DRAWING211if(iGraphicsContext){TSize imgSize(aImage.SizeInPixels());if(aReDraw){iGraphicsContext->DrawBitmap(TRect(0,0, imgSize.iWidth,imgSize.iHeight), &aImage);}else{iGraphicsContext->DrawBitmapMasked(aArea, &aImage,TRect(0, 0, imgSize.iWidth, imgSize.iHeight),&aMask, EFalse);}}DrawNow();}As you can see from the code samples, if the frame information flags aredefining that the background should be restored (with a ERestoreToBackground flag), the mask is not used, but the image is drawn withoutit.
This usually happens at least with the first frame, so the animation canstart from fresh.Tip: The example code uses a defined interval for timing the animation. You could also read the timing information from the frameinformation and get the frames timed exactly as they were designed tobe handled.What may go wrong when you do this: With this example old framesare not saved, to optimize the speed storage, so you don’t need tokeep loading them when looping the animation. But you need toremember that the maximum default heap is 1 MB, and if you exceedyour application’s maximum heap, you will get ‘Out of memory error’.For example, 20 frames on 320 × 240 sized images with 24 bits/pixelwill take a little over 4.6 MB of memory.4.5.3.3Draw Skins as Backgrounds (S60 Only)Amount of time required: 20 minutesLocation of example code: \Graphics_ImagesLibaries: aknskins.lib, aknskinsrv.lib,aknswallpaperutils.lib212SYMBIAN C++ RECIPESRequired header file(s): aknsbasicbackgroundcontrolcontext.h, aknsskininstance.h, aknsdrawutils.hRequired platform security capability(s): NoneProblem: You want to have the background drawn with the currentlyselected skin.Solution: The skin is drawn to a container by calling CAknsBasicBackgroundControlContext, so first you need to construct one:iBgContext =CAknsBasicBackgroundControlContext::NewL(KAknsIIDQsnBgAreaMain,TRect(0, 0, 1, 1), ETrue);The first argument defines which skin item is used, the second onedefines the area to be used and the third defines whether the parentcontrols (i.e., the control you wish to draw the skin to); the relativeposition to the screen should also be taken into account.As you can see, the rectangle for the object is not set here.
This is tomake sure it is always set correctly and as you can see from the example,the object is constructed before the rectangle area for the container isset. This means that as soon as the rectangle area for the container is setthe SizeChanged() function will be called, and in this function youshould always set the correct position and area:if ( iBgContext ){iBgContext->SetRect(Rect());if ( &Window() ){iBgContext->SetParentPos( PositionRelativeToScreen() );}}After this, you are ready to draw the skin into your container’s background:void CMySplashContainer::Draw(const TRect& /*aRect*/) const{CWindowGc& gc = SystemGc();MAknsSkinInstance* skin = AknsUtils::SkinInstance();AknsDrawUtils::Background(skin, iBgContext, this, gc, Rect());}Note that to make the skins actually work, you also need to implementthe MopSupplyObject() function in your container:TTypeUid::Ptr CMySplashContainer::MopSupplyObject(TTypeUid aId){GRAPHICS AND DRAWING213if (iBgContext){return MAknsControlContext::SupplyMopObject(aId, iBgContext );}return CCoeControl::MopSupplyObject(aId);}What may go wrong when you do this: The first argument forthe CAknsBasicBackgroundControlContext defines which skinitem is used.
Thus if you select the wrong background, your skin mightnot look ideal for the situation. In case you have problems with it,check the AknsConstants.h header file for different options.4.5.3.4Draw Outside the Symbian OS Application FrameworkAmount of time required: 40 minutesLocation of example code: \Graphics_NonAFWRequired library(s): ws32.lib, gdi.lib, apgrfx.libRequired header file(s): w32std.h, gdi.h, apgwgnam.hRequired platform security capability(s): NoneProblem: You want to draw without the application framework.Solution: When constructing GUI applications without using the standardapplication framework, the first task is to construct the screen device,which presents the device’s physical display.