Click here to Skip to main content
Email Password   helpLost your password?

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.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralAlpha support
nesculcas
7:16 10 Feb '07  
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 sample
nesculcas
14:40 8 Feb '07  
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 sample
nesculcas
5:49 10 Feb '07  
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 sample
nesculcas
7:17 10 Feb '07  
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...
a coder
21:23 7 Apr '05  
... 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...
x-plorer
22:39 7 Apr '05  
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(&current_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 !
cnkKlau
1:26 10 Mar '05  
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 updates
Sagi dror
1:33 10 May '04  

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 app
Ruination
13:54 28 Aug '03  

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 crashesFrown

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 activated
Johanps
4:22 17 Jun '03  
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 activated
shorer
1:46 30 Aug '09  
delete the function OnEraseBkgnd.
GeneralWhy here not Visual Basic?
niekass
3:06 12 Jun '03  
Why not be VB samples?
GeneralWhy here not Visual Basic?
niekasss
2:40 12 Jun '03  
Why not be VB samples?
GeneralRe: Why here not Visual Basic?
Twink
14:57 9 Jan '05  
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)
GeneralSetDIBitsToDevice
Anonymous
23:19 1 Apr '02  
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 much
ackka
11:11 7 Jan '02  
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 code
gerty
13:08 29 Sep '01  
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 question
Matt Ashland
16:38 25 Sep '01  
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 question
gerty
12:41 29 Sep '01  
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 benchmark
Matt Ashland
4:26 1 Oct '01  
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 missing
Friedhelm Schuetz
5:33 25 Sep '01  
Hi,
downloaded and extracted the project. Tried to compile, but "atlres.h" isn't there.Confused
GeneralRe: atlres.h missing
Matt Ashland
18:09 25 Sep '01  
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 missing
gerty
12:44 29 Sep '01  
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...
J.G. Hattingh
3:47 25 Sep '01  
Looks interesting, and I was wondering whether you could tell me what the pros and cons are of using this method...

TIA
GeneralRe: Interesting...
gerty
12:57 29 Sep '01  
I'm not really aware of any cons - I guess you can still find machines with crapy graphics cards in, which may affect performance - my machine has an awfull ati mach 64 something or other Cry , and I still get reasonable performance, mind you i'm not not doing vast amounts of heavy drawing.

I found the actual implementation of the double buffering very easy - just a few lines of code really - certainly easier than the various other methods I have tried - creating memory hdcs and blitting via the software, which I can never get to work first time.

Performance wise I haven't benchmarked the code, it would be interesting to hear from anyone if this method goes any slower/faster than any other method they have.

The thing I liked about it was that its simple, its com based - which i like - and it works across just about every version of windows there is, Even _NT4_ (with the right service pack that is!)


Last Updated 24 Sep 2001 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010