How to Buffer a Screen

I'm wondering if there is a way to buffer a screen's contents so that everything will draw at once when I make the final HDC run. (To stop all screen flickering)

My plan was to have an invisible child window render the image and save it as a bitmap so the main window could then open the bitmap. I will need to learn how to save an image to bitmap, and I'm still unsure if the idea is even possible, or if it will run too slowly for it to be worthwhile.


So two questions really;

1. Is the method I mentioned worthwhile to follow up on?

2. Is there a better/faster way to buffer a screen?

Yes this is certainly possible, and in fact it is a very common technique.

What you want to do is create an "offscreen HDC" and create a bitmap to put in it. Then you can draw your full scene to the offscreen DC.. and when it's complete do a final BitBlt to the screen DC.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Setup:
HDC offscreen = CreateCompatibleDC( NULL ); // <- create the offscreen DC
HBITMAP bmp = CreateBitmap( width, height, 1, bits_per_pixel, NULL );
HBITMAP bmpold = (HBITMAP)SelectObject(offscreen, bmp); // <- put the bitmap in the dc


// .. draw your stuff to 'offscreen'  ...


// to clean up (when you're done with the offscreen buffer
SelectObject(offscreen, bmpold);
DeleteObject(bmp);
DeleteDC(offscreen);


Note that this will reduce most flickering, but if you are erasing the background that will still cause some flicker. To avoid that you can handle the WM_ERASEBACKGROUND message and prevent it from doing anything (do not call DefWindowProc).

That + the double buffer will give you perfectly smooth updates with no flicker.
Well, the main question has been answered.
As for the other two, the numbered ones:
1. Is the method I mentioned worthwhile to follow up on?

Surely.
Double buffering is probably one of the best things in graphics.
Its only major downside is the increased memory usage.
But the flickernessiness does pay back, especially for a layered (transparent) window.

2. Is there a better/faster way to buffer a screen?

Probably using hardware acceleration (So, avoiding to use GDI).
Sadly, using HW-acceleration makes things lot harder for the programmer (It may also become a slow thing if the target PC is old and does not support hardware acceleration), and forces you to learn DirectX, Direct2D or OpenGL.
Also, Direct2D is only supported since Windows Vista SP2.

In some cases, even GDI itself may be hardware accelerated.
But I do not know if it's something you can "turn on and off".
Wow, thanks guys, I wasn't expecting that idea to be so commonly used, I thought people were going to say it was way too slow.
Thanks, Disch, for the walkthrough, it's better than what I've been able to find so far. I'm in class right now so I'm looking foreward to trying it out.
I thought people were going to say it was way too slow.


Well CPU drawing is always "slow". But it might be fast enough for basic purposes.

It really depends on what you're using it for and how big of an area you'll be drawing.


I've written several retro-system emulators using this mechanism and was easily able to achieve 120+ FPS (on what would nowadays be considered low-end systems)... but the size of the screen I was drawing to was relatively small (only 512x480)


If you need really high performance, then this wouldn't be the way to go.
Note that for color bitmaps, MSDN recommends that you use CreateCompatibleBitmap rather than CreateBitmap for performance reasons (if the bitmap is known to be compatible, it doesn't have to be checked for compatibility when it's selected into the device context.).

There is a small catch. When you create the compatible bitmap, you've got to use the original DC rather than the memory DC or your bitmap will end up being monochome. So instead of passing NULL to CreateCompatibleDC, you need to pass the DC obtained using GetDC.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Setup:
HDC dcWnd = GetDC(hWnd); // <- get DC for client area of main window
HDC dcMem = CreateCompatibleDC(dcWnd); // <- create the offscreen DC
HBITMAP bmp = CreateCompatibleBitmap(dcWnd, width, height); // with window DC
HBITMAP bmpOld = (HBITMAP)SelectObject(dcMem, bmp); // <- put the bitmap in the dc

// .. draw your stuff offscreen...

// to clean up (when you're done with the offscreen buffer)
SelectObject(dcMem, bmpOld);
DeleteObject(bmp);
DeleteDC(dcMem);
ReleaseDC(dcWnd);


I also use the same approach in the WM_PAINT handler when I just want to composite a bitmap, blit it, and delete it. In that situation, the starting DC comes from BeginPaint rather than GetDC.

It is possible to configure the memory DC to use color, but the approach I've shown above is the more common, easier approach.

See remarks in MSDN entries for the CreateBitmap and CreateCompatibleBitmap functions for details.

Also, for further reading:

Guide to Win32 Memory DC
http://www.codeproject.com/Articles/224754/Guide-to-Win32-Memory-DC

Andy

CreateBitmap function
http://msdn.microsoft.com/en-us/library/windows/desktop/dd183485%28v=vs.85%29.aspx

CreateCompatibleDC function
http://msdn.microsoft.com/en-us/library/windows/desktop/dd183489%28v=vs.85%29.aspx
Last edited on
I don't know of anyone who doesn't run 32-bit color anymore. So creating a 32-bit bitmap is just as good as creating a compatible one -- only it saves you the step of having to get/release the window DC.
creating a 32-bit bitmap is just as good as creating a compatible one

For the record, this is counter to the advise given by Microsoft:

The CreateBitmap function can be used to create color bitmaps. However, for performance reasons applications should use CreateBitmap to create monochrome bitmaps and CreateCompatibleBitmap to create color bitmaps. Whenever a color bitmap returned from CreateBitmap is selected into a device context, the system checks that the bitmap matches the format of the device context it is being selected into. Because CreateCompatibleBitmap takes a device context, it returns a bitmap that has the same format as the specified device context. Thus, subsequent calls to SelectObject are faster with a color bitmap from CreateCompatibleBitmap than with a color bitmap returned from CreateBitmap.

From remarks in MSDN entry for CreateBitmap function
http://msdn.microsoft.com/en-us/library/windows/desktop/dd183485%28v=vs.85%29.aspx

Andy

PS All the tutorials I've seen to date on the use of GDI memory DCs use CreateCompatibleBitmap, so I just tried to Google "memory DC CreateBitmap", to see if counter advise was being given somewhere. But Google has gone and returned results for CreateCompatibleBitmap, so even it knows what's up!
Last edited on
Fair enough.
Topic archived. No new replies allowed.