Board index » cppbuilder » Offscreen bitmaps and DIB bitmaps

Offscreen bitmaps and DIB bitmaps

    Could someone explain offscreen bitmaps and device independant bitmaps?
I had a problem with flickering a while ago, and had someone explain a
method to fix it but I can't get anywhere. Basically, I have a tile based
drawing program where the user makes a picture by placing tiles. They can
then move these tiles around etc. Problem is, once I get plenty of tiles on
the canvas, it gets a bit sluggish as it has to redraw the entire canvas so
much, redrawing every tile (like for example if I am {*word*221} banding while
line drawing). From what I understand, I can draw everything onto an
offscreen bitmap, then just draw that entire bitmap onto the canvas, thus
reducing it from many onscreen draws to just one. But then how to I take
that bitmap and put it onto the canvas so it appears exactly as it would if
I had just painted it onto the canvas in the first place? I use the mouse
coordinates on the canvas to determine which tile needs to be moved, so it
is important that various aspects of the bitmap fall in the correct spot
when I transfer it back to the canvas. Anyway, sorry for the long post, but
im kind of stuck. Anymore information in this area would help, or if anyone
has ideas for a solution that would also be great.

TIA
Adam.

 

Re:Offscreen bitmaps and DIB bitmaps


Hi Adam,

Quote
> Could someone explain offscreen bitmaps and device independant
> bitmaps?

The term "offscreen bitmap" is usually used to refer to a
device-dependent bitmap (or a DIB section bitmap) that has been
selected into a memory device context.  This bitmap serves as a
so-called "back buffer" to which all drawings are made.  It's called
"offscreen" because it sits in the back, accruing the drawings until
you're ready to display them.  So, instead of drawing directly to the
screen, you direct all your drawing code to this "back buffer"
bitmap.  When you are ready to display your drawings, you simply draw
this "back buffer" bitmap to the "front buffer", the latter of which
is your display (i.e., a display device context; e.g.,
Form1->Canvas).  In BCB, you can create an offscreen bitmap by using a
separate TBitmap object or by using the TBitmap object that's part of
a TImage object whose Visible property is set to false.

A device-independent bitmap (DIB) is nothing more than a block of
memory that contains three parts: a header (BITMAPINFOHEADER,
BITMAPV4HEADER, or BITMAPV5HEADER), an optional color table (an array
of RGBQUADs or WORDs), and an array of pixels.  When stored in a file,
a DIB is preceded by a BITMAPFILEHEADER structure.

Quote
> Basically, I have a tile based drawing program where the user
> makes a picture by placing tiles. They can then move these tiles
> around etc. Problem is, once I get plenty of tiles on the canvas,
> it gets a bit sluggish as it has to redraw the entire canvas so
> much, redrawing every tile (like for example if I am {*word*221}
> banding while line drawing).

90% of the time, flickering is caused by unnecessary erasing.  See,
each window receives a message called WM_PAINT, which instructs the
window to paint its contents.  In reponse to this message, most
windows call the BeginPaint() function, which sends the window the
WM_ERASEBKGND message telling it to erase its background so that any
subsequent drawing will be made to a "clean slate".  In response to
the WM_ERASEBKGND message, most windows use the FillRect() function to
fill their backgrounds with a solid color (e.g., clBtnFace).  After
the background is "erased" the window continues processing the
WM_PAINT message, in reponse to which it paints its contents.  You can
see that this scheme is a bit inefficient, because there's no need to
erase the portions that you're going to draw over anyway.  For
example, if you have a TImage component that occupies the entire
client area of a Form, there's no need for the Form to fill its
background because the Image will be drawn over it.  In short, if
you're seeing a flash between the color of the Image's Parent
component (e.g., clBtnFace of Form1) and the contents of your Image,
then you should trap the WM_ERASEBKGND message (at the level of your
Image's Parent window), and then fill only the areas that you're not
going to draw over.  For example, if your Image is parented to Form1,
you'd do the following...

// in header...
private:           // User declarations
   MESSAGE void __fastcall WMEraseBkgnd(TMessage& AMsg)
   {
      const HDC hDC = reinterpret_cast<HDC>(AMsg.WParam);
      HBRUSH hBrush = Brush->Handle;

      // left edge
      RECT RFill = {0, 0, Image1->Left, ClientHeight};
      FillRect(hDC, &RFill, hBrush);

      // bottom edge
      RFill.left = Image1->Left;
      RFill.top = Image1->Top + Image1->Height;
      RFill.right += Image1->Width;
      FillRect(hDC, &RFill, hBrush);

      // top edge
      RFill.top = 0;
      RFill.bottom = Image1->Top;
      FillRect(hDC, &RFill, hBrush);

      // right edge
      RFill.left += Image1->Width;
      RFill.right = ClientWidth;
      RFill.bottom = ClientHeight;
      FillRect(hDC, &RFill, hBrush);      
   }
public:         // User declarations
   __fastcall TForm1(TComponent* Owner);

BEGIN_MESSAGE_MAP
   MESSAGE_HANDLER(WM_ERASEBKGND, TMessage, WMEraseBkgnd)
END_MESSAGE_MAP(TForm)  

Quote
};

