How to change background color of SysDateTimePick32 or CDateTimeCtrl? - c++

I seem not to be able to change the background color of a SysDateTimePick32 control (white in this case):
in my Win32/MFC application.
I first tried overriding OnCtlColor notification message in the parent window, which wasn't even called.
I then tried a subclassing approach described here, which was called alright but the control did not change visually. (I did my tests on Windows 8.1 machine.)
So does anyone have idea how to do it?
PS. I need this to work on Windows XP and up.

I'm not sure how much of a hack the following solution is, but it seems to work for a quick fix until someone suggests a better one. Again, it is based on this code, and also requires Windows Vista or later OS:
//Called from a subclassed WndProc
case WM_PAINT:
{
PAINTSTRUCT ps;
::BeginPaint(hWnd, &ps);
//Render control
RenderWithBkgndColor(hWnd, ps.hdc, RGB(255, 0, 0));
::EndPaint(hWnd, &ps);
return 0;
}
void RenderWithBkgndColor(HWND hWnd, HDC hDC, COLORREF clrBkgnd)
{
//Render control with the background color
//'clrBkgnd' = color for the background (if control is enabled)
//SOURCE:
// http://comp.os.ms-windows.programmer.win32.narkive.com/0H8cHhw1/setting-color-for-sysdatetimepick32-control
RECT rect;
::GetWindowRect(hWnd, &rect);
::MapWindowPoints(NULL, hWnd, (LPPOINT)&rect, 2);
long nWidth = rect.right - rect.left, nHeight = rect.bottom - rect.top;
HDC hDCMem = ::CreateCompatibleDC(hDC);
HBITMAP hBitmap = ::CreateBitmap(nWidth, nHeight, ::GetDeviceCaps(hDC, PLANES), ::GetDeviceCaps(hDC, BITSPIXEL), (const void *) NULL);
if (hBitmap)
{
HBITMAP hBitmapOld = (HBITMAP)::SelectObject(hDCMem, hBitmap);
//Render control itself
::SendMessage(hWnd, WM_PRINT, (WPARAM)hDCMem, PRF_CLIENT | PRF_CHILDREN | PRF_NONCLIENT);
//Only if we have the color
if(clrBkgnd != NULL)
{
//Only if control is enabled
if((::GetWindowLongPtr(hWnd, GWL_STYLE) & (WS_VISIBLE | WS_DISABLED)) == (WS_VISIBLE | 0))
{
#define ALLOWED_DIFF 20
DWORD dwBkgClr = ::GetSysColor(COLOR_WINDOW); //0xFFFFFF;
DWORD br0 = dwBkgClr & 0xFF;
DWORD br1 = (dwBkgClr & 0xFF00) >> 8;
DWORD br2 = (dwBkgClr & 0xFF0000) >> (8 * 2);
for(int y = 0; y < nHeight; y++)
{
for(int x = 0; x < nWidth; x++)
{
COLORREF clrPxl = ::GetPixel(hDCMem, x, y);
DWORD r0 = clrPxl & 0xFF;
DWORD r1 = (clrPxl & 0xFF00) >> 8;
DWORD r2 = (clrPxl & 0xFF0000) >> (8 * 2);
int nDiff_r0 = r0 - br0;
int nDiff_r1 = r1 - br1;
int nDiff_r2 = r2 - br2;
if(abs(nDiff_r0) < ALLOWED_DIFF &&
abs(nDiff_r1) < ALLOWED_DIFF &&
abs(nDiff_r2) < ALLOWED_DIFF)
{
::SetPixel(hDCMem, x, y, clrBkgnd);
}
}
}
}
}
::BitBlt(hDC, rect.left, rect.top, nWidth, nHeight, hDCMem, 0, 0, SRCCOPY);
::SelectObject(hDCMem, hBitmapOld);
::DeleteObject(hBitmap);
}
::DeleteDC(hDCMem);
}

Related

win api DoubleBuffering

I'm not good at winapi field.. so I want to ask your help.
Below is the code.
void DrawScreen(HDC hdc)
{
int x = 0;
int y = 0;
BITMAP bit;
HBITMAP backBitmap;
HBITMAP hOldBitmap;
HBITMAP hOldBitmap2;
HDC backMemDC, MemDc;
backBitmap = CreateCompatibleBitmap(hdc, rt.right, rt.bottom);
backMemDC = CreateCompatibleDC(hdc);
MemDc = CreateCompatibleDC(backMemDC);
//hOldBitmap2 = (HBITMAP)SelectObject(MemDc, backBmitmap2);
FillRect(backMemDC, &rt, (HBRUSH)GetStockObject(WHITE_BRUSH));
hOldBitmap = (HBITMAP)SelectObject(backMemDC, backBitmap);
for ( y = 0; y < 3; ++y)
{
for ( x = 0; x < 3; ++x)
{
switch (cChessBoard[y][x])
{
case '#':
iBit = WHITE_BOARD;
break;
case '#':
iBit = BLACK_BOARD;
break;
SelectObject(MemDc, hBit[iBit]);
}//end of switch
hOldBitmap2 = (HBITMAP)SelectObject(MemDc,hBit[iBit]);
BitBlt(backMemDC, x*BW, y*BH, BW, BH, MemDc, 0, 0, SRCCOPY);
}//end of for
}//end of for
hOldBitmap2 = (HBITMAP)SelectObject(MemDc, hBit[PLAYER]);
BitBlt(backMemDC, Player.m_iX*BW, Player.m_iY*BH, BW, BH, MemDc, 0, 0, SRCCOPY);
//InvalidateRect(hWndMain, NULL, FALSE);
BitBlt(hdc, 0, 0, rt.right, rt.bottom, backMemDC, 0, 0, SRCCOPY);
ReleaseDC(hWndMain,hdc);
DeleteObject(hOldBitmap);
DeleteDC(backMemDC);
DeleteDC(MemDc);
}
As you can see from the code above, I want to do double buffering.
But I still can not stop flickering... Someone help me please?
Moving to memory dc and moving things to hdc is not right?

Capture game window using wm_paint

