Wiley.Games.on.Symbian.OS.A.Handbook.for.Mobile.Development.Apr.2008 (779888), страница 23
Текст из файла (страница 23)
There is not much difference in this code compared with using awindow graphics context.Figure 3.7 The image resulting from code running on S60 that uses a DSA graphicscontext. The code draws scrolling green vertical bars3.6.3 Writing Pixel Data to the ScreenIn order to write pixel data directly to the screen, the base address ofthe screen is required to calculate the address of individual rows andpixels. As with all direct access techniques, manipulating the pixel datarequires knowledge of the layout of the data (see section 3.8 for moreinformation).80GRAPHICS ON SYMBIAN OSOver the various releases of Symbian OS, there have been a few waysof retrieving information required to directly address the screen; most ofthese methods are deprecated or removed. Unfortunately, there is a lot ofvariability between devices (including the emulator) and manufacturers.The example code below presents a method which should scale acrossvarious devices and which also works in the emulator.The code is encapsulated in the method SetupScreenDataL().
Itfirst queries the DSA for a hardware bitmap representing the screen.If the bitmap is not accessible, the screen attributes are retrieved fromthe HAL (hardware abstraction layer). In practice, the emulator supportshardware bitmaps, but target device support is unlikely. To deal withthis, SetupScreenDataL() reads and normalizes all values into a datastructure, SScreenData (defined in the example code), which thenallows all the other code to work independently of the implementation.struct SScreenData{TInt iDisplayMemoryAddress;TInt iDisplayOffsetToFirstPixel;TInt iDisplayXPixels;TInt iDisplayYPixels;TInt iDisplayMode;TInt iDisplayOffsetBetweenLines;TInt iDisplayBitsPerPixel;TBool iDisplayIsPixelOrderLandscape;};void CGfxDirectAccess::SetupScreenDataL(){Mem::FillZ(&iScreenData, sizeof(iScreenData));RHardwareBitmap hwBmp =iDirectScreenAccess->ScreenDevice()->HardwareBitmap();TAcceleratedBitmapInfo bmpInfo;hwBmp.GetInfo(bmpInfo);// If the platform supports s/w accessible hardware bitmap// (emulator?) otherwise get the values directly from the HALif( hwBmp.iHandle && (bmpInfo.iSize.iWidth!=0) ){iScreenData.iDisplayMemoryAddress = (TInt)bmpInfo.iAddress;iScreenData.iDisplayOffsetToFirstPixel = 0;iScreenData.iDisplayXPixels = bmpInfo.iSize.iWidth;iScreenData.iDisplayYPixels = bmpInfo.iSize.iHeight;iScreenData.iDisplayBitsPerPixel = 1 << bmpInfo.iPixelShift;iScreenData.iDisplayOffsetBetweenLines = bmpInfo.iLinePitch;}else{// The display mode must be set as the incoming paramater forDIRECT SCREEN ACCESS81// all other values to return correct figures// The display mode is used to retrieve all the other paramsUser::LeaveIfError(HAL::Get(HALData::EDisplayMode,iScreenData.iDisplayMode);// Incoming parameters to HAL must now contain iDisplayModeiScreenData.iDisplayOffsetToFirstPixel =iScreenData.iDisplayMemoryAddress =iScreenData.iDisplayXPixels =iScreenData.iDisplayBitsPerPixel = iScreenData.iDisplayMode;User::LeaveIfError(HAL::Get(HALData::EDisplayMemoryAddress,iScreenData.iDisplayMemoryAddress));// Repetitive code is omitted.// HAL is used to fill the SScreenData structure...}The following code extract demonstrates how to fill the screen withRGB values based on screen mode of EColor16MU.
In the code below,iDsaRect defines the screen coordinates of the rectangle representingthe window under DSA control and iFrameCounter is simply an integerthat gets incremented on each frame. The code simply varies the RGBvalues of each pixel by using the iFrameCounter variable to influencethe color.void CGfxDirectAccess::ProcessFrameWritePixels(){TUint32* screenAddress =(TUint32*)(iScreenData.iDisplayMemoryAddress);const TInt limitX = iDsaRect.Width();const TInt limitY = iDsaRect.Height();TInt rowWidthInBytes = iScreenData.iDisplayOffsetBetweenLines;const TInt strideInWords = iScreenData.iDisplayOffsetBetweenLines>>2;TUint32* lineAddress = screenAddress;lineAddress += (strideInWords*iDsaRect.iTl.iY);for(TInt y=0; y<limitY ; y++){TUint32* p = lineAddress + iDsaRect.iTl.iX;for(TInt x=0; x<limitX; x++){TRgb rgb((y+iFrameCounter);*(p++)=rgb._Color16MU();}lineAddress += strideInWords;}}For each row in the DSA rectangle, the address of the first pixel iscalculated and stored in variable p which is used to write 32-bit values82GRAPHICS ON SYMBIAN OSFigure 3.8 A screenshot from the DSA pixel write example.
The code draws scrolling redhorizontal barsrepresenting the RGB of each pixel. Variable p is incremented by 32 bitseach time so that it always points to the next pixel. This is illustrated inFigure 3.8.3.6.4 Updating the Screen DeviceAs I mentioned in section 3.3.3, the LCD may have an internal screenbuffer separate from the frame buffer exposed by DSA. When not usingthe graphics context, the screen device must be notified of which regionsof the frame buffer have been modified.void CGfxDirectAccess::EndDraw(){TRegionFix<1> reg(iDsaRect); // Create a one rectangle regioniDirectScreenAccess->ScreenDevice()->Update(reg);iClient.Flush();}Update should always be called to ensure that the Symbian framebuffer is copied to the LCD memory (on systems with such hardware).
Onthe emulator, calling Update() causes the frame buffer to be flushed tothe underlying Microsoft Windows GUI window.3.6.5 Handling a Change in WindowsWhen the DSA session was created, it required an MDirectScreenAccess derived object to be supplied to the constructor. The MDirectScreenAccess mixin contains two callbacks, AbortNow() andRestart(), as previously described.DOUBLE BUFFERING AND ANTI-TEARING83Once an AbortNow() callback is invoked, the DSA session becomesinvalid (including any GC and screen device objects obtained fromCDirectScreenAccess) and must not be used any more.void CGfxDirectAccess::AbortNow(RDirectScreenAccess::TTerminationReasons aReason){Cancel();}When it is safe to resume drawing, Restart() will be called and theclient should call CDirectScreenAccess::StartL() in order to setup a new DSA and GC.
Note that StartL() can leave, for example,through lack of memory. If a leave occurs, direct screen access cannot berestarted. For this reason, calling StartL() from Restart() requiresit to be called within a TRAP().void CGfxDirectAccess::Restart(RDirectScreenAccess::TTerminationReasons aReason){if(iGcDrawMode){TRAP_IGNORE(StartL()); // absorb error}}In the example code, the DSA is only restarted if the GC drawingmode is used since GC drawing supports clipping (the direct mode wouldoverwrite overlapping windows).StartL() recalculates the clipping region so that if direct screenaccess was aborted because another window appeared in front of it,that window will not be overwritten when direct screen access resumes.Dealing with partial redraws when writing pixels to the screen is verycomplex and offers little gain.
For a game, it’s wise to pause and let theuser resume. In the example code, the timer is canceled, which stops theredraw.Note that no other window server functions are called within AbortNow(), because that would cause a dead lock, since the window serversession is synchronously waiting. Calls to window server APIs can beresumed when the Restart() callback comes through.3.7 Double Buffering and Anti-TearingDouble buffering is an established technique for drawing flicker-freegraphics.
It is an essential idiom used for rendering fluid motion inanything from drag and drop puzzle games to high speed shoot ‘em84GRAPHICS ON SYMBIAN OSups. This section discusses the problem that double buffering solves andintroduces the concept of tearing, and the methods of preventing tearing.3.7.1 Buffering TechniquesTable 3.1 shows the buffering techniques available on Symbian OS.Table 3.1 Buffering schemes and their implementation on Symbian OSBufferschemeDescriptionImplementationon Symbian OSSinglebufferingA single frame buffer of pixel datais used to draw to the screen.Direct screen access (DSA),although on systems thatrequire an Update() toflush the frame buffer, theresults will be doublebuffered to an extent.DoublebufferingThis scheme uses an off-screenbuffer, also known as a backbuffer. Double buffering allowsslower drawing operations to beperformed on the off-screenbuffer.
The contents of theoff-screen buffer can then bequickly copied, or ‘blitted’, to thecurrent frame buffer, which is alsoknown as the front buffer.Off-screen bitmap. Forsynchronized drawing withanti-tearing, it’s best to useCDirectScreenBitmap.PageflippingPage flipping avoids the need tocopy pixel data from one buffer toanother. It works by swapping theaddresses pointing to the backbuffer and the front buffer.