You can also do this by using clipping regions, like so...

private:           // User declarations
   MESSAGE void __fastcall WMEraseBkgnd(TMessage& AMsg)
   {
      const HDC hDC = reinterpret_cast<HDC>(AMsg.WParam);

      RECT RClip = static_cast<RECT>(Image1->BoundsRect);
      ExcludeClipRect(
         hDC, RClip.left, RClip.top, RClip.right, RClip.bottom
         );

      // have the form fill its background
      TForm::Dispatch(&AMsg);

      // restore the clipping region
      HRGN hRgn = CreateRectRgnIndirect(&RClip);
      InvalidateRgn(Handle, hRgn, FALSE);
      DeleteObject(hRgn);
   }
public:         // User declarations
   __fastcall TForm1(TComponent* Owner);

BEGIN_MESSAGE_MAP
   MESSAGE_HANDLER(WM_ERASEBKGND, TMessage, WMEraseBkgnd)
END_MESSAGE_MAP(TForm)  

Quote
};

Also, you mention that you're moving the tiles around.  I've found it
easiest to use a TPanel object for each tile.  This way, instead of
manually redrawing the image at a new location during the move
operation, you simply change the Top and Left properties of the
Panel.  See the following page for an example...

http://bcbcaq.freeservers.com/eliminate_flicker.html

Note, there's an error in the above page's code; change the
NewPanelWP() member function to the following...

void __fastcall TForm1::NewPanelWP(TMessage &Msg)
{
  if (Msg.Msg == WM_ERASEBKGND) Msg.Result = TRUE;
  else OldPanelWP(Msg);

Quote
}  

Good luck,
--
Damon C. (TeamB)
- BCBCAQ <http://bcbcaq.freeservers.com>
- Graphics API Black Book <http://graphicsBB.itgo.com>

Re:Offscreen bitmaps and DIB bitmaps


Quote
> 90% of the time, flickering is caused by unnecessary erasing.  See,
> each window receives a message called WM_PAINT, which instructs the
> window to paint its contents.  In reponse to this message, most
> windows call the BeginPaint() function, which sends the window the
> WM_ERASEBKGND message telling it to erase its background so that any
> subsequent drawing will be made to a "clean slate".  In response to
> the WM_ERASEBKGND message, most windows use the FillRect() function to
> fill their backgrounds with a solid color (e.g., clBtnFace).  After
> the background is "erased" the window continues processing the
> WM_PAINT message, in reponse to which it paints its contents.  You can
> see that this scheme is a bit inefficient, because there's no need to
> erase the portions that you're going to draw over anyway.  For
> example, if you have a TImage component that occupies the entire
> client area of a Form, there's no need for the Form to fill its
> background because the Image will be drawn over it.  In short, if
> you're seeing a flash between the color of the Image's Parent
> component (e.g., clBtnFace of Form1) and the contents of your Image,

I guess I'll try this... I have a white panel behind a paint box, and I can
see white flickering as various operations take place. I guess it is
repainting the white everytime as well.

Quote
> then you should trap the WM_ERASEBKGND message (at the level of your
> Image's Parent window), and then fill only the areas that you're not
> going to draw over.  For example, if your Image is parented to Form1,
> you'd do the following...

I'll give this a go.

Quote
> Also, you mention that you're moving the tiles around.  I've found it
> easiest to use a TPanel object for each tile.

Thats actually what I am doing. When I say 'tiles', I just have a number of
bitmaps of pretty similar size. The user can click on them then place a them
on the canvas. This just then paints that bitmap (using draw) onto the
canvas. Then when I want to move one, I click on it, the program scans
through (I created an image class that records location width height etc) an
array of my image class to determine what image was clicked. I then remove
that image from the array, place it on a image component on a panel to move
it. This means there is no problem with flickering on the moving tile.

I guess my main problem is that everytime it repaints the canvas, it runs
through the array painting each image.

Thanks heaps for you advice, I'll give it a go.

Adam.

Other Threads