Sometimes you need to draw some graphics (lines, rectangles, images) on top of the Symbian application. When this graphics refreshes often (like animation) you need create some secondary bitmap buffer where the next frame would be drawn.
The primary and secondary buffers may then be switched.
This is important because if to draw different picture elements to the main buffer (which continuously is mapped onto the screen of phone), user will watch flicker image. The same would be if try erasing the main buffer and then draw new frame of animation on it.
Use of the second bitmap can be attributed to the technology of "
Dual buffering".
Let's look how can we realize this technique in a Symbian GUI project.
First, I'll abstract from the whole application and will show the core of creating and manipulating the buffer.
Secondary we will tie this to a simple 'Hello World!' GUI project which is available in Carbide.c++ IDE.
=== The Core ===
Let us create a Symbian C++ class that will have necessary properties and methods to organize the buffer. And also to draw to the buffer. That would be the
RBitmapBuffer class.
Listing 1.
class RBitmapBuffer
{
public:
// Some constructors and cleanup stack operations omitted for clarity
The properties would be:
CFbsBitmap* iBitmapBuffer ; // Contains the bitmap whis is our buffer
CFbsBitGc * iDrawer ; // CFbsBitGc - the class to draw on Bitmap device
CFbsBitmapDevice* iBmpDevice ; // BitmapDevice is a mediator between the drawer and the buffer.
TRect iSizeOfScreenRect; // The size of the screen
TDisplayMode* pDisplayMode ; // The display mode of the buffer. In general this represents the color mode.
=============
The methods would be:
/* Two-phased constructor. */
RBitmapBuffer* RBitmapBuffer::NewL(const TDisplayMode&, const TRect& )
{
//omitted for clarity
//call to ConstructL
}
/* Initialize renderer */
void
RBitmapBuffer::
ConstructL(
const TDisplayMode& aDisplayMode, const TRect& aSizeOfScreenRect)
{
setSizeOfScreen(aSizeOfScreenRect);
setDisplayMode (aDisplayMode);
iBitmapBuffer = new (ELeave) CFbsBitmap;
iBitmapBuffer ->Create(iSizeOfScreenRect.Size() , *pDisplayMode);
iDrawer = CFbsBitGc::NewL();
iBmpDevice = CFbsBitmapDevice::NewL(iBitmapBuffer);
// Activate virtual bitmap device
iDrawer -> Activate(iBmpDevice);
iDrawer -> SetPenColor(KRgbBlack);
/*
See the description below
*/
} // function
/* Sets display mode of the buffer */
void RBitmapBuffer::setDisplayMode(const TDisplayMode& aDisplayMode)
{ pDisplayMode = (TDisplayMode*) &aDisplayMode; }
/* Sets sizeOfScreeen property */
void RBitmapBuffer::setSizeOfScreen(const TRect& aSizeOfScreenRect)
{ iSizeOfScreenRect = aSizeOfScreenRect; }
/* Returns the buffer pointer so any other class can use it */
CFbsBitmap* RBitmapBuffer::getBitmapBuffer()
{ return iBitmapBuffer; }
/* Closes (deallocates) all the resources */
CFbsBitmap*
RBitmapBuffer::
Close()
{
delete iBitmapBuffer ; // Contains the bitmap whis is our buffer
delete iDrawer ; // CFbsBitGc - the class to draw on Bitmap device
delete iBmpDevice ; // BitmapDevice is a mediator between the drawer and the buffer.
}
} // class
Let's look through the
ConstructL() method which prepares the bitmap buffer.
1. Set screen size.
2. Set display mode.
3. Allocate the object of
CFbsBitmap class.
4. Initialize it by
Create() method as empty buffer.
5. Allocate the
CFbsBitGc and
CFbsBitmapDevice objects to allow drawing to the bitmap buffer.
6. Show the
iDrawer the device that it can draw. The device is
iBmpDevice which is initialized with
iBitmapBuffer object (the actual buffer).
7. Set the color of of drawer.
Now we can inherit the RBitmapBuffer class to create a new
CBufferRenderer class which will draw something to the buffer.
Let's see:
Listing 2.
class CBufferRenderer : public CBase, public RBitmapBuffer
{
public:
=============
The methods (except the constructors):
void CBufferRenderer::drawRect(TRect *aRect)
{
// Use parent's drawer mechanism
iDrawer->DrawRect ( *aRect );
}
void CBufferRenderer::~CBufferRenderer()
{
Close();
}
} // class
After instantiating the
CBufferRenderer class we can draw some rectangles on it (or lots of other picture elements and bitmaps) and then return the buffer to the main program that will substitute the old screen image with the new one. The code will look like below:
Listing 3.
void mainDrawer()
{
TDisplayMode displayMode = iEikEnv->WsSession().GetDefModeMaxNumColors( color, gray );
TRect screenSize (10, 10, 20, 20);
TRect myRectangle (10, 10, 20, 20);
CBufferRenderer* myBackBuffer;
myBackBuffer = CBufferRenderer::NewL(screenSize, displayMode);
CleanupStack :: PushL(myBackBuffer);
myBackBuffer -> drawRect(&myRectangle);
someMainProgramDrawer -> (myBackBuffer->getBitmapBuffer);
CleanupStack :: Pop(myBackBuffer);
delete myBackBuffer;
} // function
Today I've shown the idea of the main core of the bitmap buffer.
This example has some
pseudo code (the main program is not defined).
At the next post I'll show how to tie the RBitmapBuffer with the real Symbian GUI application.
However here is a list of the headers and libraries you will need to include to the project to use the above graphics classes:
Listing 4.
fbscli.lib
bitgdi.lib
ws32.lib
.
Re: Bitmap manipulation: Image buffer
ltomuta | 13/06/2008, 19:28
For those interested in the topic but willing a more standard/Open C++ approach they should have a look at the recently published RGA framework, it also provides back buffer implementation.