I'm trying to capture a game window using SendMessage with wm_paint and wm_printclient.
I already did it successfully using PrintWindow but the game can change between graphic engines and for some of them I get a white rectangle. I was hoping using SendMessage would not have this problem.
The problem is I'm getting a black rectangle as result of SendMessage, for any graphic engine and even for any program/window.
void capture::captureProgramScreen(HWND hwnd, tImage* res)
{
RECT rc;
GetWindowRect(hwnd, &rc);
//create
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, rc.right - rc.left, rc.bottom - rc.top);
res->width = rc.right - rc.left - 17;
res->height = rc.bottom - rc.top - 39;
res->absoluteTop = rc.top;
res->absoluteLeft = rc.left;
SelectObject(hdc, hbmp);
SendMessage(hwnd, WM_PRINTCLIENT, (int)hdc, PRF_CHILDREN | PRF_CLIENT | PRF_ERASEBKGND | PRF_NONCLIENT | PRF_OWNED);
BITMAPINFO MyBMInfo = { 0 };
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
if (0 == GetDIBits(hdc, hbmp, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS))
{
res->error = true;
res->errorcode = 2;
return;
}
res->v = std::vector<BYTE>(MyBMInfo.bmiHeader.biSizeImage);
MyBMInfo.bmiHeader.biBitCount = 32;
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biHeight = abs(MyBMInfo.bmiHeader.biHeight);
if (0 == GetDIBits(hdc, hbmp, 0, MyBMInfo.bmiHeader.biHeight, &(res->v[0]), &MyBMInfo, DIB_RGB_COLORS))
{
res->error = true;
res->errorcode = 3;
res->width = 0;
res->height = 0;
res->v.clear();
return;
}
//4 Bytes per pixel order (B G R A) from [left to right] [bottom to top]
return;
}
Thank you!
There are at least a few possible issues:
Not all programs/windows implement WM_PRINTCLIENT. Many games don't even implement WM_PAINT, as they draw continuously at their desired frame rate rather than in response to a need to update themselves. Many games use newer graphics APIs that don't really draw to a Device Context.
I'm not sure why you have two calls to GetDIBits. The first one happens before you initialize all the fields of the BITMAPINFO, so that one will fail. It's still not completely filled out by the time you make the second call.

Change the color of the title bar (caption) of a win32 application

