Drawing a semi-transparent rectangle in a transparent window - c++

I found this code in stackoverflow and it really draws a rectangle on a transparent window using a bitmap. But somehow I cannot change the transparency of the rectangle. More precisely, I can, but it gets darker. As if the bitmap itself has a black background. How do I make the rectangle transparent?
void paintRect(HDC hdc, RECT dim, COLORREF penCol, COLORREF brushCol, int opacity)
{
HDC tempHDC = CreateCompatibleDC(hdc);
BITMAPINFO bitmapInfo;
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = dim.right - dim.left;
bitmapInfo.bmiHeader.biHeight = dim.bottom - dim.top;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = (dim.right - dim.left) * (dim.bottom - dim.top) * 4;
HBITMAP hBitmap = CreateDIBSection(tempHDC, &bitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0x0);
SelectObject(tempHDC, hBitmap);
SetDCPenColor(tempHDC, RGB(0, 0, 255));
SetDCBrushColor(tempHDC, RGB(0, 0, 255));
FillRect(tempHDC, &dim, CreateSolidBrush(RGB(0, 0, 255)));
BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, 0 };
AlphaBlend(hdc, dim.left, dim.top, dim.right, dim.bottom, tempHDC, dim.left, dim.top, dim.right, dim.bottom, blend);
}
Full code
#include <Windows.h>
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);
LRESULT CALLBACK windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
WNDCLASS windowClass {};
windowClass.lpfnWndProc = windowProc;
windowClass.hInstance = hInstance;
windowClass.lpszClassName = L"Keystrokes";
windowClass.style = CS_NOCLOSE;
windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClass(&windowClass))
{
return 0;
}
HWND hwnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, L"Keystrokes", L"Keystrokes", WS_POPUP, 0, 0, 148, 140, 0, 0, hInstance, 0);
if (!hwnd)
{
return 0;
}
SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY);
ShowWindow(hwnd, nCmdShow);
MSG msg {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
void paintRect(HDC hdc, RECT dim, COLORREF penCol, COLORREF brushCol, int opacity)
{
HDC tempHDC = CreateCompatibleDC(hdc);
BITMAPINFO bitmapInfo;
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = dim.right - dim.left;
bitmapInfo.bmiHeader.biHeight = dim.bottom - dim.top;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = (dim.right - dim.left) * (dim.bottom - dim.top) * 4;
HBITMAP hBitmap = CreateDIBSection(tempHDC, &bitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0x0);
SelectObject(tempHDC, hBitmap);
SetDCPenColor(tempHDC, RGB(0, 0, 255));
SetDCBrushColor(tempHDC, RGB(0, 0, 255));
FillRect(tempHDC, &dim, CreateSolidBrush(RGB(0, 0, 255)));
BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, 0 };
AlphaBlend(hdc, dim.left, dim.top, dim.right, dim.bottom, tempHDC, dim.left, dim.top, dim.right, dim.bottom, blend);
}
LRESULT CALLBACK windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
{
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
break;
case WM_MBUTTONDOWN:
{
PostQuitMessage(0);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hwnd, &ps);
paintRect(hDC, { 0, 0, 48, 48 }, RGB(255, 0, 0), RGB(255, 255, 255), 255);
EndPaint(hwnd, &ps);
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

“As if the bitmap itself has a black background.”
Because your background is set to:
windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
Of course you can set it to NULL_BRUSH to make it look transparent:
windowClass.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
But when you move it, you will find that this is not to make the window transparent, but to stop drawing the background:
As #Ben answers here:
You haven't made the window transparent, you have just stopped drawing the background. What you see as the background is just whatever happened to be underneath the window when it was first drawn.
And you need to refer to Layered Windows
You can't add rectangles directly on the window, you should use a layered window, and then add what you need.
Here is a sample that you can refer to it:
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR szCmdLine, _In_ int iCmdShow)
{
static TCHAR szAppName[] = TEXT("test window");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
}
hwnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, szAppName,
TEXT("the hello program"),
WS_POPUP,
CW_USEDEFAULT,
CW_USEDEFAULT,
148,
148,
NULL,
NULL,
hInstance,
NULL);
SetLayeredWindowAttributes(hwnd, 0, 1, LWA_ALPHA);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
VOID FadeRect(RECT* prc, HDC hdc)
{
BOOL fFade = FALSE;
static HWND hwnd;
SIZE size;
POINT ptSrc = { 0, 0 };
BLENDFUNCTION blend;
SystemParametersInfo(SPI_GETSELECTIONFADE, 0, &fFade, 0);
if (!fFade)
return;
if (!hwnd) hwnd = CreateWindowEx(WS_EX_LAYERED |
WS_EX_TRANSPARENT |
WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
L"static", L"static", WS_POPUP | WS_VISIBLE, prc->left,
prc->top, prc->right, prc->bottom, NULL, (HMENU)0, NULL, NULL);
else MoveWindow(hwnd, prc->left, prc->top, prc->right, prc->bottom, TRUE);
RECT rect{ prc->left,prc->top,prc->right,prc->bottom };
size.cx = prc->right - prc->left;
size.cy = prc->bottom - prc->top;
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.AlphaFormat = 0;
blend.SourceConstantAlpha = 150;
UpdateLayeredWindow(hwnd, NULL, NULL, &size, hdc, &ptSrc, 0,
&blend, ULW_ALPHA);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
{
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case WM_MBUTTONDOWN:
{
PostQuitMessage(0);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
RECT rect;
GetClientRect(hwnd, &rect);
MapWindowPoints(hwnd, GetParent(hwnd), (LPPOINT)&rect, 2);
HDC hDC = BeginPaint(hwnd, &ps);
RECT rc{ rect.left,rect.top,rect.left + 48,rect.top + 48 };
FadeRect(&rc, hDC);
EndPaint(hwnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

Related

How to correctly draw simple non-client area (4 px red border)?

I'm trying to draw a custom painted non-client area, instead of default theme border (Windows 10).
I handled WM_NCCALCSIZE to resize the non-client area to 4 pixels on each side and then handled WM_NCPAINT to draw the red border.
My custom painting succeeds when application is first displayed, but fails to redraw either when application is resized, or when minimized and restored, despite the fact that both WM_NCCALCSIZE and WM_NCPAINT are called during resizing or when window is restored.
#pragma comment(lib, "UxTheme")
#include <windows.h>
#include <uxtheme.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = (HICON) LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(0,128,0));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "window";
wcex.hIconSm = NULL;
RegisterClassEx(&wcex);
HWND hWnd = CreateWindowEx(
WS_EX_COMPOSITED,
"window",
NULL,
WS_OVERLAPPEDWINDOW,
100,
100,
600,
400,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hWnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CREATE:
SetWindowTheme(hWnd, L"", L"");
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_NCCALCSIZE:
{
RECT rect;
GetWindowRect(hWnd, &rect);
LPNCCALCSIZE_PARAMS ncParams = (LPNCCALCSIZE_PARAMS) lParam;
ncParams->rgrc[0].top = rect.top + 4;
ncParams->rgrc[0].left = rect.left + 4;
ncParams->rgrc[0].bottom = rect.bottom - 4;
ncParams->rgrc[0].right = rect.right - 4;
return 0;
}
case WM_NCPAINT:
{
RECT rect;
GetWindowRect(hWnd, &rect);
HDC dc = GetDCEx(hWnd, (HRGN) wParam, DCX_WINDOW | DCX_CACHE | DCX_INTERSECTRGN | DCX_LOCKWINDOWUPDATE);
HPEN pen = CreatePen(PS_INSIDEFRAME, 4, RGB(255, 0, 0));
HGDIOBJ old = SelectObject(dc, pen);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
Rectangle(dc, 0, 0, width, height);
SelectObject(dc, old);
DeleteObject(pen);
ReleaseDC(hWnd, dc);
return 0;
}
case WM_NCACTIVATE:
RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW);
return 0;
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
The wParam of WM_NCPAINT message sometimes returns 1 instead of a handle to a region (HRGN). In that case HRGN must be created using CreateRectRgn function.
#pragma comment(lib, "UxTheme")
#include <windows.h>
#include <uxtheme.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = (HICON) LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(0,128,0));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "window";
wcex.hIconSm = NULL;
RegisterClassEx(&wcex);
HWND hWnd = CreateWindowEx(
NULL,
"window",
NULL,
WS_OVERLAPPEDWINDOW,
100,
100,
600,
400,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hWnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CREATE:
SetWindowTheme(hWnd, L"", L"");
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_NCCALCSIZE:
{
LPNCCALCSIZE_PARAMS ncParams = (LPNCCALCSIZE_PARAMS) lParam;
ncParams->rgrc[0].top += 4;
ncParams->rgrc[0].left += 4;
ncParams->rgrc[0].bottom -= 4;
ncParams->rgrc[0].right -= 4;
return 0;
}
case WM_NCPAINT:
{
RECT rect;
GetWindowRect(hWnd, &rect);
HRGN region = NULL;
if (wParam == NULLREGION) {
region = CreateRectRgn(rect.left, rect.top, rect.right, rect.bottom);
} else {
HRGN copy = CreateRectRgn(0, 0, 0, 0);
if (CombineRgn(copy, (HRGN) wParam, NULL, RGN_COPY)) {
region = copy;
} else {
DeleteObject(copy);
}
}
HDC dc = GetDCEx(hWnd, region, DCX_WINDOW | DCX_CACHE | DCX_INTERSECTRGN | DCX_LOCKWINDOWUPDATE);
if (!dc && region) {
DeleteObject(region);
}
HPEN pen = CreatePen(PS_INSIDEFRAME, 4, RGB(255, 0, 0));
HGDIOBJ old = SelectObject(dc, pen);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
Rectangle(dc, 0, 0, width, height);
SelectObject(dc, old);
ReleaseDC(hWnd, dc);
DeleteObject(pen);
return 0;
}
case WM_NCACTIVATE:
RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW);
return 0;
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

Radio button text doesn't get displayed properly when setting it's font to an italic font

When setting the font of a radio button to an italic font, the radio button text doesn't get displayed all the way. It gets clipped on the right side. It only happens when visual styles are on. I am testing this on Windows XP. How do you fix this?
#include <Windows.h>
#include <Commctrl.h>
#pragma comment(linker, \
"\"/manifestdependency:type='Win32' "\
"name='Microsoft.Windows.Common-Controls' "\
"version='6.0.0.0' "\
"processorArchitecture='*' "\
"publicKeyToken='6595b64144ccf1df' "\
"language='*'\"")
#pragma comment(lib, "Comctl32.lib")
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HFONT hFont = 0;
static HWND hBtn = 0;
switch(msg)
{
case WM_CREATE:
{
HDC hdc = GetDC(hwnd);
int nHeight = -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72);
hFont = CreateFont(nHeight, 0, 0, 0, FW_NORMAL, TRUE, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, L"Microsoft Sans Serif");
ReleaseDC(hwnd, hdc);
hBtn = CreateWindowEx(0, L"Button", L"Hello", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON,
20, 20, 220, 20, hwnd, 0, GetModuleHandle(0), 0);
SendMessage(hBtn, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(FALSE, 0));
}
break;
case WM_PAINT: //"Hello" gets displayed properly with WM_PAINT but not in radio button
{
HDC hdc;
PAINTSTRUCT ps;
HFONT hOldFont;
hdc = BeginPaint(hwnd, &ps);
hOldFont = (HFONT)SelectObject(hdc, hFont);
TextOut(hdc, 20, 80, L"Hello", 5);
SelectObject(hdc, hOldFont);
EndPaint(hwnd, &ps);
}
break;
case WM_DESTROY:
DeleteObject(hFont);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc = {0};
HWND hwnd;
MSG msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = L"MainClass";
RegisterClassEx(&wc);
InitCommonControls();
hwnd = CreateWindowEx(0, L"MainClass", L"Hello", WS_OVERLAPPEDWINDOW, 140, 140, 400, 200, 0, 0, hInstance, 0);
ShowWindow(hwnd, nCmdShow);
while(GetMessage(&msg, 0, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}

Resize window to fit content after WM_PAINT

is there a way to resize after all WM_PAINT calls are done? so that i can automatically resize my window to fit whatever i have painted, since initially my window size is defined at a certain size, there may be leftover spaces that are not painted, how can i remove these leftover spaces?
As you can see from the screenshot below the white spaces are the leftover spaces.
#include <windows.h>
#include "resource.h"
#include "loadFromTextFile.h"
const char g_szClassName[] = "myWindowClass";
const int recLength = 3;
const int wndHeight = 700;
const int wndWidth = 700;
PAINTSTRUCT ps;
HDC hdc;
HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
HBRUSH redBrush = CreateSolidBrush(RGB(255, 0, 0));
HPEN myPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
//load from text
string FILE_PATH = ("10x10.txt");
loadFromTextFile loaded = loadFromTextFile(FILE_PATH);
int mazeWidth = loaded.getColumns() * recLength;
int mazeHeight = loaded.getRows() * recLength;
void paintMaze(HWND hwnd, vector<vector<string> > grid){
hdc = ::BeginPaint(hwnd, &ps);
SelectObject(hdc, myPen);
for (int r = 0; r < loaded.getRows(); r++){
for (int c = 0; c < loaded.getColumns(); c++){
RECT rect;
rect.left = c*recLength;//x upper left
rect.top = r*recLength;//y upper left
rect.right = (c + 1)*recLength;//x lower right corner
rect.bottom = (r + 1)*recLength;//y lower right corner
if (grid[r][c] == "1"){//1 path
FillRect(hdc, &rect, blackBrush);
}
else if (grid[r][c] == "0"){//wall
FillRect(hdc, &rect, whiteBrush);
}
if (r == 50 && c == 50){
FillRect(hdc, &rect, redBrush);
}
}
}
DeleteObject(blackBrush);
DeleteObject(whiteBrush);
DeleteObject(redBrush);
DeleteObject(myPen);
EndPaint(hwnd, &ps);
}
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
case ID_ABOUT:
SetWindowPos(hwnd, 0, 0, 0, mazeWidth, mazeHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
}
break;
case WM_PAINT:
{
paintMaze(hwnd, loaded.getNodeGrid());
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);;
wc.lpszClassName = g_szClassName;
wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 32, 32, 0);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
" Maze",
WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU,//prevent resize
CW_USEDEFAULT, CW_USEDEFAULT, wndWidth, wndHeight,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
Paint in an offscreen buffer.
This allows you to resize the window.
Paint the window by copying the buffer.

Transparency in GDI DCs

I have the "simple" goal of drawing a bitmap with some transparency around it on the screen. That bit wasn't so hard:
#include <windows.h>
#include "BBKG.h"
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static int wH = 156;
static int wW = 166;
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
HDC mem0, mem1;
HBITMAP hbmMask;
BITMAP bm;
GetObject(hbmColour, sizeof(BITMAP), &bm);
hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
mem0 = CreateCompatibleDC(0);
mem1 = CreateCompatibleDC(0);
SelectObject(mem0, hbmColour);
SelectObject(mem1, hbmMask);
SetBkColor(mem0, crTransparent);
BitBlt(mem1, 0, 0, bm.bmWidth, bm.bmHeight, mem0, 0, 0, SRCCOPY);
BitBlt(mem0, 0, 0, bm.bmWidth, bm.bmHeight, mem1, 0, 0, SRCINVERT);
DeleteDC(mem0);
DeleteDC(mem1);
return hbmMask;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
hInst = hInstance;
MSG msg;
HWND hwnd;
WNDCLASSW wc;
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.lpszClassName = L"nope";
wc.hInstance = hInst;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassW(&wc);
hwnd = CreateWindowW(wc.lpszClassName, L"",
WS_VISIBLE | WS_POPUP| WS_EX_TRANSPARENT,
100, 100, wW, wH, NULL, NULL, hInst, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
//Workaround for focusables stealing my Esc key
if (msg.message == WM_KEYDOWN){
if (msg.wParam == VK_ESCAPE) {
SendMessage(hwnd, WM_CLOSE, 0, 0);
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
static int px;
static int py;
static HBITMAP bhbm;
static RECT nRect = { 0, 0, wW, wH };
switch (msg)
{
case WM_CREATE:
{
HWND bb = CreateWindowW(L"STATIC", L"",
WS_VISIBLE | WS_CHILD ,
0, 0, wW, wH,
hwnd, (HMENU)11, hInst, NULL);
//SetTimer(hwnd, 1, 80, NULL);
return 0;
}
case WM_PAINT: {
//Vars
RECT wRect;
if (GetUpdateRect(hwnd, &wRect, FALSE) == 0) {
return 0; //Nothing to paint
}
PAINTSTRUCT gps;
PAINTSTRUCT ps;
BeginPaint(hwnd, &gps);
HWND bb = GetDlgItem(hwnd, 11);
HDC bbhdc = BeginPaint(bb, &ps);
HDC mdc = CreateCompatibleDC(bbhdc);
//Load Image
BITMAP pBM;
HBITMAP pHBM = (HBITMAP)LoadImage(NULL, L"twi00.bmp", 0, 0, 0, LR_LOADFROMFILE);
HBITMAP pMBM = CreateBitmapMask((HBITMAP)pHBM, 0x00000000);
GetObject(pHBM, sizeof(pBM), &pBM);
//Paint
HBITMAP oldBM = (HBITMAP)SelectObject(mdc, pMBM);
BitBlt(bbhdc, 0, 0, pBM.bmWidth, pBM.bmHeight, mdc, 0, 0, SRCAND);
SelectObject(mdc, pHBM);
BitBlt(bbhdc, 0, 0, pBM.bmWidth, pBM.bmHeight, mdc, 0, 0, SRCPAINT);
//Cleanup
SelectObject(mdc, oldBM);
DeleteObject(pHBM);
DeleteObject(pMBM);
DeleteDC(mdc);
EndPaint(bb, &ps);
EndPaint(hwnd, &gps);
return 1;
}
case WM_ERASEBKGND: {
return 0;
}
case WM_DESTROY:
{
DeleteObject(bhbm);
PostQuitMessage(0);
return 0;
}
case WM_LBUTTONDOWN:
SetCapture(hwnd);
px = LOWORD(lParam);
py = HIWORD(lParam);
return 1;
case WM_LBUTTONUP:
{
ReleaseCapture();
return 1;
}
case WM_MOUSEMOVE:
{
if (GetCapture() == hwnd)
{
RECT rcWindow;
GetWindowRect(hwnd, &rcWindow);
SetWindowPos(hwnd, NULL, rcWindow.left + LOWORD(lParam) - px, rcWindow.top + HIWORD(lParam) - py, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
break;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
Use any generic bmp with a black border will do, I used this:
Now the question is, how can I make it so that when I move the window (click/drag) the background updates? I was hoping for something like putting the bitmap into a transparent window so that it's overlayed on top of things but it seems to just grab the pixels of what ever is behind it.
I'm attempting to do this without GDI+ or other libraries, if possible.
CreateWindow() does not accept extended window styles, such as WS_EX_TRANSPARENT (which is why it has EX in its name). You have to use CreateWindowEx() instead:
hwnd = CreateWindowExW(WS_EX_TRANSPARENT,
wc.lpszClassName, L"",
WS_VISIBLE | WS_POPUP,
100, 100, wW, wH, NULL, NULL, hInst, NULL);
A better option is to create a layered window (see also this) by using the WS_EX_LAYERED extended style). Then you can use the UpdateLayeredWindow() function to provide the window with the bitmap and the transparent color (you can also specify alpha as well). Let the window manage all of the hard work of drawing the bitmap transparently for you.
Your WndProc() can also respond to the WM_NCHITTEST message to tell the OS that all clicks on the window should be treated as if the user were clicking on the window's titlebar. Let the window handle the mouse tracking and auto-positioning for you.
Try something more like this:
#include <windows.h>
HINSTANCE hInst;
static int wH = 156;
static int wW = 166;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
hInst = hInstance;
WNDCLASSW wc = {0};
wc.lpszClassName = L"nope";
wc.hInstance = hInst;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassW(&wc);
HWND hwnd = CreateWindowEx(WS_EX_LAYERED,
wc.lpszClassName, L"",
WS_POPUP, 100, 100, wW, wH, NULL, NULL,
hInst, NULL);
HBITMAP hBmp = (HBITMAP) LoadImage(NULL, L"twi00.bmp", 0, 0, 0, LR_LOADFROMFILE);
HDC hdcScreen = GetDC(0);
HDC hdcBmp = CreateCompatibleDC(hdcScreen);
HBITMAP oldBM = (HBITMAP) SelectObject(hdcBmp, hBmp);
POINT pt = {0};
UpdateLayeredWindow(hwnd,
hdcScreen,
NULL, NULL,
hdcBmp, &pt,
RGB(0, 0, 0), // black
NULL, ULW_COLORKEY
);
SelectObject(hdcBmp, oldBM);
DeleteDC(hdcBmp);
ReleaseDC(0, hdcScreen);
DeleteObject(hBmp);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
//Workaround for focusables stealing my Esc key
if ((msg.message == WM_KEYDOWN) && (msg.wParam == VK_ESCAPE) {
SendMessage(hwnd, WM_CLOSE, 0, 0);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_NCHITTEST:
{
return HTCAPTION;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}

GDI sprite shown some time and disappear

I'm studying sprite drawing with GDI in Visual Studio 2013 with C++.
I can draw sprite using two bmp files, image with black background, and black mask. But after program start, sprite shown some time (about 15 second) and disappear. I can't find the reason.
Here is my code.
#include <Windows.h>
#include "resource.h"
using namespace std;
LRESULT WINAPI WinProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
HWND hWnd = 0;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
ZeroMemory(&msg, sizeof(msg));
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
wc.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_HAND));
wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ASTERISK));
wc.hInstance = hInst;
wc.lpfnWndProc = WinProc;
wc.lpszClassName = "ClassName";
wc.lpszMenuName = 0;
wc.style = CS_VREDRAW | CS_HREDRAW;
if (!RegisterClass(&wc))
MessageBox(0, "Class was not registered!", "Error", 0);
hWnd=CreateWindow("ClassName", "WindowName", WS_OVERLAPPEDWINDOW, 100, 100, 640, 480, 0, 0, hInst, 0);
if (hWnd==0)
MessageBox(0, "Window was not created!", "Error", 0);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while (msg.message!=WM_QUIT)
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
HDC hdc = GetDC(hWnd);
HDC hBuffDC = CreateCompatibleDC(hdc);
HDC hMemDC = CreateCompatibleDC(hdc);
HBITMAP hBuffBitmap = CreateCompatibleBitmap(hdc, 640, 480);
HBITMAP hSpriteBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_MASK));
SelectObject(hBuffDC, hBuffBitmap);
SelectObject(hBuffDC, (HBRUSH)GetStockObject(GRAY_BRUSH));
Rectangle(hBuffDC, 0, 0, 640, 480);
SelectObject(hMemDC, hSpriteBitmap);
BitBlt(hBuffDC, 0, 0, 640, 480, hMemDC, 0, 0, SRCAND);
hSpriteBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SPRITE));
SelectObject(hMemDC, hSpriteBitmap);
BitBlt(hBuffDC, 0, 0, 640, 480, hMemDC, 0, 0, SRCPAINT);
BitBlt(hdc, 0, 0, 640, 480, hBuffDC, 0, 0, SRCCOPY);
DeleteObject(hBuffBitmap);
DeleteObject(hSpriteBitmap);
DeleteDC(hMemDC);
DeleteDC(hBuffDC);
ReleaseDC(hWnd, hdc);
}
return 0;
}
LRESULT WINAPI WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_KEYDOWN:
switch (wParam)
{
case VK_ESCAPE:
DestroyWindow(hWnd);
break;
}
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
images are just a black circle with white background (mask), and green circle with black background.
//Here is corrected code without memory leak and ReleaseDc return 1
#include <Windows.h>
#include "resource.h"
using namespace std;
LRESULT WINAPI WinProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
HWND hWnd = 0;
HDC hdc = 0;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
ZeroMemory(&msg, sizeof(msg));
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_HAND));
wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ASTERISK));
wc.hInstance = hInst;
wc.lpfnWndProc = WinProc;
wc.lpszClassName = "ClassName";
wc.lpszMenuName = 0;
wc.style = CS_VREDRAW | CS_HREDRAW;
if (!RegisterClass(&wc))
MessageBox(0, "Class was not registered!", "Error", 0);
hWnd=CreateWindow("ClassName", "WindowName", WS_OVERLAPPEDWINDOW, 100, 100, 640, 480, 0, 0, hInst, 0);
if (hWnd==0)
MessageBox(0, "Window was not created!", "Error", 0);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
hdc = GetDC(hWnd);
while (msg.message!=WM_QUIT)
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
HDC hBuffDC = CreateCompatibleDC(hdc);
HDC hMemDC = CreateCompatibleDC(hdc);
HBITMAP hBuffBitmap = CreateCompatibleBitmap(hdc, 640, 480);
HBITMAP hSpriteBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SPRITE));
HBITMAP hMaskBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_MASK));
HBITMAP hOldBuffBitmap= (HBITMAP)SelectObject(hBuffDC, hBuffBitmap);
HBRUSH hOldBrush= (HBRUSH)SelectObject(hBuffDC, (HBRUSH)GetStockObject(GRAY_BRUSH));
Rectangle(hBuffDC, 0, 0, 640, 480);
HBITMAP hOldSpriteBitmap= (HBITMAP)SelectObject(hMemDC, hMaskBitmap);
BitBlt(hBuffDC, 0, 0, 640, 480, hMemDC, 0, 0, SRCAND);
SelectObject(hMemDC, hSpriteBitmap);
BitBlt(hBuffDC, 0, 0, 640, 480, hMemDC, 0, 0, SRCPAINT);
BitBlt(hdc, 0, 0, 640, 480, hBuffDC, 0, 0, SRCCOPY);
SelectObject(hBuffDC, hOldBuffBitmap);
SelectObject(hMemDC, hOldSpriteBitmap);
SelectObject(hBuffDC, hOldBrush);
DeleteObject(hBuffBitmap);
DeleteObject(hSpriteBitmap);
DeleteObject(hMaskBitmap);
DeleteDC(hMemDC);
DeleteDC(hBuffDC);
}
return 0;
}
LRESULT WINAPI WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_KEYDOWN:
switch (wParam)
{
case VK_ESCAPE:
DestroyWindow(hWnd);
break;
}
return 0;
case WM_CLOSE:
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
int Res = ReleaseDC(hWnd, hdc); //Release main window device context before destroying window
DWORD err = GetLastError();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}