1. Problem
I have two buffers. Primary buffer, which is displayed on the screen and a secondary buffer where everything is drawn and then passed to the primary.
The Graphics object is created from the secondary buffer, which is associated with a bitmap of size 800x600. Naturally when you resize the window, the size of the bitmap has to change in order to prevent clipping issues. The secondary HDC gets updated, and the bitmap is copied to the primary.
The issue is that there is something left in the Graphics object associated with secondary HDC that generates clipping. The drawing region still stays 800x600 despite already being updated to something larger (1000x1000).
My question is what should I update inside the Graphics object (Without having to explicitly recreate it from the existing HDC) in order to make it's drawing region fit the bitmap size.
2. What I tried
The first thing I tried was recreating the Graphics object from the updated HDC. This method works and the drawing region fits the size of the bitmap. It does not meet the design standard however. Graphics should be reusable.
I also tried updating the clipping region of the graphics object using the SetClip method. Although that did not seem to be the problem.
This is how I create the buffers.
HDC CoreWindowFrame::InitPaint(HWND hWnd)
{
windowHdc = GetDC(hWnd);
HDC secondaryBuffer = CreateCompatibleDC(windowHdc);
HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height);
SelectObject(secondaryBuffer, map);
return secondaryBuffer;
}
This function is called on resize
void CoreWindowFrame::UpdateBitmap(int width, int height)
{
HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height);
SelectObject(secondaryBuffer, map);
}
And this is message processing:
case WM_SIZE:
ConsoleWrite("WM_SIZE RECIEVED");
width = *((unsigned short*)&lParam);
height = ((unsigned short*)&lParam)[1];
UpdateBitmap(width, height);
break;
case WM_PAINT:
ConsoleWrite("WM_PAINT RECIEVE");
windowGraphics->Clear(Color(255, 255, 255));
Pen* testPen = new Pen(Color(255, 0, 0), 1.0F);
windowGraphics->DrawLine(testPen, 0, 0, calls*3, 100);
BitBlt(windowHdc, 0, 0, updatedWidth, updatedHeight, secondaryBuffer, 0, 0, MERGECOPY);
3. Expected Result
The graphics object should be reusable and it should not be tossed away and replaced with a new one each time something is refreshed. Therefore it has to be updated accordingly in case anything is resized or changed. I expect the region to fit the size of the currently updated bitmap in the secondary HDC. The bitmap, as seen in the code, is updated properly. It is the Graphics object that does not. It acts like it still remembers some of the value from the previous BitMap which results in this behavior.
HDC
obtained fromGetDC
orBeginPaint
cannot be reused, as noted in comments.You can however reuse memory bitmap (from
CreateCompatibleBitmap
) and reuse memory dc (obtained fromCreateCompatibleDC
), although there is usually no point in reusing memory dc.Moreover, cleanup is required to avoid resource leak. Call
ReleaseDC
when you are finished withGetDC
. See documentation for relevant release/delete functions.Do this for a simple paint routine:
Usually you don't need to do anything more. Just call
InvalidateRect
in response toWM_SIZE
to update paint.For double-buffering, or drawing on a canvas, you can create a memory bitmap and reuse it. If window size changes, then you have to call
DeleteObject
for the old bitmap, and create a new bitmap based on the new size. Or you can create a bitmap which matches the largest window size, then use this bitmap for all window sizes.See the example below for double-buffer paint (however reusing
hbitmap
is not necessary in this case)Summary:
GetDC
doesn't create anything. It only returns a reference to an existing handle which is used by the system. When you are finished with this handle, release it. There is no way to optimize this any further.Other GDI objects, like pen or bitmap, can be created for your program and they can be reused. It only takes a few nano-seconds to create/destroy a pen, so it's usually not worth the added complexity to store these objects and keeping track.
The main bottle-neck is when you draw on the screen, for example drawing a line. You have to talk to the graphics card and talk to the monitor which takes a while. You can use double-buffering to draw on a bitmap, then
BitBlt
on the screen.BitBlt
can be slow too, depending on the system.For animation, use double-buffering if you see flicker.
For better performance you can use newer technologies like
Direct2D
If animation is still too slow, consider using a second thread to run any math type calculations (the second thread should not reference any window handle, such as
HDC
fromGetDC
orBeginPaint
)