Go to page:
1 2 Next
The solution to fixing flickering issues when drawing graphics is to use double buffering. Double buffering basically means that an off-screen buffer is created. Everything is rendered to this off-screen buffer and, when drawing is completed, this off-screen buffer is copied to the screen. The end result is that you do not see any flickering and you do not see the drawing being created part by part. A little unknown fact is that Windows Vista has built-in support for double buffering, so managing off-screen buffers, copying data, and so forth are all managed by Windows for you. This article will explain how to use that functionality.
Example Application
(continued)This article comes with an example application to illustrate the Windows Vista double buffering technique. For demonstration purposes, I created the following rendering function:
void RenderWindow(HDC hdc, RECT& rcClient)
{
HBRUSH hBrushWhite = (HBRUSH)GetStockObject(WHITE_BRUSH);
FillRect(hdc, &rcClient, hBrushWhite);
RECT rc = rcClient;
rc.left += 16;
static const int NUM = 10;
for (int i=0; i<NUM; ++i)
{
if (bUseBufferedPainting)
DrawText(hdc, L"Buffered Painting", -1, &rc, DT_TOP);
else
DrawText(hdc, L"Unbuffered Painting", -1, &rc, DT_TOP);
rc.top += (rcClient.bottom/NUM);
Sleep(10);
}
}
The sleep call is added to slow down the rendering a little bit. The effect of this is that the flickering with non double buffering will be even more noticeable.
The WM_PAINT handler of the normal, unbuffered rendering is as follows:
case WM_PAINT:
{
RECT rc;
GetClientRect(hWnd, &rc);
hdc = BeginPaint(hWnd, &ps);
RenderWindow(hdc, rc);
EndPaint(hWnd, &ps);
}
break;
Nothing special up to now.
Using Double Buffering
Double buffering support in Windows Vista is called Buffered Painting. The first thing you need to do is to include the uxtheme header file and link with the uxtheme library. This can be done as follows:
#include <uxtheme.h>
#pragma comment(lib, "uxtheme.lib")
Before you use any buffered painting functions, it is highly recommended to call BufferedPaintInit. BufferedPaintInit should be called once in every thread that wants to use buffered painting. It will set up some internal buffers that will be reused with every buffered painting operation. Every call to BufferedPaintInit should be matched with a call to BufferedPaintUnInit. When you do not call BufferedPaintInit, it might be that buffered painting will still work but you will have degraded performance because some internal data structures will be created and destroyed every time you use buffered painting. A good idea is to call BufferedPaintInit in your WM_CREATE handler and to call BufferedPaintUnInit in your WM_NCDESTROY handler, as can be seen below.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_CREATE:
BufferedPaintInit();
break;
case WM_NCDESTROY:
BufferedPaintUnInit();
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Now, to actually use the buffered painting, you call BeginBufferedPaint which will give you a new device context. This is the off-screen buffer to which you render everything. Once you call EndBufferedPaint, this off-screen buffer can be copied to the screen for you. Below is some code to demonstrate this.
case WM_PAINT:
{
RECT rc;
GetClientRect(hWnd, &rc);
hdc = BeginPaint(hWnd, &ps);
if (bUseBufferedPainting)
{
HDC hNewDC;
HPAINTBUFFER hBufferedPaint =
BeginBufferedPaint(hdc, &rc, BPBF_COMPATIBLEBITMAP,
NULL, &hNewDC);
if (hBufferedPaint)
{
RenderWindow(hNewDC, rc);
EndBufferedPaint(hBufferedPaint, TRUE);
}
else
{
RenderWindow(hdc, rc);
}
}
else
{
RenderWindow(hdc, rc);
}
EndPaint(hWnd, &ps);
}
break;
With the call to BeginBufferedPaint, you start your buffered painting. The third parameter to this function defines the type of off-screen buffer that will be created. The following options are available (from MSDN):
- BPBF_COMPATIBLEBITMAP: Compatible bitmap. The number of bits per pixel is based on the color format of the device associated with the HDC specified with BeginBufferedPaint—typically, this is the display device.
- BPBF_DIB: Bottom-up device-independent bitmap. The origin of the bitmap is the lower-left corner. Uses 32 bits per pixel.
- BPBF_TOPDOWNDIB:Top-down device-independent bitmap. The origin of the bitmap is the upper-left corner. Uses 32 bits per pixel.
- BPBF_TOPDOWNMONODIB: Top-down, monochrome, device-independent bitmap. Uses 1 bit per pixel.
Setting this to, for example, BPBF_DIB will ensure you always have a 32 bit off-screen buffer, so you can always draw in 32 bit regardless of your current monitor settings.
About the Author
Marc graduated from the Catholic University Leuven, Belgium, with a degree in "Burgerlijk ingenieur in de computer wetenschappen" (equivalent to Master of Science in Engineering in Computer Science) in 2003. In 2004 he got the cum laude degree of Master In Artificial Intelligence at the same university. In 2005 he started working for a big software consultancy company. His main expertise is C/C++ and specifically Microsoft VC++ and the MFC framework. Next to C/C++, he also likes C# and uses PHP for creating webpages. Besides his main interest for Windows development, he also has experience in developing C++ programs running 24x7 on Linux platforms and in developing critical 2G,3G software running on Solaris for big telecom operators.
Go to page:
1 2 Next
Downloads
BufferedPaintExample_demo.zip - Demo application
BufferedPaintExample_src.zip - Demo application source code