Click here to Skip to main content
Click here to Skip to main content

Double Buffered Graphics using DirectDraw

By , 23 Sep 2001
 

Sample Image - DXSurfaceMgr.gif

Introduction

This article explains how my class CDXSurfaceMgr can be used to facilitate Double Buffered drawing/graphics.

Double Buffered Drawing

Normally when drawing using the GDI you do all your painting straight into the HDC that's provided by either BeginPaint or a CDC (if you are using MFC). Iif you draw a line it immediately appears on the surface of the window you are drawing on. If you draw a lot of graphic items the screen is updated with each item and this can result in flicker or stuttering as the surface is filled with the drawing. Double Buffering can be used to create smooth updates, you build a "frame" of graphics in a non visible place first then copy the whole thing onto the main windows surface.

There a lots of ways to implement double buffering, one way is to create a compatible HDC in memory somewhere and draw into that then Blit (copy) that memory into the visible windows HDC. My approach uses a subset of DirectX called DirectDraw which can be used to draw 2d Graphics. CDXSurfaceMgr is written using the DirectDraw interfaces supplied in VC6 (ddraw.h/lib) and should work on 95/98/Me/2k/XP and NT 4.0 (sp3 - I think).

Using CDXSurfaceMgr

Create an instance of CDXSurfaceMgr

