I'm studying WinAPI double buffering, and when I run the code below, the screen flickers too much. I think I did it right, but where is the problem?
Draw memdc1 and memdc2 to memdc, and memdc to hdc.
switch (iMsg)
{
case WM_CREATE:
hdc = GetDC(hwnd);
GetClientRect(hwnd, &clientRt);
PlaceCenterScreen(hwnd);
SetTimer(hwnd, 1, 10, NULL);
hMixBit = CreateCompatibleBitmap(hdc, 819, 614);
hHouseBit = (HBITMAP)LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_HOUSE));
hTigerBit = (HBITMAP)LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_TIGER));
ReleaseDC(hwnd, hdc);
break;
case WM_TIMER:
posX += 3;
InvalidateRgn(hwnd, NULL, true);
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
memdc = CreateCompatibleDC(hdc);
memdc1 = CreateCompatibleDC(memdc);
memdc2 = CreateCompatibleDC(memdc);
oldHouseBit = (HBITMAP)SelectObject(memdc1, hHouseBit);
oldTigerBit = (HBITMAP)SelectObject(memdc2, hTigerBit);
oldMixBit = (HBITMAP)SelectObject(memdc, hMixBit);
BitBlt(memdc, 0, 0, 819, 614, memdc1, 0, 0, SRCCOPY);
BitBlt(memdc, posX, 0, 67, 47, memdc2, 0, 0, SRCCOPY);
BitBlt(hdc, 0, 0, 819, 614, memdc, 0, 0, SRCCOPY);
SelectObject(memdc1, oldHouseBit);
SelectObject(memdc2, oldTigerBit);
SelectObject(memdc, oldMixBit);
Related
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.
On Windows, I'm trying to use CEF (Chromium Embedded Framework) to create a window application with parent window to be transparent and its child window to be opaque (I want to have a rounded corner and an arrow pointing to the status bar in the child window). Something similar to:
I tried to use SetLayeredWindowAttributes to make the parent window transparent but it also makes the child window transparent. Is there a way to make this happen on Windows?
SetLayeredWindowAttributes needs a color for transparency. Make sure the transparency color is not used by the child window. You can pick a random color, for example RGB(255, 0, 254) and assume the child window is not using it.
If you have no control over the child window, and you can't be sure what colors it might use, then SetWindowRgn is another option to create non-rectangular windows.
The example below shows how to set the region such that the corners are round with a triangle on top.
You can use GDI+ to gain more flexibility for drawing the region, and for anti-aliasing effect so that the borders look more smooth.
#include <Windows.h>
int triangle_height = 30;
int corner_size = 20;
int caption_height = 60;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_CREATE:
{
//create a child button for testing
CreateWindowW(L"BUTTON", L"Close", WS_CHILD | WS_VISIBLE,
10, caption_height + 10, 100, 30,
hwnd, HMENU(100), GetModuleHandle(NULL), NULL);
RECT rc;
GetClientRect(hwnd, &rc);
//create a triangle region
int w = rc.right;
int h = rc.bottom;
int z = triangle_height;
POINT pt[3];
pt[0] = { w / 2, 0 };
pt[1] = { w / 2 - z, z };
pt[2] = { w / 2 + z, z };
HRGN htri = CreatePolygonRgn(pt, 3, WINDING);
//create a round rectangle region
HRGN hrgn = CreateRoundRectRgn(0, z, w, h - z, corner_size, corner_size);
//combine the triangle with round rectangle
CombineRgn(hrgn, htri, hrgn, RGN_OR);
//set the new region
SetWindowRgn(hwnd, hrgn, TRUE);
DeleteObject(htri);
DeleteObject(hrgn);
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
//we don't have a standard title bar, paint one here:
rc.bottom = caption_height;
SetDCBrushColor(hdc, RGB(80, 80, 80));
FillRect(hdc, &rc, (HBRUSH)GetStockObject(DC_BRUSH));
//paint the background
rc = ps.rcPaint;
rc.top = caption_height;
SetDCBrushColor(hdc, RGB(240, 240, 240));
FillRect(hdc, &rc, (HBRUSH)GetStockObject(DC_BRUSH));
//use FrameRgn to paint a border around the region
HRGN hrgn = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hwnd, hrgn);
SetDCBrushColor(hdc, RGB(128, 128, 128));
FrameRgn(hdc, hrgn, (HBRUSH)GetStockObject(DC_BRUSH), 1, 1);
DeleteObject(hrgn);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCHITTEST:
{
//we don't have a standard title-bar
//respond to our custome title-bar manually:
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hwnd, &pt);
if(pt.y < caption_height)
return HTCAPTION;
break;
}
case WM_COMMAND:
if(HIWORD(wparam) == BN_CLICKED)
if(LOWORD(wparam) == 100)
SendMessage(hwnd, WM_CLOSE, 0, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
WNDCLASSEXW wcex = { sizeof(wcex) };
wcex.style = CS_DROPSHADOW;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszClassName = L"classname";
RegisterClassExW(&wcex);
CreateWindowW(wcex.lpszClassName, L"Test", WS_VISIBLE | WS_POPUP,
200, 200, 600, 400, 0, 0, hInstance, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
I am trying to print a brush pattern like I see on the screen but I get different results depending on the printer DC. I use Microsoft PDF or a printer. Both are printing using 600 DPI. I'm trying to get the same pattern when I'm printing by resizing the bitmap that is being used by CreatePatternBrush. This seems to work fine for Microsoft PDF but not for the printer. The pattern using the printer is way too large. Any clue why?
OnBnClickedButtonprint is where I'm trying to print where ScaleBitmap is done on the pattern bitmap. Here is the code:
#include "stdafx.h"
#include "Printing.h"
#include "PrintingDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
RECT rcSmall = { 25, 25, 75, 75 };
RECT rcBig = { 55, 55, 255, 255 };
RECT rcWholeArea = { 0, 0, 300, 300 };
COLORREF crRed = RGB(255, 0, 0);
COLORREF crGreen = RGB(0, 255, 0);
COLORREF crBlue = RGB(0, 0, 255);
COLORREF crWhite = RGB(255, 255, 255);
COLORREF crBlack = RGB(0, 0, 0);
CPrintingDlg::CPrintingDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_PRINTING_DIALOG, pParent)
{
WORD HatchBits[8] = { 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe };
m_hBitmap = ::CreateBitmap(8, 8, 1, 1, HatchBits);
}
void CPrintingDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CPrintingDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTONPRINT, &CPrintingDlg::OnBnClickedButtonprint)
END_MESSAGE_MAP()
// CPrintingDlg message handlers
BOOL CPrintingDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
return TRUE; // return TRUE unless you set the focus to a control
}
HBITMAP CPrintingDlg::ScaleBitmap(HDC hdc, HBITMAP hBitmapSrc)
{
double gDPIScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f;
double gDPIScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0f;
if (gDPIScaleX == 1.0 && gDPIScaleY == 1.0)
return hBitmapSrc;
// Get logical coordinates
BITMAP bm;
::GetObject(hBitmapSrc, sizeof(bm), &bm);
int iWidth = (int)(bm.bmWidth * gDPIScaleX);
int iHeight = (int)(bm.bmHeight * gDPIScaleY);
// Select the source DC
HDC hdcSrc = ::CreateCompatibleDC(hdc);
HBITMAP hBitmapOldSrc = (HBITMAP)::SelectObject(hdcSrc, hBitmapSrc);
// Create the bitmap and select the destination DC
HDC hdcDst = ::CreateCompatibleDC(hdc);
HBITMAP hBitmapDst = ::CreateCompatibleBitmap(hdcDst, iWidth, iHeight);
HBITMAP hBitmapOldDst = (HBITMAP)::SelectObject(hdcDst, hBitmapDst);
// Resize
::StretchBlt(hdcDst, 0, 0, iWidth, iHeight, hdcSrc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
// Reselect the old bitmaps
::SelectObject(hdcSrc, hBitmapOldSrc);
::SelectObject(hdcDst, hBitmapOldDst);
// Delete the resources
DeleteDC(hdcSrc);
DeleteDC(hdcDst);
return hBitmapDst;
}
RECT CPrintingDlg::ScaleRect(HDC hdc, RECT rcOriginal)
{
double gDPIScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f;
double gDPIScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0f;
if (gDPIScaleX == 1.0 && gDPIScaleY == 1.0)
return rcOriginal;
RECT rcScaled;
rcScaled.left = (long)(rcOriginal.left * gDPIScaleX);
rcScaled.right = (long)(rcOriginal.right * gDPIScaleX);
rcScaled.top = (long)(rcOriginal.top * gDPIScaleY);
rcScaled.bottom = (long)(rcOriginal.bottom * gDPIScaleY);
return rcScaled;
}
void CPrintingDlg::DrawRectangle(HDC hdc, RECT rect, COLORREF cr)
{
HPEN hPen = ::CreatePen(PS_SOLID, 1, cr);
::SelectObject(hdc, hPen);
HBRUSH hBrush = ::CreateSolidBrush(cr);
::SelectObject(hdc, hBrush);
::Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
}
void CPrintingDlg::DrawPatternRectangle(HDC hdc, HBITMAP hBitmap, RECT rect, COLORREF cr)
{
int oldROP2 = 0;
HPEN hPen = ::CreatePen(PS_SOLID, 1, crBlack);
::SelectObject(hdc, hPen);
HBRUSH hPatternBrush = ::CreatePatternBrush(hBitmap);
::SelectObject(hdc, hPatternBrush);
::SetTextColor(hdc, crBlack);
if (::GetROP2(hdc) == R2_COPYPEN)
{
::SetBkColor(hdc, crWhite);
oldROP2 = ::SetROP2(hdc, R2_MASKNOTPEN);
::Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
::SetBkColor(hdc, cr);
::SetROP2(hdc, R2_MERGEPEN);
::Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
::SetROP2(hdc, oldROP2);
}
else
{
::SetBkColor(hdc, cr);
::Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
}
}
void CPrintingDlg::OnPaint()
{
CPaintDC hdc(this);
DrawRectangle(hdc, rcWholeArea, crWhite);
DrawRectangle(hdc, rcSmall, crGreen);
DrawPatternRectangle(hdc, m_hBitmap, rcBig, crRed);
}
void CPrintingDlg::OnBnClickedButtonprint()
{
CPrintDialog dlgPrint(FALSE, PD_NOSELECTION);
dlgPrint.GetDefaults();
dlgPrint.m_pd.Flags &= ~PD_RETURNDEFAULT;
DEVMODE* dm = (DEVMODE*)GlobalLock(dlgPrint.m_pd.hDevMode);
dm->dmFields |= DM_ORIENTATION;
dm->dmOrientation = DMORIENT_LANDSCAPE;
GlobalUnlock(dlgPrint.m_pd.hDevMode);
dlgPrint.DoModal();
HDC hdc = dlgPrint.GetPrinterDC();
DOCINFO docInfo;
ZeroMemory(&docInfo, sizeof(docInfo));
docInfo.cbSize = sizeof(docInfo);
docInfo.lpszDocName = _T("PrintTest");
StartDoc(hdc, &docInfo);
StartPage(hdc);
DrawRectangle(hdc, ScaleRect(hdc, rcWholeArea), crWhite);
DrawRectangle(hdc, ScaleRect(hdc, rcSmall), crGreen);
DrawPatternRectangle(hdc, ScaleBitmap(hdc, m_hBitmap), ScaleRect(hdc, rcBig), crRed);
EndPage(hdc);
EndDoc(hdc);
DeleteDC(hdc);
}
Here is what I'm trying to print:
GetWindowRect(hWnd, &wnd);
hdc = BeginPaint(hWnd, &ps);
hdcMem = CreateCompatibleDC(hdc);
for (int i = 0; i < n; ++i)
{
HRGN rgn = CreateRoundRectRgn(0, 0, CARD_WIDTH, CARD_HEIGHT, 7, 7);
SetWindowRgn(cards[info[i].card], rgn, TRUE);
oldBitmap = SelectObject(hdcMem, cards[info[i].card]);
GetObject(cards[info[i].card], sizeof(bitmap), &bitmap);
BitBlt(hdc, info[i].pos.x, info[i].pos.y, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, oldBitmap);
}
I have problem with cutting corners for bitmap picture.
GetWindowRect(hWnd, &wnd);
hdc = BeginPaint(hWnd, &ps);
hdcMem = CreateCompatibleDC(hdc);
for (int i = 0; i < n; ++i)
{
HRGN rgn = CreateRoundRectRgn(info[i].pos.x, info[i].pos.y, info[i].pos.x + CARD_WIDTH, info[i].pos.y + CARD_HEIGHT, 7, 7);
SelectClipRgn(hdc, rgn);
oldBitmap = SelectObject(hdcMem, cards[info[i].card]);
GetObject(cards[info[i].card], sizeof(bitmap), &bitmap);
BitBlt(hdc, info[i].pos.x, info[i].pos.y, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, oldBitmap);
}
Thanks to Jonathan Potter.
I attampted to draw a irregular window with the UpdateLayeredWindow(), in msvc2008, xp sp3.
Here is part of my code:
//Add something(CreateWindowEx()):
hwndCyauWnd = CreateWindowEx(
/*WS_EX_TOOLWINDOW |*/ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_LAYERED,
lpwsCyauClassName,
lpwsCyauWndName,
WS_CLIPSIBLINGS | WS_POPUP,
GetSystemMetrics(SM_CXSCREEN)-320,
GetSystemMetrics(SM_CYSCREEN)-232,
320, 200,
NULL,
NULL,
hInstance,
NULL);
//Skip Lines
HDC hdcCyauWnd = GetDC(hwndCyauWnd);
HDC hdcBuffer = CreateCompatibleDC(hdcCyauWnd);
//HBITMAP hbmCyau = CreateCompatibleBitmap(hdcBuffer,120, 93);
//SelectObject(hdcBuffer, hbmCyau);
POINT ptZero = {0, 0};
POINT ptDrawPos = {0, 0};
RECT rctCyauWnd;
GetWindowRect(hwndCyauWnd, &rctCyauWnd);
SIZE szCyauWnd={rctCyauWnd.right - rctCyauWnd.left, rctCyauWnd.bottom - rctCyauWnd.top};
BLENDFUNCTION blendPixelFunction = { AC_SRC_OVER, 0, 100, AC_SRC_ALPHA};
Graphics gphCyauWnd(hdcBuffer);
Image imgCyau(L"surface0000.png");
gphCyauWnd.DrawImage(&imgCyau, 0, 0, 125, 93);
UpdateLayeredWindow(hwndCyauWnd,
hdcCyauWnd, &ptZero,
&szCyauWnd,
hdcBuffer, &ptZero,
0, //RGB(255, 255, 255),
&blendPixelFunction,
ULW_ALPHA);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
I have tried several method to use this function, but all failed, NOTHING APPEAR on the screen.
Could anybody tell me what happens and how to slove it?
Add:
Whole source file have been upload to my skydrive, anyone can edit, much appreciation! (I have become a poor underdog now...)
You mixed up GDI and GDI+, which is not a good idea. Here is a working example:
hWnd = CreateWindowEx(WS_EX_LAYERED, szWindowClass, szTitle, 0,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
// Load PNG
CImage img;
img.Load("BACKGR.png");
// Get image sizes
int nWidth = img.GetWidth();
int nHeight = img.GetHeight();
// Create memory DC
HDC hdcScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hdcScreen);
// Create memory bitmap
HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, nWidth, nHeight);
HBITMAP hBmpOld = (HBITMAP)SelectObject(hDC, hBmp);
// Draw image to memory bitmap (currently selected in memory DC)
img.Draw(hDC, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight);
// Call UpdateLayeredWindow
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 128;// half transparent
blend.AlphaFormat = AC_SRC_ALPHA;
POINT ptLocation = {0, 0};
SIZE szWnd = {nWidth, nHeight};
POINT ptSrc = {0, 0};
UpdateLayeredWindow(hWnd, hdcScreen, &ptLocation, &szWnd, hDC, &ptSrc, 0, &blend, ULW_ALPHA);
ShowWindow(hWnd, SW_SHOW);
SelectObject(hDC, hBmpOld);
DeleteObject(hBmp);
DeleteDC(hDC);
ReleaseDC(NULL, hdcScreen);
If you want GDI+ to draw to an image with an alpha channel, you have to draw to a Bitmap, not an HDC, and you have to specify that the Bitmap's format has alpha. To do that with an HBITMAP, you have to also point GDI+ to the bitmap bits.
Something like this:
BITMAPINFOHEADER bih;
HBITMAP hbmp;
HDC hdc;
void *bits;
bih.biSize = sizeof(bih);
bih.biWidth = width;
bih.biHeight = -height;
bih.biPlanes = 1;
bih.biBitCount = 32;
bih.biCompression = BI_RGB;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
hdc = CreateCompatibleDC(NULL);
hbmp = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS, &bits, NULL, 0);
Bitmap bitmap(width, height, 0, PixelFormat32bppPARGB, bits);
Graphics graphics(bitmap);
graphics->DrawWhatever(...);
graphics->Flush();
SelectObject(hdc, hbitmap);
UpdateLayeredWindow(hwnd, hdc, ...