-
Double buffering problem
Hi there,
I am trying to get my program to double buffer, with no success. Basically, the program calls InvalidateRect whenever a timer (50 milliseconds) ticks, and here is what I have in WM_PAINT:
Code:
case WM_PAINT:
{
PAINTSTRUCT Ps;
HBRUSH EarthBrush;
HBRUSH SatBrush;
HDC hdc = BeginPaint(g_hwndWnd, &Ps);
HDC hdcBuffer = CreateCompatibleDC(hdc);
HBITMAP hbmBuffer = CreateCompatibleBitmap(hdc, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
HBITMAP hbmOldBuffer = (HBITMAP)SelectObject(hdcBuffer, hbmBuffer);
EarthBrush = CreateSolidBrush(RGB(0, 0, 250));
SelectObject(hdcBuffer, EarthBrush);
Ellipse(hdcBuffer, earthxcoord, earthycoord, earthxcoord+(2*6378.14*conversionfactor), earthycoord+(2*6378.14*conversionfactor));
DeleteObject(EarthBrush);
SatBrush = CreateSolidBrush(RGB(0, 250, 0));
SelectObject(hdcBuffer, SatBrush);
Ellipse(hdcBuffer, xcoord, ycoord, xcoord+10, ycoord+10);
DeleteObject(SatBrush);
BitBlt(hdc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), hdcBuffer, 0, 0, SRCCOPY);
SelectObject(hdcBuffer, hbmOldBuffer);
DeleteObject(hbmBuffer);
DeleteDC(hdcBuffer);
EndPaint(g_hwndWnd, &Ps);
break;
}
The variables don't make sense to you of course, but can anyone identify why this program still flickers annoyingly?
Thanks a lot! I will provide more info if necessary.
-
It doesn't look like this program would be very long. If you posted the whole thing I could try running it.
A couple of points. You could create your brushes in WM_CREATE and destroy them in WM_DESTROY. You could do the same with hdcBuffer and hdmBuffer, and leave hdmBuffer selected into hdcBuffer.
-
My suggestion would be to make the backbuffer DC (hdcBuffer) a static variable declared at the beginning of the window procedure, created in WM_CREATE and deleted in WM_DESTROY.
Do all your weird Ellipse(...) stuff during the WM_TIMER notification, before InvalidateRect.
Reduce WM_PAINT to a simple BeginPaint, BitBlt, EndPaint using the information provided in PAINTSTRUCT:-
Code:
BitBlt(Ps.hdc, Ps.rcPaint.left, Ps.rcPaint.top, Ps.rcPaint.right - Ps.rcPaint.left, Ps.rcPaint.bottom - Ps.rcPaint.top, hdcBuffer, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
That way, when you drag another window across it doesn't continuously redraw the backbuffer AND blit the whole thing when only a smaller area needs to be updated. ;)
-
To get proper double-buffering you'd have to use DirectX (or OpenGL), which use the buffers on the graphics card.
-
In addition to SMurf;
You are leaking 2 GDI brushes each paint (and possibly more GDI resources), because GDI objects (generally) can not be deleted while selected into a DC.
Catch the default when you select the first brush in, replace the default before you clean up the mem DC, as you are doing with the BITMAP.
Handle WM_ERASEBKGND (just return true to stop the background being redrawn as well each paint)
In WM_PAINT only re-draw the rect in the paintstruct, and only InvalidateRect() the smallest area you can.
Bypass the OS msg queue by using InvalidateRect() followed by UpdateWindow()
-
Hi, sorry for the delay, but thanks for your help. I have re-jigged it according to your advice, and there is no more flickering. I also thank you for making the general improvements to my code - hopefully I will get better as I learn more GDI. Thanks again :)