I want to change the color of the title bar in my application as I have seen done in programs such as Skype Preview. I have found only one solution offered on the internet for this (WM_NCPAINT) which seems to require me to draw a completely custom title bar which is of course not ideal when all I want to do is change the background color. Is anyone aware of a better solution? Someone suggested hooking GetSysColor, but it is never called with an index of 2 (COLOR_ACTIVECAPTION) so the color is being retrieved from elsewhere.
Current title bar:
(source: pbrd.co)
End goal:
You can change window title bar color / behaviour with DWMAPI's DwmSetWindowAttribute function in Win32.
NOTE: Might require Windows SDK 10.0.22000.0 (aka first Windows 11 SDK) as DWMWA_USE_IMMERSIVE_DARK_MODE|DWMWA_BORDER_COLOR|DWMWA_CAPTION_COLOR were not documented in Windows SDK 10.0.19041.0 (latest Windows 10 SDK). People have got DWMWA_USE_IMMERSIVE_DARK_MODE working before the public documentation of the variable by simply using dwAttribute as 20 (or 19 if Windows was before Windows 10 20H1). Here is an example of Qt using it in their Windows platform integration by allowing application to use it by passing -platform windows:darkmode=1 flag when launching the application.
To enable immersive dark mode (available in Windows 10 / 11, but undocumented in Windows 10 SDK (can be used by setting value as dwAttribute as 20), value was 19 pre Windows 10 20H1 Update)
Immersive Dark Mode Enabled
Immersive Dark Mode Enabled Out of Focus
#include <dwmapi.h>
BOOL USE_DARK_MODE = true;
BOOL SET_IMMERSIVE_DARK_MODE_SUCCESS = SUCCEEDED(DwmSetWindowAttribute(
WINhWnd, DWMWINDOWATTRIBUTE::DWMWA_USE_IMMERSIVE_DARK_MODE,
&USE_DARK_MODE, sizeof(USE_DARK_MODE)));
To change border color (only in Windows 11 SDK)
Border Color set to 0x00FFFFFF
Border Color set to 0x00505050
#include <dwmapi.h>
COLORREF DARK_COLOR = 0x00505050;
BOOL SET_CAPTION_COLOR = SUCCEEDED(DwmSetWindowAttribute(
WINhWnd, DWMWINDOWATTRIBUTE::DWMWA_BORDER_COLOR,
&DARK_COLOR, sizeof(DARK_COLOR)));
To change caption color (only in Windows 11 SDK)
Caption Color set to 0x00FFFFFF
Caption Color set to 0x00505050
#include <dwmapi.h>
COLORREF DARK_COLOR = 0x00505050;
BOOL SET_CAPTION_COLOR = SUCCEEDED(DwmSetWindowAttribute(
WINhWnd, DWMWINDOWATTRIBUTE::DWMWA_CAPTION_COLOR,
&DARK_COLOR, sizeof(DARK_COLOR)));
The first thing I'm going to say is: You have been warned!
This is an awfully laborious task. It's a long way from easy and much time was spent reading the Wine sources (linux implementation of native win32 functionality)
Seeing this question brought back 'fond' (read: exasperating!) memories of my efforts to achieve the same result. The process is a somewhat convoluted one and foisters upon you a great many more responsibilities than simply painting the title-bar. (I've included about 500 lines of code)
Amongst other things, you need to handle window activation/inactivation, sizing, NC area buttons, application icon and title-text.
Using some (drawing) utility functions in other files I've not included, the following was achieved:
Both of which are modifications to this dialog:
With the assistance of these (color-keyed) images:
and some stretching/drawing (the image is divided into 9 pieces)
I note upon re-vising this code, that the borders are over-drawn by the client-area. I imagine, because I've not sized it correctly in response to the WM_NCCALCSIZE message. I also used another image which did indeed have borders of 8pixels wide, rather than the 14 that these two show. (You can see the commented-out code in response to the message I mentioned)
The idea is that first, we sub-class the standard dialog-box's WindowProc. In this subclassed handler, we tell the Desktop Window Manager to disable composition for our window, we setup a layered window (this is how the black one appears semi-transparent) and then finally, do the non-client drawing ourselves in response to a WM_NCPAINT message.
I'll also point out that for reasons that have long since escaped me, I wasn't especially satisfied with the functioning of the code.
With that said, here's some code to sink your teeth into:
#define _WIN32_WINNT 0x0501
#define UNICODE 1
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include "resource.h"
#include "../graphics/graphics.h"
#include <dwmapi.h>
using namespace Gdiplus;
HINSTANCE hInst;
LRESULT CALLBACK DlgSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, LPARAM lParam);
HBITMAP frameImg, frameRedImg, frameOrigImg;
int frameIndex = 0;
//HRESULT DwmEnableComposition(UINT uCompositionAction);
typedef HRESULT (WINAPI *pFnDwmEnableComposition)(UINT uCompAction);
typedef HRESULT (WINAPI *pFnDwmSetWindowAttribute)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
#define WM_DMWNCRENDERINGCHANGED 0x31F
// wParam = 1 (fRenderingEnabled = true)
// wParam = 0 (fRenderingEnabled = false)
HRESULT EnableNcDwm(HWND hwnd, bool enable)
{
HMODULE dwmMod = LoadLibrary(L"dwmapi.dll");
if (dwmMod)
{
pFnDwmSetWindowAttribute DwmSetWindowAttribute;
DwmSetWindowAttribute = (pFnDwmSetWindowAttribute)GetProcAddress(dwmMod, "DwmSetWindowAttribute");
HRESULT hr = S_OK;
DWMNCRENDERINGPOLICY ncrp;
if (enable)
ncrp = DWMNCRP_ENABLED;
else
ncrp = DWMNCRP_DISABLED;
// Disable non-client area rendering on the window.
hr = DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncrp, sizeof(ncrp));
FreeLibrary(dwmMod);
if (SUCCEEDED(hr))
{
return hr;
}
}
return S_FALSE;
}
/*
#define DWM_EC_DISABLECOMPOSITION 0
#define DWM_EC_ENABLECOMPOSITION 1
HRESULT EnableDWM(HWND hwnd, bool enable)
{
HMODULE dwmMod = LoadLibrary(L"dwmapi.dll");
pFnDwmEnableComposition DwmEnableComposition;
DwmEnableComposition = (pFnDwmEnableComposition)GetProcAddress(dwmMod, "DwmEnableComposition");
HRESULT hr = S_OK;
// Disable DWM Composition
if (enable)
hr = DwmEnableComposition(DWM_EC_ENABLECOMPOSITION);
else
hr = DwmEnableComposition(DWM_EC_DISABLECOMPOSITION);
FreeLibrary(dwmMod);
return hr;
}
*/
BOOL CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static bool isSubclassed = false;
switch(uMsg)
{
case WM_DMWNCRENDERINGCHANGED:
{
long dwEx = GetWindowLong(hwndDlg, GWL_EXSTYLE);
dwEx &= ~(WS_EX_LAYERED);
SetWindowLong(hwndDlg, GWL_EXSTYLE, dwEx);
InvalidateRect(hwndDlg, NULL, true);
UpdateWindow(hwndDlg);
MoveAnchorsImmediatelly(hwndDlg);
}
return 0;
// case WM_ERASEBKGND:
// {
// RECT rc;
// GetClientRect(hwndDlg, &rc);
// FillRect((HDC)wParam, &rc, (HBRUSH) COLOR_BACKGROUND);
// return 1;
// }
case WM_INITDIALOG:
{
mSetAnchorMode(GetDlgItem(hwndDlg, IDC_BUTTON1), ANCHOR_CENTER);
}
return TRUE;
case WM_SIZE:
{
//MoveAnchorsImmediatelly(hwndDlg);
DeferAnchorsMove(hwndDlg);
}
return TRUE;
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDC_BUTTON1:
if (isSubclassed == false)
{
SetWindowSubclass( hwndDlg, DlgSubclassProc, 1, NULL);
EnableNcDwm(hwndDlg, false);
frameIndex++;
frameIndex &= 1; // make sure it can only be in range [0..1]
}
else
{
RemoveWindowSubclass( hwndDlg, DlgSubclassProc, 1);
EnableNcDwm(hwndDlg, true);
}
isSubclassed = !isSubclassed;
// InvalidateRect(hwndDlg, NULL, true);
// UpdateWindow(hwndDlg);
// MoveAnchorsImmediatelly(hwndDlg);
break;
}
}
return TRUE;
}
return FALSE;
}
LRESULT CALLBACK DlgSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
static byte alpha = 255;
switch (uMsg)
{
case WM_ENTERSIZEMOVE:
printf("WM_ENTERSIZEMOVE\n");
return 0;
break;
case WM_EXITSIZEMOVE:
printf("WM_EXITSIZEMOVE\n");
return 0;
break;
case WM_MOUSEWHEEL:
if (((SHORT)(HIWORD(wParam))) > 0)
{
if (alpha > 30)
alpha -= 5;
}
else if (alpha < 255)
alpha += 5;
SetLayeredWindowAttributes(hwnd, RGB(255,0,255), alpha, LWA_COLORKEY|LWA_ALPHA);
UpdateWindow(hwnd);
return 0;
case WM_DMWNCRENDERINGCHANGED:
{
// printf("WM_DMWNCRENDERINGCHANGED\n");
long dwEx = GetWindowLong(hwnd, GWL_EXSTYLE);
dwEx |= WS_EX_LAYERED;
SetWindowLong(hwnd, GWL_EXSTYLE, dwEx);
SetLayeredWindowAttributes(hwnd, RGB(255,0,255), alpha, LWA_COLORKEY|LWA_ALPHA);
//MoveAnchorsImmediatelly(hwnd);
DeferAnchorsMove(hwnd);
InvalidateRect(hwnd, NULL, true);
UpdateWindow(hwnd);
// showWndRect(hwnd);
return 0;
}
break;
case WM_NCACTIVATE:
case WM_NCPAINT:
// printf("WM_NCPAINT -");
// printf("wParam: 0x%08d lParam 0x%08x\n", wParam, lParam);
onNcPaint(hwnd, wParam, lParam);
return 0;
case WM_NCCALCSIZE:
{
RECT *rc = (RECT*)lParam;
rc->left += 8; // frame image margin widths
rc->top += 30;
rc->right -= 8;
rc->bottom -= 8;
// rc->left += 14; // frame image margin widths
// rc->top += 39;
// rc->right -= 14;
// rc->bottom -= 14;
}
return (WVR_HREDRAW | WVR_VREDRAW);
case WM_NCHITTEST:
{
POINT mousePos, rawMousePos;
RECT clientRect, windowRect;
mousePos.x = LOWORD(lParam);
mousePos.y = HIWORD(lParam);
rawMousePos = mousePos;
GetClientRect(hwnd, &clientRect);
GetWindowRect(hwnd, &windowRect);
ScreenToClient(hwnd, &mousePos);
if ((mousePos.x < clientRect.left) && (rawMousePos.y < windowRect.top+8))
return HTTOPLEFT;
if ((mousePos.x > clientRect.right) && (rawMousePos.y < windowRect.top+8))
return HTTOPRIGHT;
if ( (mousePos.x < clientRect.left) && (mousePos.y > clientRect.bottom))
return HTBOTTOMLEFT;
if ( (mousePos.x > clientRect.right) && (mousePos.y > clientRect.bottom))
return HTBOTTOMRIGHT;
if (rawMousePos.x < windowRect.left+11)
return HTLEFT;
if (rawMousePos.x > windowRect.right-11)
return HTRIGHT;
if (mousePos.y > clientRect.bottom)
return HTBOTTOM;
RECT closeRect;
SetRect(&closeRect, windowRect.left + 15, windowRect.top+7, windowRect.left+15+16, windowRect.top+25);
if (PtInRect(&closeRect, rawMousePos))
{
// printf("over sys menu (appIcon) - %d,%d\n", mousePos.x, mousePos.y);
return HTSYSMENU;
}
if (rawMousePos.y < windowRect.top+8)
return HTTOP;
if (mousePos.y < 0)
return HTCAPTION;
else
return HTCLIENT;
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
// return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
// draw Frame
// HBRUSH mBrush = CreateSolidBrush( RGB(0,113,201) );
HDC hdc = GetWindowDC(hwnd);
// HDC hdc = GetDCEx(hwnd, (HRGN)wParam, DCX_WINDOW);//|DCX_INTERSECTRGN);
RECT mRect, wndRect;
GetWindowRect(hwnd, &mRect);
wndRect = mRect;
mRect.right -= mRect.left;
mRect.bottom -= mRect.top;
mRect.left = 0;
mRect.top = 0;
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP old, memBmp;
old = (HBITMAP)GetCurrentObject(memDC, OBJ_BITMAP);
memBmp = CreateCompatibleBitmap(hdc, mRect.right, mRect.bottom);
//memBmp = zCreateDibSection(hdc, mRect.right, mRect.bottom, 24);
SelectObject(memDC, memBmp);
//StretchNineDraw(HDC destDC, RECT destRect, HBITMAP srcImage, RECT srcRect,
// int marginLeft, int marginTop, int marginRight, int marginBottom, int alpha);
if (frameIndex == 0)
// StretchNineDraw(memDC, mRect, frameImg, (RECT){0,0,33,58}, 16,41,16,16, 255);
StretchNineDrawNoAlpha(memDC, mRect, frameImg, (RECT){0,0,33,58}, 16,41,16,16);
else
StretchNineDraw(memDC, mRect, frameRedImg, (RECT){0,0,33,58}, 16,41,16,16, 255);
// StretchNineDrawNoAlpha(memDC, mRect, frameRedImg, (RECT){0,0,33,58}, 16,41,16,16);
// StretchNineDrawNoAlpha(memDC, mRect, frameOrigImg, (RECT){0,0,17,39}, 8,30,8,8);
//1111drawImgNineSquareStretching(memDC, mRect, frameImg, (RECT){0,0,33,58}, 16,41,16,16);
//void StretchNineDrawNoAlpha(HDC destDC, RECT destRect, HBITMAP srcImage, RECT srcRect,
// int marginLeft, int marginTop, int marginRight, int marginBottom)
// draw icon
HICON smallIcon = LoadIcon (NULL, IDI_APPLICATION);
DrawIconEx(memDC, 15, 9, smallIcon, 16, 16, 0,0, DI_NORMAL );
// draw window text
wchar_t wndText[100];
RECT textRect;
textRect.left = 9 + 16 + 9;
textRect.top = 0;
textRect.right = 1000;
textRect.bottom = 32;
GetWindowText(hwnd, wndText, 99);
//int oldMode = SetBkMode(hdc, TRANSPARENT);
//int oldMode = SetBkMode(memDC, TRANSPARENT);
SetBkMode(memDC, TRANSPARENT);
HFONT oldFont, hfont0 = CreateFont(-13, 0, 0, 0, 0, FALSE, FALSE, FALSE, 1, 0, 0, 0, 0, (L"Ms Shell Dlg"));
oldFont = (HFONT)SelectObject(memDC, hfont0);
DrawText(memDC, wndText, -1, &textRect, DT_VCENTER|DT_SINGLELINE|DT_LEFT);
SelectObject(memDC, oldFont);
DeleteObject(hfont0);
// top slice
BitBlt(hdc, 0,0, mRect.right,41, memDC, 0,0, SRCCOPY);
// left edge
BitBlt(hdc, 0, mRect.top + 41, 16, mRect.bottom - (41+16),
memDC, 0, mRect.top + 41, SRCCOPY);
// right edge
BitBlt(hdc, mRect.right-16, mRect.top + 41, 16, mRect.bottom - (41+16),
memDC, mRect.right-16, mRect.top + 41, SRCCOPY);
// bottom slice
BitBlt(hdc, 0,mRect.bottom-16, mRect.right,16, memDC, 0,mRect.bottom-16, SRCCOPY);
// BitBlt(hdc, 0,0, mRect.right,mRect.bottom, memDC, 0,0, SRCCOPY);
ReleaseDC(hwnd, hdc);
SelectObject(memDC, old);
DeleteDC(memDC);
DeleteObject(memBmp);
// ValidateRgn(hwnd, (HRGN)wParam);
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
frameImg = mLoadImageFile(L"frame.png");
frameRedImg = mLoadImageFile(L"frameRed.png");
frameOrigImg = mLoadImageFile(L"frameOrig.png");
hInst=hInstance;
InitCommonControls();
int result = DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
GdiplusShutdown(gdiplusToken);
return result;
}
Find below the functions that:
- Load a png file (uses GDI+)
- stretch draw this image onto the borders
/* BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF */
HBITMAP mLoadImageFile(wchar_t *filename)
{
HBITMAP result = NULL;
Bitmap bitmap(filename, false);
bitmap.GetHBITMAP(0, &result);
return result;
}
void GetRectSize(LPRECT tgt, int *width, int *height)
{
*width = (tgt->right - tgt->left) + 1;
*height = (tgt->bottom - tgt->top) + 1;
}
BOOL SetRectSizePos(LPRECT tgt, int xPos, int yPos, int width, int height)
{
return SetRect(tgt, xPos, yPos, xPos+(width-1), yPos+(height-1));
}
BOOL MoveRect(LPRECT tgt, int newX, int newY)
{
int width, height;
GetRectSize(tgt, &width, &height);
return SetRectSizePos(tgt, newX,newY, width,height);
}
// ****** marginLeft = 6
// ....|....|.... *
// ..tL.|.tM.|.tR.. *
// ---------------- * marginTop = 3
// ..mL.|.mM.|.mR..
// ---------------- * marginBottom = 3
// ..bL.|.bM.|.bR.. *
// ....|....|.... *
// ****** marginRight = 6
void CalcNineRects(LPRECT srcRect, RECT *dest, int marginLeft, int marginTop, int marginRight, int marginBottom)
{
int srcWidth, srcHeight;
int leftWidth, midWidth, rightWidth;
int topHeight, midHeight, botHeight;
int xOrig, yOrig;
GetRectSize(srcRect, &srcWidth, &srcHeight);
xOrig = srcRect->left;
yOrig = srcRect->top;
leftWidth = marginLeft;
midWidth = srcWidth - (marginLeft + marginRight) - 1;
rightWidth = marginRight;
topHeight = marginTop;
midHeight = srcHeight - (marginTop + marginBottom) - 1;
botHeight = marginBottom;
SetRectSizePos(&dest[0], xOrig, yOrig, leftWidth, topHeight);
SetRectSizePos(&dest[1], xOrig+(leftWidth), yOrig, midWidth, topHeight);
SetRectSizePos(&dest[2], xOrig+(leftWidth+midWidth), yOrig, rightWidth, topHeight);
SetRectSizePos(&dest[3], xOrig, yOrig+(topHeight), leftWidth, midHeight);
SetRectSizePos(&dest[4], xOrig+(leftWidth), yOrig+(topHeight), midWidth, midHeight);
SetRectSizePos(&dest[5], xOrig+(leftWidth+midWidth), yOrig+(topHeight), rightWidth, midHeight);
SetRectSizePos(&dest[6], xOrig,yOrig+(topHeight+midHeight), leftWidth, botHeight);
SetRectSizePos(&dest[7], xOrig+(leftWidth), yOrig+(topHeight+midHeight), midWidth, botHeight);
SetRectSizePos(&dest[8], xOrig+(leftWidth+midWidth), yOrig+(topHeight+midHeight), rightWidth, botHeight);
}
void StretchNineDraw(HDC destDC, RECT destRect, HBITMAP srcImage, RECT srcRect,
int marginLeft, int marginTop, int marginRight, int marginBottom,int alpha)
{
RECT destRectList[9], srcRectList[9];
int i;
int curSrcWidth, curSrcHeight, curDestWidth, curDestHeight;
HDC srcDC;
HBITMAP oldSrcBmp;
srcDC = CreateCompatibleDC(destDC);
// GetCurrentObject(srcDC, OBJ_BITMAP);
oldSrcBmp = (HBITMAP) SelectObject(srcDC, srcImage);
BLENDFUNCTION bf = {AC_SRC_OVER,0,alpha,AC_SRC_ALPHA};
int destHeight, destWidth;
GetRectSize(&destRect, &destWidth, &destHeight);
CalcNineRects(&srcRect, srcRectList, marginLeft, marginTop, marginRight, marginBottom);
CalcNineRects(&destRect, destRectList, marginLeft, marginTop, marginRight, marginBottom);
// printf("dst rect: %d,%d - %d,%d -- \n", destRect.left, destRect.top, destRect.right,destRect.bottom);
for (i=0; i<9; i++)
{
GetRectSize(&srcRectList[i], &curSrcWidth, &curSrcHeight);
GetRectSize(&destRectList[i], &curDestWidth, &curDestHeight);
AlphaBlend( destDC,
destRectList[i].left, destRectList[i].top,
curDestWidth, curDestHeight,
srcDC,
srcRectList[i].left, srcRectList[i].top,
curSrcWidth, curSrcHeight,
bf
);
}
SelectObject(srcDC, oldSrcBmp);
DeleteDC(srcDC);
}
Finally, the code for anchoring controls to the window (automatically recalculate their position when the window is re-sized)
typedef struct ANCHORPROPERTY
{
long anchorType;
RECT rc;
} *pANCHORPROPERTY;
#define ANCHOR_NONE 0
#define ANCHOR_WIDTH 1
#define ANCHOR_RIGHT 2
#define ANCHOR_CENTER_HORZ 3
#define ANCHOR_HEIGHT 4
#define ANCHOR_HEIGHT_WIDTH 5
#define ANCHOR_HEIGHT_RIGHT 6
#define ANCHOR_BOTTOM 7
#define ANCHOR_BOTTOM_WIDTH 8
#define ANCHOR_BOTTOM_RIGHT 9
#define ANCHOR_CENTER_HORZ_BOTTOM 10
#define ANCHOR_CENTER_VERT 11
#define ANCHOR_CENTER_VERT_RIGHT 12
#define ANCHOR_CENTER 13
pANCHORPROPERTY getWndAnchor(HWND hwnd);
bool removeAnchor(HWND hwnd);
bool mSetAnchorMode(HWND hwnd, long anchorMode);
BOOL CALLBACK AnchorEnum(HWND hwnd, LPARAM lParam);
void MoveAnchorsImmediatelly(HWND controlParent);
void DeferAnchorsMove(HWND controlParent);
long getChildCount(HWND controlParent);
pANCHORPROPERTY getWndAnchor(HWND hwnd)
{
return (pANCHORPROPERTY)GetProp(hwnd, L"anchor");
}
bool removeAnchor(HWND hwnd)
{
pANCHORPROPERTY pAnchor;
if (GetProp(hwnd, L"anchor") != NULL)
{
pAnchor = (pANCHORPROPERTY)RemoveProp(hwnd, L"anchor");
delete pAnchor;
}
return false;
}
bool mSetAnchorMode(HWND hwnd, long anchorMode)
{
bool result = false;
RECT rc, pr;
POINT p;
if (IsWindow(hwnd))
{
pANCHORPROPERTY pAnchor;
pAnchor = getWndAnchor(hwnd);
if (pAnchor == NULL)
{
pAnchor = new ANCHORPROPERTY;
SetProp(hwnd, L"anchor", pAnchor);
}
GetWindowRect(hwnd, &rc);
p.x = rc.left;
p.y = rc.top;
ScreenToClient( GetParent(hwnd), &p);
GetClientRect( GetParent(hwnd), &pr);
// printf("pos: %d,%d\n", p.x, p.y);
pAnchor->anchorType = mmin( mmax(anchorMode, ANCHOR_NONE), ANCHOR_CENTER);
pAnchor->rc.left = p.x;
pAnchor->rc.top = p.y;
pAnchor->rc.right = pr.right - (rc.right-rc.left + p.x);
pAnchor->rc.bottom = pr.bottom - (rc.bottom - rc.top + p.y);
result = true;
}
return result;
}
BOOL CALLBACK AnchorEnum(HWND hwnd, LPARAM lParam)
{
RECT pr, rc;
long x,y,xW,yH;
pANCHORPROPERTY pAnchor;
pAnchor = (pANCHORPROPERTY)GetProp(hwnd, L"anchor");
if (pAnchor != NULL)
{
if (pAnchor->anchorType != ANCHOR_NONE)
{
// printf("child enumerated - %d\n", pAnchor->anchorType);
RECT client, wnd;
GetClientRect(hwnd, &client);
GetWindowRect(hwnd, &wnd);
// printf("WndRect: %d x %d", (wnd.right-wnd.left) + 1, (wnd.bottom-wnd.top)+1);
// printf("client: %d x %d", client.right-client.left+1, client.bottom-client.top+1 );
int wW, wH, cW,cH;
wW = (wnd.right-wnd.left) + 1;
wH = (wnd.bottom-wnd.top) + 1;
cW = (client.right-client.left) + 1;
cH = (client.bottom-client.top) + 1;
GetClientRect(hwnd, &rc);
GetClientRect(GetParent(hwnd), &pr);
switch (pAnchor->anchorType)
{
case ANCHOR_WIDTH:
x = pAnchor->rc.left;
y = pAnchor->rc.top;
xW = mmax(pr.right - pAnchor->rc.left - pAnchor->rc.right, 0);
yH = rc.bottom;
break;
case ANCHOR_RIGHT: // = 2
x = (pr.right - rc.right - pAnchor->rc.right) - (wW-cW);
y = pAnchor->rc.top;
xW = rc.right + (wW-cW);
yH = rc.bottom + (wH-cH);
// printf("xPos, yPos: %d, %d - Size: %d x %d\n", x,y,xW,yH);
break;
case ANCHOR_CENTER_HORZ: // = 3
x = (pr.right - rc.right) / 2;
y = pAnchor->rc.top;
xW = rc.right;
yH = rc.bottom;
break;
case ANCHOR_HEIGHT: // = 4
x = pAnchor->rc.left;
y = pAnchor->rc.top;
xW = rc.right;
yH = mmax(pr.bottom - pAnchor->rc.top - pAnchor->rc.bottom, 0);
break;
case ANCHOR_HEIGHT_WIDTH: // = 5
x = pAnchor->rc.left;
y = pAnchor->rc.top;
xW = mmax(pr.right - pAnchor->rc.left - pAnchor->rc.right, 0);
yH = mmax(pr.bottom - pAnchor->rc.top - pAnchor->rc.bottom, 0);
break;
case ANCHOR_HEIGHT_RIGHT: // = 6
x = pr.right - rc.right - pAnchor->rc.right;
y = pAnchor->rc.top;
xW = rc.right;
yH = mmax(pr.bottom - pAnchor->rc.top - pAnchor->rc.bottom, 0);
break;
case ANCHOR_BOTTOM: // = 7
x = pAnchor->rc.left;
y = pr.bottom - pAnchor->rc.bottom - rc.bottom;
xW = rc.right;
yH = rc.bottom;
break;
case ANCHOR_BOTTOM_WIDTH: // = 8
x = pAnchor->rc.left;
y = pr.bottom - pAnchor->rc.bottom - rc.bottom;
xW = mmax(pr.right - pAnchor->rc.left - pAnchor->rc.right, 0);
yH = rc.bottom;
break;
case ANCHOR_BOTTOM_RIGHT: // = 9
x = pr.right - rc.right - pAnchor->rc.right;
y = pr.bottom - pAnchor->rc.bottom - rc.bottom;
xW = rc.right;
yH = rc.bottom;
break;
case ANCHOR_CENTER_HORZ_BOTTOM: // = 10
x = (pr.right - rc.right) / 2;
y = pr.bottom - pAnchor->rc.bottom - rc.bottom;
xW = rc.right;
yH = rc.bottom;
break;
case ANCHOR_CENTER_VERT: // = 11
x = pAnchor->rc.left;
y = (pr.bottom - rc.bottom) / 2;
xW = rc.right;
yH = rc.bottom;
break;
case ANCHOR_CENTER_VERT_RIGHT: // = 12
x = pr.right - rc.right - pAnchor->rc.right;
y = (pr.bottom - rc.bottom) / 2;
xW = rc.right;
yH = rc.bottom;
break;
case ANCHOR_CENTER: // = 13
x = (pr.right - rc.right) / 2;
y = (pr.bottom - rc.bottom) / 2;
xW = rc.right;
yH = rc.bottom;
break;
}
if (lParam == 0)
SetWindowPos(hwnd,0, x,y, xW,yH, SWP_NOZORDER|SWP_NOCOPYBITS);
else
DeferWindowPos((HDWP)lParam, hwnd, 0, x,y, xW,yH, SWP_NOZORDER|SWP_NOCOPYBITS);
}
}
return true;
}
void MoveAnchorsImmediatelly(HWND controlParent)
{
EnumChildWindows(controlParent, AnchorEnum, 0);
}
BOOL CALLBACK CountEnum(HWND hwnd, LPARAM lParam)
{
long *pCount = (long*)lParam;
++(*pCount);
return true;
}
long getChildCount(HWND controlParent)
{
long curCount = 0;
EnumChildWindows(controlParent, CountEnum, (LPARAM)&curCount);
// printf("Child count: %d\n", curCount);
return curCount;
}
void DeferAnchorsMove(HWND controlParent)
{
HDWP deferMoveHandle;
int childCount = getChildCount(controlParent);
deferMoveHandle = BeginDeferWindowPos(childCount);
EnumChildWindows(controlParent, AnchorEnum, (LPARAM)deferMoveHandle);
EndDeferWindowPos(deferMoveHandle);
}
You may try to create a window without a frame; i found reference here:
opening a window that has no title bar with win32
and then make your own title bar inside your application, in the color you like (located at the top of the window starting from the visible part).

EMF quality diminishes when window is shrinked, but is good when window dimensions are high

I am creating desktop application using C++ and pure WinApi. I need to display image that was given to me as SVG.
Since WinAPI supports only EMF files as vector format, I have used Inkscape to convert the file into EMF. My graphics design skills are at beginner level, but I have managed to convert SVG file into EMF successfully. However, the result is not looking as the original one, it is less "precise" so to say.
If I export the SVG as PNG and display it with GDI+, the result is the same as the original file. Unfortunately I need vector format.
To see exactly what I mean, download SVG, and EMF and PNG that I made here. Just click on Download:test.rar above 5 yellow stars ( see image below ).
Here are the instructions for creating minimal application that reproduces the problem:
1) Create default Win32 project in Visual Studio ( I use VS 2008, but this shouldn't be the problem );
2) Rewrite WM_PAINT like this:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rcClient;
GetClientRect( hWnd, &rcClient );
FillRect( hdc, &rcClient, (HBRUSH)GetStockObject( LTGRAY_BRUSH) );
// put metafile in the same place your app is
HENHMETAFILE hemf = GetEnhMetaFile( L".\\test.emf" );
ENHMETAHEADER emh;
GetEnhMetaFileHeader( hemf, sizeof(emh), &emh );
// rescale metafile, and keep proportion
UINT o_height = emh.rclFrame.bottom - emh.rclFrame.top,
o_width = emh.rclFrame.right - emh.rclFrame.left;
float scale = 0.5;
scale = (float)( rcClient.right - rcClient.left ) / o_width;
if( (float)( rcClient.bottom - rcClient.top ) / o_height < scale )
scale = (float)( rcClient.bottom - rcClient.top ) / o_height;
int marginX = ( rcClient.right - rcClient.left ) - (int)( o_width * scale );
int marginY = ( rcClient.bottom - rcClient.top ) - (int)( o_height * scale );
marginX /= 2;
marginY /= 2;
rcClient.left = rcClient.left + marginX;
rcClient.right = rcClient.right - marginX;
rcClient.top = rcClient.top + marginY;
rcClient.bottom = rcClient.bottom - marginY;
// Draw the picture.
PlayEnhMetaFile( hdc, hemf, &rcClient );
// Release the metafile handle.
DeleteEnhMetaFile(hemf);
EndPaint(hWnd, &ps);
}
break;
3) Add following handlers for WM_SIZE and WM_ERASEBKGND just below WM_PAINT :
case WM_SIZE:
InvalidateRect( hWnd, NULL, FALSE );
return 0L;
case WM_ERASEBKGND:
return 1L;
4) Resize the window to the smallest possible size, and then maximize it.
Notice that the bigger the window gets, the better the image quality is, but the smaller it gets the "less precise" the image gets. I tested this on Windows XP.
I am asking your help to get the same graphic quality of the EMF file as the original SVG.
Thank you for your time and efforts. Best regards.
Solved it!
The solution makes much, if not all of the solution I've submitted redundant. I've therefore decided to replace it with this one.
There's a number of things to take into account and a number of concepts that are employed to get the desired result. These include (in no particular order)
The need to set a maskColour that closely matches the background, while also not being present in the final computed image. Pixels that straddle the border between transparent/opaque areas are blended values of the mask and the EMF's colour at that point.
The need to choose a scaling rate that's appropriate for the source image - in the case of this image and the code I've used, I chose 8. This means that we're drawing this particular EMF at about a megapixel, even though the destination is likely to be in the vicinity of about 85k pixels.
The need to manually set the alpha channel of the generated 32bit HBITMAP, since the GDI stretching/drawing functions disregard this channel, yet the AlphaBlend function requires them to be accurate.
I also note that I've used old code to draw the background manually each time the screen is refreshed. A much better approach would be to create a patternBrush once which is then simply copied using the FillRect function. This is much faster than filling the rect with a solid colour and then drawing the lines over the top. I can't be bothered to re-write that part of the code, though I'll include a snippet for reference that I've used in other projects in the past.
Here's a couple of shots of the result I get from the code below:
Here's the code I used to achieve it:
#define WINVER 0x0500 // for alphablend stuff
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <stdint.h>
#include "resource.h"
HINSTANCE hInst;
HBITMAP mCreateDibSection(HDC hdc, int width, int height, int bitCount)
{
BITMAPINFO bi;
ZeroMemory(&bi, sizeof(bi));
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biWidth = width;
bi.bmiHeader.biHeight = height;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = bitCount;
bi.bmiHeader.biCompression = BI_RGB;
return CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, 0,0,0);
}
void makePixelsTransparent(HBITMAP bmp, byte r, byte g, byte b)
{
BITMAP bm;
GetObject(bmp, sizeof(bm), &bm);
int x, y;
for (y=0; y<bm.bmHeight; y++)
{
uint8_t *curRow = (uint8_t *)bm.bmBits;
curRow += y * bm.bmWidthBytes;
for (x=0; x<bm.bmWidth; x++)
{
if ((curRow[x*4 + 0] == b) && (curRow[x*4 + 1] == g) && (curRow[x*4 + 2] == r))
{
curRow[x*4 + 0] = 0; // blue
curRow[x*4 + 1] = 0; // green
curRow[x*4 + 2] = 0; // red
curRow[x*4 + 3] = 0; // alpha
}
else
curRow[x*4 + 3] = 255; // alpha
}
}
}
// Note: maskCol should be as close to the colour of the background as is practical
// this is because the pixels that border transparent/opaque areas will have
// their colours derived from a blending of the image colour and the maskColour
//
// I.e - if drawing to a white background (255,255,255), you should NOT use a mask of magenta (255,0,255)
// this would result in a magenta-ish border
HBITMAP HbitmapFromEmf(HENHMETAFILE hEmf, int width, int height, COLORREF maskCol)
{
ENHMETAHEADER emh;
GetEnhMetaFileHeader(hEmf, sizeof(emh), &emh);
int emfWidth, emfHeight;
emfWidth = emh.rclFrame.right - emh.rclFrame.left;
emfHeight = emh.rclFrame.bottom - emh.rclFrame.top;
// these are arbitrary and selected to give a good mix of speed and accuracy
// it may be worth considering passing this value in as a parameter to allow
// fine-tuning
emfWidth /= 8;
emfHeight /= 8;
// draw at 'native' size
HBITMAP emfSrcBmp = mCreateDibSection(NULL, emfWidth, emfHeight, 32);
HDC srcDC = CreateCompatibleDC(NULL);
HBITMAP oldSrcBmp = (HBITMAP)SelectObject(srcDC, emfSrcBmp);
RECT tmpEmfRect, emfRect;
SetRect(&tmpEmfRect, 0,0,emfWidth,emfHeight);
// fill background with mask colour
HBRUSH bkgBrush = CreateSolidBrush(maskCol);
FillRect(srcDC, &tmpEmfRect, bkgBrush);
DeleteObject(bkgBrush);
// draw emf
PlayEnhMetaFile(srcDC, hEmf, &tmpEmfRect);
HDC dstDC = CreateCompatibleDC(NULL);
HBITMAP oldDstBmp;
HBITMAP result;
result = mCreateDibSection(NULL, width, height, 32);
oldDstBmp = (HBITMAP)SelectObject(dstDC, result);
SetStretchBltMode(dstDC, HALFTONE);
StretchBlt(dstDC, 0,0,width,height, srcDC, 0,0, emfWidth,emfHeight, SRCCOPY);
SelectObject(srcDC, oldSrcBmp);
DeleteDC(srcDC);
DeleteObject(emfSrcBmp);
SelectObject(dstDC, oldDstBmp);
DeleteDC(dstDC);
makePixelsTransparent(result, GetRValue(maskCol),GetGValue(maskCol),GetBValue(maskCol));
return result;
}
int rectWidth(RECT &r)
{
return r.right - r.left;
}
int rectHeight(RECT &r)
{
return r.bottom - r.top;
}
void onPaintEmf(HWND hwnd, HENHMETAFILE srcEmf)
{
PAINTSTRUCT ps;
RECT mRect, drawRect;
HDC hdc;
double scaleWidth, scaleHeight, scale;
int spareWidth, spareHeight;
int emfWidth, emfHeight;
ENHMETAHEADER emh;
GetClientRect( hwnd, &mRect );
hdc = BeginPaint(hwnd, &ps);
// calculate the draw-size - retain aspect-ratio.
GetEnhMetaFileHeader(srcEmf, sizeof(emh), &emh );
emfWidth = emh.rclFrame.right - emh.rclFrame.left;
emfHeight = emh.rclFrame.bottom - emh.rclFrame.top;
scaleWidth = (double)rectWidth(mRect) / emfWidth;
scaleHeight = (double)rectHeight(mRect) / emfHeight;
scale = min(scaleWidth, scaleHeight);
int drawWidth, drawHeight;
drawWidth = emfWidth * scale;
drawHeight = emfHeight * scale;
spareWidth = rectWidth(mRect) - drawWidth;
spareHeight = rectHeight(mRect) - drawHeight;
drawRect = mRect;
InflateRect(&drawRect, -spareWidth/2, -spareHeight/2);
// create a HBITMAP from the emf and draw it
// **** note that the maskCol matches the background drawn by the below function ****
HBITMAP srcImg = HbitmapFromEmf(srcEmf, drawWidth, drawHeight, RGB(230,230,230) );
HDC memDC;
HBITMAP old;
memDC = CreateCompatibleDC(hdc);
old = (HBITMAP)SelectObject(memDC, srcImg);
byte alpha = 255;
BLENDFUNCTION bf = {AC_SRC_OVER,0,alpha,AC_SRC_ALPHA};
AlphaBlend(hdc, drawRect.left,drawRect.top, drawWidth,drawHeight,
memDC, 0,0,drawWidth,drawHeight, bf);
SelectObject(memDC, old);
DeleteDC(memDC);
DeleteObject(srcImg);
EndPaint(hwnd, &ps);
}
void drawHeader(HDC dst, RECT headerRect)
{
HBRUSH b1;
int i,j;//,headerHeight = (headerRect.bottom - headerRect.top)+1;
b1 = CreateSolidBrush(RGB(230,230,230));
FillRect(dst, &headerRect,b1);
DeleteObject(b1);
HPEN oldPen, curPen;
curPen = CreatePen(PS_SOLID, 1, RGB(216,216,216));
oldPen = (HPEN)SelectObject(dst, curPen);
for (j=headerRect.top;j<headerRect.bottom;j+=10)
{
MoveToEx(dst, headerRect.left, j, NULL);
LineTo(dst, headerRect.right, j);
}
for (i=headerRect.left;i<headerRect.right;i+=10)
{
MoveToEx(dst, i, headerRect.top, NULL);
LineTo(dst, i, headerRect.bottom);
}
SelectObject(dst, oldPen);
DeleteObject(curPen);
MoveToEx(dst, headerRect.left,headerRect.bottom,NULL);
LineTo(dst, headerRect.right,headerRect.bottom);
}
BOOL CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HENHMETAFILE hemf;
switch(uMsg)
{
case WM_INITDIALOG:
{
hemf = GetEnhMetaFile( "test.emf" );
}
return TRUE;
case WM_PAINT:
onPaintEmf(hwndDlg, hemf);
return 0;
case WM_ERASEBKGND:
{
RECT mRect;
GetClientRect(hwndDlg, &mRect);
drawHeader( (HDC)wParam, mRect);
}
return true;
case WM_SIZE:
InvalidateRect( hwndDlg, NULL, true );
return 0L;
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
}
}
return TRUE;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInst=hInstance;
InitCommonControls();
return DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
}
Finally, here's an example of creating a patternBrush for filling the background using the FillRect function. This approach is suitable for any tileable background.
HBRUSH makeCheckerBrush(int squareSize, COLORREF col1, COLORREF col2)
{
HDC memDC, tmpDC = GetDC(NULL);
HBRUSH result, br1, br2;
HBITMAP old, bmp;
RECT rc, r1, r2;
br1 = CreateSolidBrush(col1);
br2 = CreateSolidBrush(col2);
memDC = CreateCompatibleDC(tmpDC);
bmp = CreateCompatibleBitmap(tmpDC, 2*squareSize, 2*squareSize);
old = (HBITMAP)SelectObject(memDC, bmp);
SetRect(&rc, 0,0, squareSize*2, squareSize*2);
FillRect(memDC, &rc, br1);
// top right
SetRect(&r1, squareSize, 0, 2*squareSize, squareSize);
FillRect(memDC, &r1, br2);
// bot left
SetRect(&r2, 0, squareSize, squareSize, 2*squareSize);
FillRect(memDC, &r2, br2);
SelectObject(memDC, old);
DeleteObject(br1);
DeleteObject(br2);
ReleaseDC(0, tmpDC);
DeleteDC(memDC);
result = CreatePatternBrush(bmp);
DeleteObject(bmp);
return result;
}
Example of result, created with:
HBRUSH bkBrush = makeCheckerBrush(8, RGB(153,153,153), RGB(102,102,102));

