Currently viewing: GipsySoft » Front Page» Articles

Flicker Free Windows and Drawing

What is it?

Whenever a window is resized using the Win32 Full Window Drag option the content of the window is redrawn as the window is stretched or shrunk, or if you have a timer event that updates some portion of your display using InvalidateRect(..) or similar then chances are that on every update your window will flicker. The content of the window will flicker if the drawing code draws in the same place more than once using two different colours. This can mean something as simple as using FillRect(...) to destroy the previous content of a screen area and using TextOut(...) to update text. Any window that does this to the window is going to flicker.

How to Cure Flicker in Everyday Life

The first step to removing flicker is to eliminate all sources of flicker that aren't your fault.

Make sure WM_ERASEBKGND is handled correctly, this probably means doing nothing but setting the return value to 1 because all of your actual drawing code takes place in your WM_PAINT handler. By having a simple handler for this message you can eliminate a large portion of the usual flicker associated with a window.

In dialog boxes that are re-sizeable make sure the style WS_CLIPCHILDREN is added, this will remove child controls from the update region generated for the dialog box.

If your window scrolls try using ScrollWindow(...) instead of simply updating your internal data and then invalidating the entire window.

Use an off screen bitmap to do your drawing on and then transfer that to your main window.

An Example

Take the following example code from a standard MFC CView derived OnDraw(...)

CRect rcClient;
GetClientRect( rcClient );
pDC->FillSolidRect( rcClient, GetSysColor( COLOR_3DFACE ) );
pDC->TextOut( rcClient.right - 100, rcClient.bottom - 100, _T("hello") );

All it does is fill the client area with a colour and then perform a text out. Using this simple code will flicker like crazy when the window is resized.

To eliminate the flicker from this example simply respond to the message WM_ERASEBKGND and return 1.

If that doesn't work, and it probably will not work for most non-trivial applications you will have to consider drawing to an off screen bitmap. You must consider the areas you will use an off screen bitmap very carefully, the size of the bitmap will have a big impact on the speed of your window update and remember that some people might have 1600x1200x32bit so a bitmap of your application maximised could be 8mb or larger. The following code added to a CView derived window class WM_PAINT handler will add off screen drawing to your application reasonably painlessly:

void CYourView::OnPaint()
{
  CPaintDC dc(this); // device context for painting

  CRect rcPaint = dc.m_ps.rcPaint;

  //
  //  Create a bitmap just big enough to hold the changes, then we
  //	create a device context to draw on, then we bring the two
  //	together so we have something to draw on.
  CBitmap bmp;
  bmp.CreateCompatibleBitmap( &dc, rcPaint.Width(), rcPaint.Height() );
  CDC newDC;
  newDC.CreateCompatibleDC( &dc );
  CBitmap *pOld = newDC.SelectObject( &bmp );

  //
  //  Set the origin of the device context so that our bitmap
  //	appears where the changes  need to be!
  newDC.SetViewportOrg( -rcPaint.left, -rcPaint.top );

  //
  //  Now we do the client stuff, this should work for most people
  OnPrepareDC( &newDC );
  OnDraw( &newDC );

  //
  //  Now that we have some drawn data on our bitmap we can simply copy
  //  it to the invalid portion of our window and deselect our bitmap
  VERIFY( dc.BitBlt( rcPaint.left, rcPaint.top
      , rcPaint.Width(), rcPaint.Height()
      , &newDC, rcPaint.left, rcPaint.top, SRCCOPY ) );
  SelectObject( newDC, pOld );
}