I am studying Double buffering in windows winapi.
When I draw text on HDC direct using DrawText function, it works well like under code.
case WM_PAINT:
hDC = BeginPaint(hwnd,&ps);
DrawText(hDC,"test",4,&rt,DT_CENTER | DT_WORDBREAK);
DeleteDC(hMemDC);
ReleaseDC(hwnd,hDC);
EndPaint(hwnd,&ps);
break;
But, I would like to want to use double buffering, so I make memory dc and bitblt function.
Under code is that,It don't works.I can show white empty screen.
case WM_PAINT:
hDC = BeginPaint(hwnd,&ps);
hMemDC = CreateCompatibleDC(hDC);
//GetClientRect(hwnd, &crt);
//hBitmap = CreateCompatibleBitmap(hDC, crt.right, crt.bottom);
//OldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
DrawText(hMemDC,"test",4,&rt,DT_CENTER | DT_WORDBREAK);
BitBlt(hDC,0,0,800,800,hMemDC,0,0,SRCCOPY);
DeleteDC(hMemDC);
ReleaseDC(hwnd,hDC);
EndPaint(hwnd,&ps);
break;
Is memory dc different original dc ?
If I use CreateCompatibleBitmap function, it work well.
What is concept am i missing ?
Is there website well-organized website?
A typical non-double-buffered drawing routine looks like this:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rcWnd;
GetClientRect(hWnd, &rcWnd);
{
DrawText(hdc, _T("Hello, world!"), -1,
&rcWnd, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
EndPaint(hWnd, &ps);
}
break;
A typical double-buffered drawing routine looks like this:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rcWnd;
GetClientRect(hWnd, &rcWnd);
{
const int width = rcWnd.right - rcWnd.left
const int height = rcWnd.bottom - rcWnd.top;
// create a new DC based on the target HDC
HDC hDCMem = CreateCompatibleDC(hdc);
// create a bitmap that is compatible with the target DC
HBITMAP hMemBmp = CreateCompatibleBitmap(hdc, width, height);
// select the new bitmap in to the DC, saving the old bitmap
HBITMAP hOldBmp = (HBITMAP)SelectObject(hDCMem, hMemBmp);
// do your drawing
HBRUSH hBr = CreateSolidBrush(RGB(255, 255, 255));
FillRect(hDCMem, &rcWnd, hBr);
DeleteObject(hBr);
DrawText(hDCMem, _T("Hello, world!"), -1,
&rcWnd, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
// copy all the bits from our new DC over to the target DC
BitBlt(hdc, rcWnd.left, rcWnd.top, width, height, hDCMem, 0, 0, SRCCOPY);
// select the original bitmap the DC came with
SelectObject(hDCMem, hOldBmp);
// delete our bitmap
DeleteObject(hMemBmp);
// delete the DC
DeleteDC(hDCMem);
}
EndPaint(hWnd, &ps);
}
break;
case WM_ERASEBKGND:
// since we are drawing the entire area because we are double-buffering, there is
// no need to erase the background. This will speed up your drawing.
return TRUE;
HDC objects are all the same -- there is no special things that happen when you create another one in memory. I mean, they are all in memory, really. You are just creating another canvas to do your drawing on, then copying that canvas over in one shot.
Related
I was told recently that in WM_PAINT: message I should only use BeginPaint() and EndPaint() .
But what when I need multiple windows with bitmaps or drawings in my main window?
How should I manage this in proper way?
So far I was using this routines, that worked for me and was logic for me.
Is it wrong? What is the proper way?
Thank You in advance.
case WM_PAINT:
{
// main window - that contains other sub windows
PAINTSTRUCT ps;
HDC hdc = BeginPaint(_hwnd, &ps);
EndPaint(_hwnd, &ps);
// a window that shows a 256x256 texture:
PAINTSTRUCT ps_texture_256;
HDC hdc_texture_256 = BeginPaint(GetDlgItem(_hwnd, IDS_DC_TEXTURE_256), &ps_texture_256);
HDC hdc_tmp_256 = CreateCompatibleDC(hdc_texture_256);
SelectObject(hdc_tmp_256, convert_hbm_texture_256);
BitBlt(hdc_texture_256, 0, 0, 256, 256, hdc_tmp_256, 0, 0, SRCCOPY);
DeleteObject(convert_hbm_texture_256);
DeleteDC(hdc_tmp_256);
EndPaint(GetDlgItem(_hwnd, IDS_DC_TEXTURE_256), &ps_texture_256);
// a smaller window that shows 128x128 texture:
PAINTSTRUCT ps_texture_128;
HDC hdc_texture_128 = BeginPaint(GetDlgItem(_hwnd, IDS_DC_TEXTURE_128), &ps_texture_128);
HDC hdc_tmp_128 = CreateCompatibleDC(hdc_texture_128);
SelectObject(hdc_tmp_128, convert_hbm_texture_128);
BitBlt(hdc_texture_128, 0, 0, 128, 128, hdc_tmp_128, 0, 0, SRCCOPY);
DeleteObject(convert_hbm_texture_128);
DeleteDC(hdc_tmp_128);
EndPaint(GetDlgItem(_hwnd, IDS_DC_TEXTURE_128), &ps_texture_128);
return (INT_PTR)TRUE;
}
case WM_PAINT:
{
PAINTSTRUCT pt;
HDC hdc;
hdc=BeginPaint(hWnd,&pt);
SetTextColor(hdc,RGB(255,0,0));
SetBkColor(hdc,RGB(0,255,0));
SetBkMode(hdc,TRANSPARENT);
//为什么矩形绘制成功,但是字体没有绘制呢?
TextOut(hdc,0,0,TEXT("WM_PAINT"),strlen("WM_PAINT"));
EndPaint(hWnd,&pt);
}
We use above code to write text in a Window. What should i do if want output "int i" to window, i increace every second? i means ouput 0, 1, 2, 3, 4, 5.... in to window.
Store your i variable somewhere that your WM_PAINT handler can access it. Then, whenever you change the value of i, such as in a timer, then you can call InvalidateRect() to trigger a repaint. Your WM_PAINT handler should draw the current value of i whenever the window needs to be painted.
int i = 0;
...
case WM_TIMER: {
++i;
InvalidateRect(hWnd, NULL, TRUE);
break;
}
case WM_PAINT: {
PAINTSTRUCT pt;
HDC hdc = BeginPaint(hWnd, &pt);
SetTextColor(hdc, RGB(255,0,0));
SetBkColor(hdc, RGB(0,255,0));
SetBkMode(hdc, TRANSPARENT);
TCHAR str[16];
int len = wsprintf(str, TEXT("%d"), i);
TextOut(hdc, 0, 0, str, len);
EndPaint(hWnd, &pt);
break;
}
By default a rich edit control has a "3d" border. I draw a thin border around a rich edit control this way:
if (message == WM_NCPAINT)
{
RECT rc;
HDC hdc;
HPEN pen;
HBRUSH brush;
HGDIOBJ oldP, oldB;
POINT tl, br;
::GetWindowRect(hWnd, &rc);
hdc = ::GetDC(hWnd);
tl.x = rc.left;
tl.y = rc.top;
br.x = rc.right;
br.y = rc.bottom;
::ScreenToClient(hWnd, &tl);
::ScreenToClient(hWnd, &br);
pen = ::CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
brush = (HBRUSH) ::GetStockObject(HOLLOW_BRUSH);
oldP = ::SelectObject(hdc, pen);
oldB = ::SelectObject(hdc, brush);
::Rectangle(hdc, tl.x, tl.y, br.x, br.y);
::SelectObject(hdc, oldP);
::SelectObject(hdc, oldB);
::DeleteObject(pen);
::ReleaseDC(hWnd, hdc);
return 0;
}
The border looks fine but the area under the old border is not redrawn. It looks like I have to redraw to whole content of the rich edit control. After that a text shouldn't be a litte bit cut from the bottom. Here you can see what I mean (the second rich edit control has custom border). How to achieve it ?
GetDC returns DC for client area. In this case you need GetWindowDC for the whole richedit window.
Either way, overriding border color in edit and rich-edit can be difficult, because you also have to handle scrollbar painting in WM_NCPAINT
Assuming you don't need vertical and/or horizontal scrollbar, use GetWindowDC as follows:
LRESULT CALLBACK RichEditProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp,
UINT_PTR, DWORD_PTR)
{
static int border_thickness = 1;
switch(msg)
{
case WM_NCPAINT:
{
HDC hdc = GetWindowDC(hwnd);
RECT rc;
GetClientRect(hwnd, &rc);
rc.right += 2 * border_thickness + 1;
rc.bottom += 2 * border_thickness + 1;
HBRUSH hbrush = (HBRUSH)GetStockObject(NULL_BRUSH);
HPEN hpen = CreatePen(PS_SOLID, 2 * border_thickness, RGB(255, 0, 0));
HBRUSH oldbrush = (HBRUSH)SelectObject(hdc, hbrush);
HPEN oldpen = (HPEN)SelectObject(hdc, hpen);
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
SelectObject(hdc, oldpen);
SelectObject(hdc, oldbrush);
DeleteObject(hpen);
DeleteObject(hbrush);
ReleaseDC(hwnd, hdc);
return 0;
}
case WM_NCCALCSIZE:
if(lp)
{
NCCALCSIZE_PARAMS* sz = (NCCALCSIZE_PARAMS*)lp;
InflateRect(&sz->rgrc[0], -border_thickness, -border_thickness);
return 0;
}
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hwnd, RichEditProc, 0);
break;
}
return DefSubclassProc(hwnd, msg, wp, lp);
}
By default, WM_NCCALCSIZE is not called in subclass procedure, you have to call SetWindowPos with SWP_FRAMECHANGED
HWND hrichedit = CreateWindowEx(...);
SetWindowSubclass(hrichedit, RichEditProc, 0, 0);
SetWindowPos(hrichedit, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
If you do need horizontal/vertical scrollbar, you might consider using a borderless richedit, then paint around the rich control in parent window's WM_PAINT procedure.
I'm trying to load a bitmap to a window I created. The bitmap should be the background of the window (I want to add labels on it an a progress bar later on).
This is my code:
HINSTANCE hInst;
LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX WndCls;
static WCHAR szAppName[] = L"BitmapIntro";
MSG Msg;
hInst = hInstance;
WndCls.cbSize = sizeof(WndCls);
WndCls.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
WndCls.lpfnWndProc = WindProcedure;
WndCls.cbClsExtra = 0;
WndCls.cbWndExtra = 0;
WndCls.hInstance = hInst;
WndCls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndCls.hCursor = LoadCursor(NULL, IDC_ARROW);
WndCls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndCls.lpszMenuName = NULL;
WndCls.lpszClassName = szAppName;
WndCls.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
RegisterClassEx(&WndCls);
CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
szAppName,
L"Bitmaps Fundamentals",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
while (GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return static_cast<int>(Msg.wParam);
}
LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
HDC hDC, MemDCExercising;
PAINTSTRUCT Ps;
HBITMAP bmpExercising;
switch (Msg)
{
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &Ps);
// Load the bitmap from the resource
bmpExercising = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP3));
// Create a memory device compatible with the above DC variable
MemDCExercising = CreateCompatibleDC(hDC);
// Select the new bitmap
SelectObject(MemDCExercising, bmpExercising);
// Copy the bits from the memory DC into the current dc
BitBlt(hDC, 10, 10, 450, 400, MemDCExercising, 0, 0, SRCCOPY);
// Restore the old bitmap
DeleteDC(MemDCExercising);
DeleteObject(bmpExercising);
EndPaint(hWnd, &Ps);
break;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}
The problem is, the PNG size is small compared to the window, so when the PNG opens, it only occupies the left high corner. How can I make it stretch to my window size or at least draw it over and over until it fills the window?
How can I make it stretch to my window size
Use StretchBlt() instead of BitBlt().
case WM_PAINT:
{
// Get the window dimensions
RECT r;
GetClientRect(hWnd, &r);
// Load the bitmap from the resource
HBITMAP bmpExercising = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP3));
// Get the bitmap dimensions
BITMAP bmp;
GetObject(bmpExercising, sizeof(BITMAP), &bmp);
PAINTSTRUCT Ps;
HDC hDC = BeginPaint(hWnd, &Ps);
// Create a memory device compatible with the above DC variable
HDC MemDCExercising = CreateCompatibleDC(hDC);
// Select the new bitmap
HBITMAP hOldBmp = SelectObject(MemDCExercising, bmpExercising);
// Copy the bits from the memory DC into the current dc
StretchBlt(hDC, 0, 0, r.right - r.left, r.bottom - r.top, MemDCExercising, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY);
// Restore the old bitmap
SelectObject(MemDCExercising, hOldBmp);
// Destroy the memory device
DeleteDC(MemDCExercising);
// Destroy the bitmap
DeleteObject(bmpExercising);
EndPaint(hWnd, &Ps);
break;
}
or at least draw it over and over until it fills the window?
There are two different ways to handle that.
at startup, load the bitmap and create an HBRUSH around it using CreatePatternBrush(), and then assign that to the WNDCLASS::hbrBackground field when you register your window class. Let the OS draw the window background using the bitmap for you.
HBITMAP bmpExercising = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP3));
WndCls.hbrBackground = CreatePatternBrush(bmpExercising);
if you want to paint the bitmap manually, have your paint handler call BitBlt() in a couple of loops. You know the dimensions of the bitmap (which you can retrieve in code using GetObject() and the BITMAP structure), and you know the dimensions of the window (which you can retrieve in code using GetWindowRect() or GetClientRect()). So simply draw the same bitmap more than one time at different offsets as needed. Start by drawing it once in the top-left corner, then move right bitmap-width pixels and draw it again, repeating until you move past window-width pixels. Then move left back to 0 and move down bitmap-height pixels and repeat the whole width-line again, repeating until you move past window-height pixels.
case WM_PAINT:
{
// Get the window dimensions
RECT r;
GetClientRect(hWnd, &r);
// Load the bitmap from the resource
HBITMAP bmpExercising = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP3));
// Get the bitmap dimensions
BITMAP bmp;
GetObject(bmpExercising, sizeof(BITMAP), &bmp);
PAINTSTRUCT Ps;
HDC hDC = BeginPaint(hWnd, &Ps);
// Create a memory device compatible with the above DC variable
HDC MemDCExercising = CreateCompatibleDC(hDC);
// Select the new bitmap
HBITMAP hOldBmp = SelectObject(MemDCExercising, bmpExercising);
int width = r.right - r.left;
int height = r.bottom - r.top;
// Copy the bits from the memory DC into the current dc
for(int y = 0; y < height; y += bmp.bmHeight)
{
for(int x = 0; x < width; x += bmp.bmWidth)
{
BitBlt(hDC, x, y, bmp.bmWidth, bmp.bmHeight, MemDCExercising, 0, 0, SRCCOPY);
}
}
// Restore the old bitmap
SelectObject(MemDCExercising, hOldBmp);
// Destroy the memory device
DeleteDC(MemDCExercising);
// Destroy the bitmap
DeleteObject(bmpExercising);
EndPaint(hWnd, &Ps);
break;
}
Now, with that said, here are some additional notes:
you should not be loading the bitmap inside of your paint handler. Load it one time before creating the window, and then reuse the same HBITMAP for each paint operation until the window is destroyed, then free the bitmap.
LoadBitmap() is deprecated, you should be using LoadImage() instead, eg:
HBITMAP bmpExercising = (HBITMAP) LoadImage(hInst, MAKEINTRESOURCE(IDB_BITMAP3), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
you said "The bitmap should be the background of the window", so you should be drawing the bitmap in response to the WM_ERASEBKGND message instead of the WM_PAINT message.
case WM_ERASEBKGND:
{
HDC hDC = (HDC) wParam;
// draw the bitmap on hDC as needed...
return 1;
}
I am trying to draw a bitmap with an alpha channel (via AlphaBlend) as the face of an owner-drawn button. The problem is that I'm not sure how to draw the background of the button. The button's bitmap is circular, and the button is on top of a static control that draws a rectangular bitmap (via SS_BITMAP). It looks fine the first time it is drawn, but subsequent drawings end up alphablending the bitmap with its remains in the DC so the edges (where the alpha pixels lie) get ugly. I tried copying the dialog background to the DC I get in WM_DRAWITEM, but that only gets me the dialog background; it does not get me the part of the static control that is under the button. How do I do this?
My bitmaps are similar to this, except the dialog has a custom background (bitmap drawn during WM_ERASEBKGND) and the rectangle extends further out horizontally.
I found a better solution. It's basically the same structure as my previous solution, only instead of copying what's already on the device context to a bitmap I send all the relevant controls WM_ERASEBKGND and WM_PRINTCLIENT messages. I based it off of the code in this KB article.
Well, I found one method that works for my needs; I don't know if it's the ideal solution, but if nobody can come up with anything better then I'll accept my own answer in a few days.
So here's the trick I'm using (transposed from use of ATL's CImage to raw Win32 APIs, so there could be some mistakes):
LRESULT CALLBACK MyButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_ERASEBKGND:
if(!m_BackgroundBitmap)
{
// first time the button's background is being erased
// all the controls below it in the z-order have already
// been drawn at this point, so take a snapshot of the
// contents of the device context to use in the future
RECT rc;
GetWindowRect(hWnd, &rc);
int cx = rc.right - rc.left;
int cy = rc.bottom - rc.top;
HDC hDC = (HDC)wParam;
HDC hDCMem = CreateCompatibleDC(hDC);
m_BackgroundBitmap = CreateCompatibleBitmap(hDC, cx, cy);
HBITMAP hBmpOld = (HBITMAP)SelectObject(hDCMem, m_BackgroundBitmap);
BitBlt(hDCMem, 0, 0, cx, cy, hDC, 0, 0, SRCCOPY);
SelectObject(hDCMem, hBmpOld);
hBmpOld = NULL;
DeleteDC(hDCMem);
hDCMem = NULL;
}
break;
}
return CallWindowProc(m_PrevProc, hWnd, uMsg, wParam, lParam);
}
INT_PTR CALLBACK MyDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
// load resources, subclass buttons, etc.
return TRUE;
case WM_DRAWITEM:
// figure out if display needs to be updated, which face to draw, etc.
HDC hDC = lpDrawItemStruct->hDC;
HDC hDCMem = CreateCompatibleDC(hDC);
// first copy the background from the snapshot taken earlier
HBITMAP hBmpOld = (HBITMAP)SelectObject(hDCMem, m_BackgroundBitmap);
BitBlt(hDC, x, y, w, h, hDCMem, 0, 0, SRCCOPY);
// then alphablend the button face on top of that
SelectObject(hDCMem, m_AlphaButtonBitmap);
AlphaBlend(hDC, x, y, w, h, hDCMem, 0, 0, w, h, bf);
SelectObject(hDCMem, hBmpOld);
hBmpOld = NULL;
DeleteDC(hDCMem);
hDCMem = NULL;
return TRUE;
}
return FALSE;
}