During application development, you must often manage various graphic elements, such as images and icons. Fortunately, C++ Builder provides the developer with several tools that use these graphic elements. In this article, we'll study the concept of offscreen bitmaps and the double-buffering technique. We'll use these tools to help us efficiently manage the graphics in our program.
void __fastcall
TForm1::buttonNormalClick
(Tobject *Sender)
{
}
We'll add the code that produces the negative image here.
The steps to achieve a negative image are very simple. First,
you must get the color value of each pixel in the image; then
you must invert every bit. Afterwards, the new color value will
be stored at the same coordinates (x, y) of the old value and displayed
on screen.
You can read individual pixels simply by using the Pixels property.
Just access the Pixels property of the canvas, letting the indexes
be the x- and y-coordinates of the pixel you want to read: Image->Picture->Bitmap->Canvas->Pixels[x][y]. Remember that the upper left corner of the canvas is the origin of the coordinate system.
Once you've gained the color, you must do a bitwise exclusive-OR
between this value and 0x00ffffff to obtain the inverted color
(Thankfully, Windows uses only 24-bits to manage colors). So,
for a single pixel, you'll write a line of code similar to this one:
Image1->Picture->Bitmap->Canvas->Pixels [x][y]= Image1->Picture->Bitmap-> Canvas->Pixels[x][y] ^ 0x00ffffff;Because we must execute this operation for each pixel of the image, we need to scan the entire bitmap using two loops. The first loop is for the horizontal size, and the second is for the vertical size. You can determine these values using the properties Image1-> Picture->Width and Image1->Picture->Height. The final code in the buttonNormalClick method should resemble Listing A. Make sure you add all of the highlighted code.
Listing A: The buttonNormalClick method
void __fastcall TForm1::buttonNormalClick
(TObject *Sender)
{
Screen->Cursor = crHourGlass;
for (int y=0; yPicture->
Height; y++)
for (int x=0; xPicture->
Width; x++)
Image1->Picture->Bitmap->Canvas->
Pixels [x][y] =
Image1->Picture->Bitmap->Canvas->
Pixels [x][y] ^0x00ffffff;
Screen->Cursor = crArrow;
}
To designate the beginning and the end of the graphic
operation within the function, I inserted two lines of
code. The first line, Screen->Cursor = crHourGlass,
marks the start of the operation by changing the mouse
cursor to a sand-glass. The second line, Screen->Cursor
= crArrow, marks the end of the operation by setting
the mouse cursor to the normal arrow shape.
Now you're ready to test the application for yourself.
First, press the [F9] key to compile and run the program.
Next, press the Normal button and watch how much time the
application takes to finish the graphic operation.
(As a reference, the program takes about 10 seconds
to produce the negative image on my P166.) Of course,
we can't consider this result a good performance, especially
because we used a really simple filter.
The performance is slow because of the tremendous number of
calls necessary to invert the Pixels property. Each time a
pixel is read, it's immediately displayed on the screen.
Since the sample image is 400x300 pixels, there are a total
of 240,000 calls to the Pixels property (each pixel requires
one call to read the color and one to write the inverted color)
and a total of 120,000 pixels printed. To avoid the performance
hit involved in such large numbers, you can use a technique called
double-buffering, which we'll discuss next.
Figure B: The original image is copied, updated, copied back, and redisplayed.
Now, let's see how we implement this very useful technique. First, note that you'll use an offscreen bitmap as a buffer. We can describe an offscreen bitmap as simply a work area not visible on the form itself; it also serves as the destination of some graphic operations. The offscreen bitmap stores the changes to the original image. Using a non-visible working area offers many advantages. A primary benefit occurs because a non-visible work area greatly reduces the execution time needed by graphic operations. To create an offscreen bitmap, you add its declaration to the definition of your form, like so:
… Public: Graphics::TBitmap *DoubleBuffer; …You also must remember to initialize and destroy DoubleBuffer when the application starts and ends. So, select Form1 and switch to the Object Inspector. Locate the OnCreate event and double-click it. Then add the following highlighted code into the Code Editor:
void __fastcall TForm1::FormCreate
(TObject *Sender)
{
DoubleBuffer = new Graphics::TBitmap;
}
Now repeat the preceding steps for the OnDestroy event, and add this line of code:
void __fastcall TForm1::FormDestroy
(TObject *Sender)
{
delete DoubleBuffer;
}
Next, double-click the buttonDoubleBuffer button to create
its OnClick event shell. We'll use the double buffer technique
to place the code that produces a negative image inside the
buttonDoubleBufferClick method. To do so, we'll use three logical steps:
DoubleBuffer->Assign (Image1->Picture->Bitmap);The second step is very easy to develop. You can reuse the same code that lies inside the buttonNormalClick method. Of course, in this case, you must use DoubleBuffer instead of Image1->Picture->Bitmap. The last step is the opposite of the first one. You assign DoubleBuffer to Image1->Picture->Bitmap. The finished buttonDoubleBufferClick method should resemble Listing B. Be sure you add all the highlighted code.
Listing B: The buttonDoubleBufferClick method
void __fastcall TForm1::buttonDouble
BufferClick(TObject *Sender)
{
DoubleBuffer->Assign
(Image1->Picture->Bitmap);
Screen->Cursor = crHourGlass;
for (int y=0; y
Height; y++)
for (int x=0; x
Width; x++)
DoubleBuffer->Canvas->Pixels
[x][y] =
DoubleBuffer->Canvas->Pixels
[x][y] ^ 0x00ffffff;
Screen->Cursor = crArrow;
Image1->Picture->Bitmap->Assign \
(DoubleBuffer);
}
Now it's time to see the offscreen bitmap technique at work.
Compile and run the application, and press the DoubleBuffer button.
How much time does the application take to produce the negative image?
On my PC, it takes less than two seconds! The graphic operation now
is about five times faster than before.