MSDN and numerous post have suggested that BeginPaint/EndPaint should used in WM_PAINT. I've also seen numerous places suggesting that if double buffering is employed in painting, it makes more sense to initialize the DC and mem allocation in WM_CREATE and reuse those handles in WM_PAINT.
For example, using BeginPaint, I commonly see:
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
MemDC = CreateCompatibleDC(hdc);
bmp = CreateCompatibleBitmap(hdc, width, height);
oldbmp = SelectObject(MemDC,bmp);
g = new Graphics(MemDC);
//do paint on bmp
//blt bmp back to hdc
EndPaint(hWnd, &ps);
DeleteObject(bmp);
g->ReleaseHDC(MemDC);
DeleteDC(MemDC);
delete g;
To save the initialization and tearing down, is it possible to do this :
case WM_CREATE:
hdc = GetDC(hWnd);
//create memDC and graphics object references ...
case WM_DESTROY
//delete memDC and graphics object references...
case WM_PAINT
BeginPaint(hWnd, &ps);
//use previously create mem and graphics object to paint
EndPaint(hWnd, &ps);
So we only use EndPaint to clear the update region but delegate the drawing to prev created objects.
No, that's a very poor practice. This goes wrong first by the device context having the wrong clipping region, you cannot properly paint the window when the user resizes the window.
Second problem is that the update region clipping won't be in effect, PAINTSTRUCT.rcPaint. Preventing any possible paint optimization that Windows may automatically perform when the update region is just a part of the window.
Third problem is that you are holding on an operating system resource needlessly. All GDI objects are allocated in a single heap that's shared by all processes that run on the same desktop.
Creating a HDC is very cheap, create it when you need it. Not to mention the PAINTSTRUCT.hdc you'll get served on a platter, ready to use.
Related
I customised the non-client area of window frame following the example in https://learn.microsoft.com/en-gb/windows/win32/dwm/customframe?redirectedfrom=MSDN. On this non-client area, I want to add a container dialog and a few custom buttons. I cannot get the color I want to be painted on my container dialog. Instead, it is always painted transparent in that area. The issue is demonstrated in the 2 figures below (the background there is for demonstrating the transparency).
The effect I want is:
But what has been painted is:
As you can see, the container dialog is wanted to be painted as purple but is painted as transparent and so is the customer picture button (gray cross inside circle).
The code I used to handle the WM_PAINT for the container dialog is:
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
Gdiplus::Graphics g(hDC);
Gdiplus::Color clr(255, 0, 0, 0);
clr.SetFromCOLORREF(RGB( 85, 107, 47));
Gdiplus::SolidBrush b(clr);
g.FillRectangle(&b, rc.left, rc.top, RECT_WIDTH(rc), RECT_HEIGHT(rc));
EndPaint(hwnd, &ps);
I googled but could not find topic of similar issue (may be I didn't use the right keywords). I am not familiar in using winapi to draw non-client area. Any help pointing out what I have missed or direction to solution is much appreciated.
A VS project that demonstrate the problem is linked below:
CustomCaption.zip
I'm programming for Win7+ and using Direct2D for drawing. UpdateLayeredWindow requires a HDC, which means I'd better create a WICRenderTarget(not GPU accelerated). Is there any way to make a semi-transparent window without using UpdateLayeredWindow?
There are only two API functions for rendering layered windows: UpdateLayeredWindow() and SetLayeredWindowAttributes().
UpdateLayeredWindow() requires you to render your window contents to your own in-memory bitmap HDC which the OS then displays when needed.
SetLayeredWindowAttributes() relies on the traditional WM_PAINT model of requiring you to render to an OS-provided HDC instead, which is implemented as an in-memory bitmap HDC that the OS applies effects to after your rendering is finished.
Given I implement double buffering in GDI:
static HDC hdc;
static HDC backDC;
static HBITMAP backBuffer;
static HGDIOBJ oldBitmap;
static RECT client;
case WM_CREATE:
hdc=GetDC(hWnd);
GetClientRect(hWnd, &client);
backDC=CreateCompatibleDC(hdc);
backBuffer=CreateCompatibleBitmap(hdc,client.right,client.bottom);
oldBitmap=SelectObject(backDC,backBuffer);
ReleaseDC(hWnd,hdc);
case WM_PAINT:
Rectangle(backDC, 0, 0,client.right,client.bottom); // displays rectangle the
size of client to draw on it
hdc = BeginPaint(hWnd, &ps);
BitBlt(hdc,0,0,client.right,client.bottom,backDC,0,0,SRCCOPY);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
How do I handle the resize ? One thing I have tried to do is
case WM_SIZE:
client.right=LOWORD(lParam);
client.bottom=HIWORD(lParam);
SendMessage(hWnd,WM_CREATE,NULL,NULL);
What it does is, once I get resize message with new client coordinates it sends message to WM_CREATE and it actually works.... HOWEVER ! It creates massive leak, because I basically create a new bitmap every time without destroying it. Can someone tell me if there is a better way to do it ? Thx
Keeping the backbuffer around is an optimization that's not always needed. You can create it in the WM_PAINT handler (to the size of GetClientRect), paint to it, blit from it to the actual window DC, and clean up. No leaks. No distribution of functionality among all the message handlers. No global variables. Nice and clean.
If you do want to keep one around, I make a class. Constructor takes the size. Destructor cleans up everything. On WM_SIZE, construct a new one as a local stack variable, swap with the old one, and let destructor for the temp stack one clean up.
I would recommend making the back buffer a fixed size (typically, the size of the screen), and then using StretchBlt (or StretchDIBits if you're using DIBs) to render it in the proper size on the display surface.
That way, you never have to worry about reallocating the back buffer.
I have subclassed a graphics control that takes a device context handle, HDC, as an input and uses that for drawing. My new control is just the original control centered on top of a larger image. I would like to be able to call the original control's Draw() method for code re-use, but I'm unsure how to proceed.
Here's the idea:
void CCheckBox::DrawCtrl( HDC hdc, HDC hdcTmp, LPSIZE pCtlSize, BYTE alpha ) {
// original method draws a checkbox
}
void CBorderedCheckBox::DrawCtrl( HDC hdc, HDC hdcTmp, LPSIZE pCtlSize, BYTE alpha ) {
// Draw my image here
// Create new hdc2 and hdcTemp2 which are just some portion of hdc and hdcTemp
// For example, hdc2 may just be a rectangle inside of hdc that is 20 pixels
// indented on all sides.
// Call CCheckBox::DrawCtrl() with hdc2 and hdcTemp2
}
I think you may be confused of what a device context is. A device context is a place in memory that you can draw to, be it the screen buffer or a bitmap or something else. Since I imagine you only want to draw on the screen, you only need one DC. To accomplish what you want, I would recommend passing a rectangle to the function that tells it where to draw. Optionally, and with poorer performance, you could create a new Bitmap for the smaller area, and give the function the Bitmap's DC to draw on. Now that I think about it, that might have been what you meant in the first place :P Good luck!
While not foolproof, you can fake a DC as a subsection of a DC by using a combination of SetViewportOrgEx and SelectObject with a region clipped to the sub area in question.
The problem with this approach is if drawing code already uses these APIs it needs to rewritten to be aware that it needs to combine its masking and offsetting with the existing offsets and clipping regions.
I'm writing a simple automated test application for Win32. It runs as a separate process and accesses the target application via the Windows API. I can read window hierarchies, find labels and textboxes, and click buttons by sending/posting messages etc. All fine.
Unfortunately many controls in the target application are composed of nothing more than an owner-drawn control/window. (For example, we use the BCG menus and controlbars). Finding the correct part of the control to send a 'click' to is problematic.
Is there any way, given a HWND, to extract GDI drawing commands? I'd like to know each piece of text drawn to that control, and its coordinates.
Failing that, is there any way to capture a single control/window (again by HWND) into a bitmap? Worse-case scenario, I could OCR it.
You can use TextGRAB SDK to do this. Unfortunately it is shareware and costs $30.
To capture a window as a bitmap:
RECT rc;
GetClientRect(hWnd, &rc);
int cx = rc.right-rc.left;
int cy = rc.bottom-rc.top;
HDC winDC = ::GetDC(hWnd);
HDC tempDC = ::CreateCompatibleDC(winDC);
HBITMAP newBMP = ::CreateCompatibleBitmap(winDC, cx, cy);
HBITMAP oldBmp = (HBITMAP)::SelectObject(tempDC, newBMP);
BitBlt(tempDC,0,0,cx,cy, winDC,0,0,SRCCOPY|CAPTUREBLT);
// now you have the window content in the newBMP bitmap, do with it as you please here
::SelectObject(tempDC, oldBmp);
::DeleteObject(newBMP);
::DeleteDC(tempDC);
::ReleaseDC(hWnd, winDC);