Get Pixel color fastest way?

I'm trying to make an auto-cliker for an windows app. It works well, but it's incredibly slow!
I'm currently using the method "getPixel" which reloads an array everytime it's called.
Here is my current code:
hdc = GetDC(HWND_DESKTOP);
bx = GetSystemMetrics(SM_CXSCREEN);
by = GetSystemMetrics(SM_CYSCREEN);
start_bx = (bx/2) - (MAX_WIDTH/2);
start_by = (by/2) - (MAX_HEIGHT/2);
end_bx = (bx/2) + (MAX_WIDTH/2);
end_by = (by/2) + (MAX_HEIGHT/2);
for(y=start_by; y<end_by; y+=10)
{
for(x=start_bx; x<end_bx; x+=10)
{
pixel = GetPixel(*hdc, x, y);
if(pixel==RGB(255, 0, 0))
{
SetCursorPos(x,y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(50);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Sleep(25);
}
}
}
So basically, it just scan a range of pixel in the screen and starts a mouse event if it detects a red button.
I know there are other ways to get the pixel color, such as bitblt. But I've made some researches, and I don't understand how I'm supposed to do, in order to scan a color array. I need something which scans screen very fast in order to catch the button.
Could you please help me?
Thanks.
I found a perfect way which is clearly faster than the GetPixel one:
HDC hdc, hdcTemp;
RECT rect;
BYTE* bitPointer;
int x, y;
int red, green, blue, alpha;
while(true)
{
hdc = GetDC(HWND_DESKTOP);
GetWindowRect(hWND_Desktop, &rect);
int MAX_WIDTH = rect.right;
int MAX_HEIGHT = rect.bottom;
hdcTemp = CreateCompatibleDC(hdc);
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = MAX_WIDTH;
bitmap.bmiHeader.biHeight = MAX_HEIGHT;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 32;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = MAX_WIDTH * 4 * MAX_HEIGHT;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
SelectObject(hdcTemp, hBitmap2);
BitBlt(hdcTemp, 0, 0, MAX_WIDTH, MAX_HEIGHT, hdc, 0, 0, SRCCOPY);
for (int i=0; i<(MAX_WIDTH * 4 * MAX_HEIGHT); i+=4)
{
red = (int)bitPointer[i];
green = (int)bitPointer[i+1];
blue = (int)bitPointer[i+2];
alpha = (int)bitPointer[i+3];
x = i / (4 * MAX_HEIGHT);
y = i / (4 * MAX_WIDTH);
if (red == 255 && green == 0 && blue == 0)
{
SetCursorPos(x,y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(50);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Sleep(25);
}
}
}
I hope this could help someone else.
The simple answer is that if this is the method you insist on using then there isn't much to optimize. As others have pointed out in comments, you should probably use a different method for locating the area to click. Have a look at using FindWindow, for example.
If you don't want to change your method, then at least sleep your thread for a bit after each complete screen scan.