Double Buffering Helpers in Windows Vista
In these days, double buffering with windows was a bit tedious task. You will have to write your own double buffering classes to avoid flickering in your drawing. Those who don’t know, why we’re using double buffering, just Google and find it out yourself… If we write as pseudocode, the double buffering can be carried out in the following steps.
CPaintDC dcWnd(this); // Create offscreen DC CDC dcOff; dcOff.CreateCompatibleDC(&dc); CBitmap bmp; bmp.CreateCompatibleBitmap(); dcOff.SelectObject(&bmp)// Now all drawings will be recorded on the bitmap dcOff.YourDrawingFxn(); // you can do your own drawing dcWnd.BitBlt( …, &dcOff, ); // Directly blit to screen
Anyway Windows Vista UxTheme DLL provides built-in functionalities to support double buffering for your application. It again follows the trivial method of drawing but it’s more flexible and easy to use. You don’t have to burn your brain cells to create off-screen DC and rendering. The new UxTheme.DLL provides following functions for basic buffered painting. Actually I noticed these functions when I was writing the previous post on Getting theme information. Then I just saw the PPT and video of Raymond Chen from PDC 2008. I’m really impressed with his session.
BufferedPaintInit – Initialize buffered painting for the current thread.
BufferedPaintUnInit – Closes down buffered painting for the current thread
BeginBufferedPaint – begins buffered paint operation
EndBufferedPaint – Completes buffer pain operations
There are few things you should notice before using the buffered functions. Since the buffered operations are based on the bitmap blitting functions, the default background for the drawing will be black (RGB(0,0,0)). So you should draw the background before starting the actual drawing. Otherwise the background will be black by default. The next thing is that, it’s better to add the message handler for WM_ERASEBKGND and just return TRUE; instead of calling base class functions. This will avoid further flickering on resizing your window. The sample snippet below implemented buffered painting and a time has been added to call Invalidate() function in each 10ms to test the flickering. See the sample snippet below. Note that after initializing the buffered painting you should not use the original window DC for painting you should use the DC output from BeginBufferedPaint() function. The code will be running only in Windows Vista.
void CFlickerSampleDlg::OnPaint()
{
BP_PAINTPARAMS paintParams = {0};
paintParams.cbSize = sizeof(paintParams);
CRect rect;
GetClientRect( rect );
CPaintDC dc(this);
HDC hdcBuffer;
HPAINTBUFFER hBufferedPaint = BeginBufferedPaint(dc, rect,
BPBF_COMPATIBLEBITMAP,
&paintParams, &hdcBuffer);
CDC* pDc = CDC::FromHandle( hdcBuffer ); // Use output DC for further painting. Dont use windows DC
if (hBufferedPaint)
{
// Draw your background. otherwise it will be black
FillRect( pDc->m_hDC, rect, GetSysColorBrush(COLOR_3DFACE));
// Application specific painting code
for (int i =0;i<10;i++)
{
pDc->TextOut( 0,i*20,_T("Quick Brown Fox Jumps over lazy dog"));
}
EndBufferedPaint(hBufferedPaint, TRUE);
}
else
{
// Error occurred, default to unbuffered painting
}
}
void CFlickerSampleDlg::OnTimer(UINT_PTR nIDEvent)
{
Invalidate(); // Repaint the window for testing purpose
CDialog::OnTimer(nIDEvent);
}
void CFlickerSampleDlg::OnDestroy()
{
CDialog::OnDestroy();
BufferedPaintUnInit(); // Uninit
}
BOOL CFlickerSampleDlg::OnEraseBkgnd(CDC* pDC)
{
return 1;
}
about 1 year ago
Nice. I agree, Raymond’s talk was good. Thanks for the concise sample.