#include "DXSurfaceMgr.h"
...
...
CDXSurfaceManager_NBP dxMgr_;
Initialise the instance like so
int CMfcdxView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 if (CView::OnCreate(lpCreateStruct) == -1)
         return -1;
 // Initialise CDXSurfaceMgr
 if(!dxMgr_.Initialise(m_hWnd)) return -1;

 return 0;
}
This should be done just once, the parameter to the Initialise method is the Handle to the window you want to draw on. Then when handling the WM_PAINT message (the place you would normally do the drawing) call the BeginPaint Method
void CMfcdxView::OnDraw(CDC* pDC)
{
 CMfcdxDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 // TODO: add draw code for native data here

 HDC& hdc = dxMgr_.BeginPaint(GetSysColor(COLOR_3DFACE));
 if(NULL !=hdc)
 {
 ....

The parameter to BeginPaint is a COLORREF that BeginPaint will use to fill the background with. BeginPaint returns an HDC that that you can then use to draw things with, either via the standard GDI API;

 ::SetBkMode(hdc,TRANSPARENT);
 ::TextOut(hdc,10,10,"Please don't call me reg its not my name", 40);
Or if you are using MFC;
 CDC* cdc = CDC::FromHandle(hdc);
 cdc->SetBkMode(TRANSPARENT);
 cdc->(10,10, CString("All aboard Brendas iron sledge"));

Note all the usual GDI rules apply - if you select a brush remember to reselect the old one etc. When you have finished drawing, call the EndPaint method like so;

dxMgr_.EndPaint();

This will "flip" your drawing onto the main windows surface. and the drawing will instantaneously appear.

What CDXSurfaceMgr does

CDXSurfaceMgr creates a primary surface - one that is visible - and a secondary off screen surface that is the same size as the primary one but invisible. When you call BeginPaint an HDC attached to the secondary surface is returned; you do all your drawing into this; then EndPaint Blits(copies) the whole of the secondary surface onto the primary one and it appears on the window. If you have a capable graphics adapter on your machine then the Blit is very very fast, if you don't then the operation is obviously not as fast, but still very functional.

Demos

There are three demos included one that uses WFC and draws rectangles and ellipses moving up and down the screen, one that uses MFC, and draws a few lines and a bit of text, and one that does exactly the same again but is just a Win32 program.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Gertruud Lawrence
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionCDialog problemmemberPeterHo38618 Oct '11 - 16:23 
Hi,
 
When I implement the DirectDraw in CDialog::OnPaint(), the onpaint() is called endlessly. And then, the CPU loading is 50% high.
 
How to fix it in CDialog ?
 

Thanks!
GeneralAlpha supportmembernesculcas10 Feb '07 - 6:16 
Can someone explain to me why the alpha (window transparency) is not working with this code.
 
I realy don't understand anything about directdraw, but my guess is that the Blt(...) function is doing something wrong. I'm saying this because if this function is not called then the alpha in the window works, my guess is that the second surface does not have the alpha information that exists in the first surface, and when the Blt(...) function is called then the alpha information of the first surface is override by the erroneous alpha information of the second surface.
 
Anyone knowns any solution to do it?
 

 
Thanks
Nuno
GeneralWorking samplemembernesculcas8 Feb '07 - 13:40 
Some one have a working source code sample doing this but without the big memory leak?
 
(another question, this is realy necessary, the implementation of the double buffered graphics in the DirectX, the DirectX do not own a mechanism to handle the flicker? its realy necessary to implement some mechanism to avoid the flickering like we must do in gdi?)
 
Thanks
Nuno
GeneralRe: Working samplemembernesculcas10 Feb '07 - 4:49 
I find this interesting and i try to resolve the problem myself. The result is in DXSurfaceMgr.h if you find any problem or any improvement please inform.
 
Nuno
GeneralRe: Working samplemembernesculcas10 Feb '07 - 6:17 
Ok i already found one problem in this code, it doesn't support window transparency. Is there anyone that knows a solution to this?
GeneralUuups, did you ever try...membera coder7 Apr '05 - 20:23 
... to resize or - simply move - your example application's window???
 
I never saw an application (even if it was a buggy example) that eated up all the available memory in the system so enormously fast!!!
LOL.
 
Or was it intended to demonstrate even that...?
Sometimes I ask myself, whether some coders here even basicly test the pieces of code they do...
 
How can this happen?
A class, which causes such a behavior is probably the worst case possible. Even if it tries to create an OFFSCREEN CONTEXT mechanism to speed up things !!!
 
By the way, why to heck it is necessary to recreate the buffer at such simple tasks like moving a window...? Even resizing can be designed smart (i.e. only neccessary, if the window resizes to "bigger" ).
 
Whatever, I have written "normal" GDI based offscreen contexts, which akt ALLOT faster than your DirectX example here for some reasons...
 
Oh my!

GeneralRe: Uuups, did you ever try...memberx-plorer7 Apr '05 - 21:39 
I just discovered, that you obviously tried to correct the leak with one of your posts some times ago...
But the downloadeble examples are still the old buggy ones...
 
Whatever: even your correction is wrong, because there is another bug in the (corrected) code:
 

HDC& BeginPaint(COLORREF BackFillColor = 0)
{
current_hdc_ = NULL_HDC;
if(!IsValid()) return current_hdc_;
 
TBEGINPAINT::BeginPaint(window_);
 
XRect client; ::GetClientRect(window_, &client); ClientToScreen(&client);

/*
// only create new secondary surface if the window has changed size
if(current_rect_!=client)
{
secondarysurface_ = CreateSecondarySurface(client.Width(), client.Height());
if(NULL==secondarysurface_.p) return current_hdc_;
current_rect_ = client;
}
*/
 
// your fix:
 
// only create new secondary surface if the window has changed size
if(current_rect_!=client)
{
secondarysurface_.Release();
CreateSecondarySurface(client.Width(), client.Height(), &secondarysurface_);
if(NULL==secondarysurface_.p) return current_hdc_;
current_rect_ = client;
}

// fill the secondary surface
fillclr(secondarysurface_, BackFillColor);
 
// GetDC will _lock_ the surface for us
secondarysurface_->GetDC(¤t_hdc_);
return current_hdc_;
}

 

"secondarysurface" will be NULL under certain circumstances and so crash the application! I discovered this by very fast resizing the applications window to test your posted fix. There may be allot more such or similar things in your code.
 
Cheerz.

Generalhuge memory leaks !membercnkKlau10 Mar '05 - 0:26 
My problem is that even the test app mfcDx is leaking resources and very fast! if I move the window on desktop so that it is partially visible ( a part of it is out of screen max coordonates ) it leaks memory (in task manager the amount of memory allocated increases even by 100MB/s!!)
Any idea why and how to fix it???
 
Hi..
Generalpull-down menue updatessussSagi dror10 May '04 - 0:33 

The pull-down menu updates are very slow.It takes time to invalidete the menu from the client screen,and you can still see it for a half a second or so,after the menu has closed itself.
I'm using a 64 bit graphic card.
Can it be a problem of performance,or somthing else?
GeneralUse in Dialog bases appmemberRuination28 Aug '03 - 12:54 

I am trying to use this in a MFC dialog based app. I initialized the surface manager in my dialogs initdialog function with
 
if(!dxMgr.Initialise(m_hWnd))return -1;
 
but this call crashes Frown | :(
 
Inside the dxMgr.Initialize it crashes at the indicated spot.
 
if(32 == tst) // 32 bit
{
std::auto_ptr rgbconverter(new CRGB32BIT);
Crashes ->rgbconverter_ = rgbconverter;
 
Now, I am realativly new to VC++ so I'm thinking its because i am in a dialog based window and not a view.
 
I have been struggling with doulbe buffering for some time now, so any help is greatly appreciated.
 
A bit of background : My app needs to write multiple bitmaps tothe screen in different locatations, and must stretch them when writing.

 
Todd.
 

GeneralCDXSurfaceMgr stops working after screen saver has been activatedmemberJohanps17 Jun '03 - 3:22 
I have observed the following behaviour on Windows 2000 and Windows XP pro (don't know about other OS:es):
 
First start the little mfcdx sample program. "lock" the computer - i.e. bring up the screen saver manually or just by waiting. Then, after "unlocking" again, the mfcdx program doesn't draw anything inside the surface managed by CDXSurfaceMgr.
 
Anyone have any clues as to how to get around this; obviously I want "my" program using CDXSurfaceMgr to continue working after the screen saver has been active for a while...
 
Regards / Johan Sörensen.
GeneralRe: CDXSurfaceMgr stops working after screen saver has been activatedmembershorer30 Aug '09 - 0:46 
delete the function OnEraseBkgnd.
QuestionWhy here not Visual Basic?sussniekass12 Jun '03 - 2:06 
Why not be VB samples?
QuestionWhy here not Visual Basic?memberniekasss12 Jun '03 - 1:40 
Why not be VB samples?
AnswerRe: Why here not Visual Basic?memberTwink9 Jan '05 - 13:57 
Obviously because the author chose to use another language, which in most people opinion is a much better one. Taking code that uses a library in VC or VB and tranlating to the other isn't that hard a task I often do it as i only find examples in VB (and i use VC)
GeneralSetDIBitsToDevicememberAnonymous1 Apr '02 - 22:19 
Hi,
 
is it possible to use this function with your class??
I want to fill an window with data from an bitmap.
 
thank you
 
Steffen
GeneralThanks so muchmemberackka7 Jan '02 - 10:11 
I have been looking for this for a while now for a programming project. I was trying to do double buffering using a second HDC but it didn't seem to work. Thanks again Wink | ;)
 
tom moses
http://userpages.umbc.edu/~tmoses1
GeneralTheres a leak in my codemembergerty29 Sep '01 - 12:08 
hi all,
 
I have found a leak in my code, in the method CDXSurfaceMgr::CreateSecondarySurface I create a secondary surface which gets addrefed in CreateSurface dx call, then once again when I return it via the method to the awaiting smart pointer, the first addref isn't released and the surface gets overwritten and a leak results.
 
fix:
bool CreateSecondarySurface(const int Width, const int Height, IDirectDrawSurface** surface)
{
if(NULL!=primarysurface_.p)
{
CDDSURFACEDESC dds(DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH, DDSCAPS_OFFSCREENPLAIN, Width, Height);
HRESULT hr = directdraw_->CreateSurface(&dds,surface, NULL);
if(SUCCEEDED(hr))
{
return true;
}
}
return false;
}
and the call
 
// only create new secondary surface if the window has changed size
if(current_rect_!=client)
{
secondarysurface_.Release();
CreateSecondarySurface(client.Width(), client.Height(), &secondarysurface_);
if(NULL==secondarysurface_.p) return current_hdc_;
current_rect_ = client;
}
 
I shall be updating the code as soon as I can.
 
Sorry for any inconveniences
 

 
gl
GeneralGreat code... and a questionmemberMatt Ashland25 Sep '01 - 15:38 
Wow, thanks for the great code Smile | :)
 
Just a quick question:
 
Is there a way to make the secondary surface always 32-bpp regardless of the primary surface format?
 
If this is possible, what's needed so that BeginPaint() from the sample code would return an HDC and a DWORD* to the raw data? That'd be really slick because you could then use GDI and optimized byte-level manipulations side-by-side.
 
Thanks again for the code, and for any help.
 
-Matt
GeneralRe: Great code... and a questionmembergerty29 Sep '01 - 11:41 
Hi Matt,
 
Glad you liked the code.
 
I'm afraid you can't set the mode of the desktop when running in cooperative mode, thats why theres a lot of code to convert colours to the various desktop pixel modes available - although its not really used much (see the colour conversion classes in the header).
 

 
gl
GeneralRe: Thanks... and a benchmarkmemberMatt Ashland1 Oct '01 - 3:26 
Thanks Gerty.
 
I don't want to change the desktop. Just wondering if the secondary surface can be created as 32-bit, and then let DirectX dither it accordingly when it's rendered to the primary surface. Any chance?
 
As for benchmarks, this method performs very similarly to the DIB drawing API provided by Windows (StretchDIBits, etc.) UNTIL you do stretching. The DirectX engine will stretch (high quality... with filtering) with almost no overhead. The DIB methods fall apart when you do any sizing.
 
However, I still need to render into a DIB, flip to the secondary, then stretch to the primary unless I can figure out how to force the secondary buffer into 32-bit mode. Lots of my code is MMX that relies on 32-bit buffer alignment.
 
Anybody have any ideas?
 
Thanks again.
 
-Matt
Generalatlres.h missingmemberFriedhelm Schuetz25 Sep '01 - 4:33 
Hi,
downloaded and extracted the project. Tried to compile, but "atlres.h" isn't there.Confused | :confused:
GeneralRe: atlres.h missingmemberMatt Ashland25 Sep '01 - 17:09 
Check out this reply from a CodeGuru article. It has the link you'll need.
 
http://www.codeguru.com/mfc/comments/23204.shtml
GeneralRe: atlres.h missingmembergerty29 Sep '01 - 11:44 
Hi,
 
The code you tried to build uses wtl 3.1 - the other demos use mfc and straight win32, but they don't do very much -
GeneralInteresting...memberJ.G. Hattingh25 Sep '01 - 2:47 
Looks interesting, and I was wondering whether you could tell me what the pros and cons are of using this method...
 
TIA

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 24 Sep 2001
Article Copyright 2001 by Gertruud Lawrence
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid