How to make a fade effect in WINAPI GDI? - c

Below I have a code snippet showing what I have tried.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#define WC_MAIN "MainClass"
#define WC_NUMBER "NumberClass"
LRESULT CALLBACK NumberProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void InitClasses(void)
{
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpfnWndProc = MainWndProc;
wc.lpszClassName = WC_MAIN;
RegisterClass(&wc);
wc.lpfnWndProc = NumberProc;
wc.lpszClassName = WC_NUMBER;
RegisterClass(&wc);
}
#define NUMBER_SPEED 2
#define NUMBER_TICK_SPEED 25
#define NUMBER_TICKS 55
typedef struct {
UINT ticks;
HBITMAP buffer;
} NUMBERINFO;
HWND CreateNumber(HWND parent, const char *text, int x, int y, COLORREF color)
{
NUMBERINFO *ni = malloc(sizeof(*ni));
HDC hdc = GetDC(NULL);
HFONT oldFont = NULL;
SIZE s;
GetTextExtentPoint32(hdc, text, strlen(text), &s);
SelectObject(hdc, oldFont);
BITMAPINFO bmi;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = s.cx;
bmi.bmiHeader.biHeight = s.cy;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; // four 8-bit components
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = s.cx * s.cy * 4;
COLORREF *pvBits;
HBITMAP buffer = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &pvBits, NULL, 0);
HDC bufferDc = CreateCompatibleDC(hdc);
HBITMAP oldBmp = SelectObject(bufferDc, buffer);
oldFont = NULL;
SetTextAlign(bufferDc, TA_TOP | TA_LEFT);
SetBkMode(bufferDc, TRANSPARENT);
SetTextColor(bufferDc, color);
TextOut(bufferDc, 0, 0, text, strlen(text));
SelectObject(bufferDc, oldFont);
SelectObject(bufferDc, oldBmp);
DeleteDC(bufferDc);
ReleaseDC(NULL, hdc);
ni->buffer = buffer;
return CreateWindow(WC_NUMBER, text, WS_VISIBLE | WS_CHILD, x - (s.cx >> 1), y - (s.cy >> 1), s.cx, s.cy, parent, NULL, NULL, ni);
}
LRESULT CALLBACK NumberProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
NUMBERINFO *info = (NUMBERINFO*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
switch(msg)
{
case WM_CREATE:
info = ((CREATESTRUCT*) lParam)->lpCreateParams;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) info);
SetTimer(hWnd, 0, NUMBER_TICK_SPEED, NULL);
info->ticks = NUMBER_TICKS;
return 0;
case WM_DESTROY: return 0;
case WM_TIMER:
{
RECT rc;
GetWindowRect(hWnd, &rc);
HWND parent = GetParent(hWnd);
MapWindowPoints(HWND_DESKTOP, parent, (POINT*) &rc, 2);
rc.top -= NUMBER_SPEED;
if(!--info->ticks)
{
DestroyWindow(hWnd);
return 0;
}
SetWindowPos(hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top - NUMBER_SPEED, SWP_NOREDRAW | SWP_NOCOPYBITS);
// redraw parent call erases last shown number
RedrawWindow(GetParent(hWnd), &rc, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
return 0;
}
case WM_ERASEBKGND: return 1;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT r;
GetClientRect(hWnd, &r);
HDC bufferDc = CreateCompatibleDC(hdc);
HBITMAP oldBmp = SelectObject(bufferDc, info->buffer);
BLENDFUNCTION bfn;
bfn.BlendOp = AC_SRC_OVER;
bfn.BlendFlags = 0;
bfn.SourceConstantAlpha = info->ticks * 0xFF / NUMBER_TICKS;
bfn.AlphaFormat = 0;
//TransparentBlt(hdc, 0, 0, r.right, r.bottom, bufferDc, 0, 0, r.right, r.bottom, 0);
AlphaBlend(hdc, 0, 0, r.right, r.bottom, bufferDc, 0, 0, r.right, r.bottom, bfn);
SelectObject(bufferDc, oldBmp);
DeleteDC(bufferDc);
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY: PostQuitMessage(0); return 0;
case WM_ERASEBKGND: return 1;
case WM_PAINT:
{
InvalidateRect(hWnd, NULL, FALSE);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// draw gradient (red to green)
RECT r;
GetClientRect(hWnd, &r);
GRADIENT_RECT gr;
gr.UpperLeft = 0;
gr.LowerRight = 1;
TRIVERTEX pVertex[2] = {
{ 0, 0, 0xFF00, 0x0000, 0x0000, 0xFF00 },
{ r.right, r.bottom, 0x0000, 0xFF00, 0x0000, 0xFF00 }
};
GradientFill(hdc, pVertex, 2, &gr, 2, GRADIENT_FILL_RECT_H);
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int main()
{
InitClasses();
HWND mainWindow = CreateWindow(WC_MAIN, "Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL);
ShowWindow(mainWindow, 1);
UpdateWindow(mainWindow);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// adding a number at mouse position if pressed (testing)
if(msg.message == WM_LBUTTONDOWN)
{
RECT r;
GetClientRect(mainWindow, &r);
int x = GET_X_LPARAM(msg.lParam);
int y = GET_Y_LPARAM(msg.lParam);
CreateNumber(mainWindow, "123", x, y, 0xFFFF00);
}
}
return 0;
}
This draws some text with any color and every time a timer ticks, the text becomes more faded (more transparent), but the black background of the bitmap the text was drawn on, is drawn, but I want no background to be present, just the text.
I would assume I need a combination of TransparentBlt and AlphaBlend.
How would I go on to solve this?

The solution was quite simple. It was to use AC_SRC_ALPHA for the AlphaFormat member of BLENDFUNCTION and in order for that to work, set the alpha values of the HBITMAP, the buffer.
After the buffer (the one inside of NUMBERINFO) is created (in CreateNumber; check question code for reference), one must loop over all the colors and set the alpha value to 255 where needed.
for(int i = s.cx * s.cy; i--; pvBits++)
{
if(*pvBits)
*pvBits |= 0xFF000000; // make non black pixels opaque
}
And setting the flag in WM_PAINT: bfn.AlphaFormat = AC_SRC_ALPHA;

Related

Why can't I replace BeginPaint() with ValidateRect()?

From my understanding, BeginPaint and EndPaint helps paint the screen and validates the rectangle that it paints, this also prevents WM_PAINT messages from being spammed since there will be no more invalid regions in the window.
With my understanding, I thought that theoretically speaking, if I were to use BitBlt() to paint the window in WM_CREATE, then use ValidateRect to Validate the entire client region, the code would work. However, I tried it and it doesn't, suggesting that there could be something possibly wrong with my understanding, or code. Could someone please explain to me where I went wrong?
Here is the code that I used, IDB_BITMAP1 can be replaced with any appropriate Bitmap. Do note the comment in WM_PAINT to reproduce the issue. Thanks for reading!
#include <windows.h>
#include "resource.h"
HBITMAP hBitMap = NULL;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
BITMAP infoBM;
hBitMap = LoadBitmap(GetModuleHandle(NULL), IDB_BITMAP1);
if (hBitMap == NULL)
{
MessageBoxA(NULL, "COULD NOT LOAD CAT BITMAP", "ERROR", MB_OK);
}
HDC winDC = GetDC(hwnd);
HDC hMem = CreateCompatibleDC(winDC);
GetObject(hBitMap, sizeof(infoBM), &infoBM);
HBITMAP hMemOld = SelectObject(hMem, hBitMap);
BitBlt(winDC, 0, 0, infoBM.bmWidth, infoBM.bmHeight, hMem, 0, 0, SRCCOPY);
SelectObject(hMem, hMemOld);
DeleteDC(hMem);
ReleaseDC(hwnd,winDC);
break;
}
case WM_PAINT: //<<<--------- I replaced the entire segment here with ValidateRect(hwnd,NULL);break; And the whole window doesnt show anymore, why is that so?
{
BITMAP infoBM;
PAINTSTRUCT ps;
HDC winDC = BeginPaint(hwnd, &ps);
GetObject(hBitMap, sizeof(infoBM), &infoBM);
HDC hMem = CreateCompatibleDC(winDC);
HBITMAP hOldMem = SelectObject(hMem, hBitMap);
BitBlt(winDC, 0, 0, infoBM.bmWidth, infoBM.bmHeight, hMem, 0, 0, SRCCOPY);
SelectObject(hMem, hOldMem);
DeleteObject(hMem);
EndPaint(hwnd, &ps);
break;
}
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:return DefWindowProcA(hwnd, msg, wParam, lParam);
}
return 0;
}
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc = { sizeof(WNDCLASSEX),0,WindowProc,0,0,hInstance,NULL,LoadCursor(NULL,IDC_ARROW),(HBRUSH)(COLOR_WINDOW+1),NULL,"WindowClass",LoadIcon(NULL,IDI_APPLICATION)};
if (!RegisterClassEx(&wc))
{
MessageBoxA(NULL, "WINDOW REGISTRATION FAILED", "ERROR", MB_OK);
}
HWND hwnd = CreateWindowEx(WS_EX_LAYERED, "WindowClass", "Title", WS_POPUP, 200, 200, 300, 300, NULL, NULL, hInstance, NULL);
if (!hwnd)
{
MessageBoxA(NULL, "WINDOW CREATION FAILED", "ERROR", MB_OK);
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
if (!SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 255, LWA_ALPHA))
{
MessageBoxA(NULL, "WINDOW LAYERED ATTRIBUTES FAILED", "ERROR", MB_OK);
}
MSG msg;
while (GetMessage(&msg, hwnd, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Typically,
You use the GetDC function to carry out drawing that must occur
instantly rather than when a WM_PAINT message is
processing.
And if you'd like, you can ValidateRect the entire client area instead of real painting when a WM_PAINT message is processing.
The following code is adapted from the thread.
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int i = 0;
switch (message)
{
case WM_PAINT:
{
if (i++ < 100)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
COLORREF clrbak = SetBkColor(hdc, RGB(255, 0, 0));
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
int x = (rect.right - rect.left) / 8;
int y = (rect.bottom - rect.top) / 8;
rect.left += x; rect.right -= x;
rect.top += y; rect.bottom -= y;
SetBkColor(hdc, RGB(0, 255, 0));
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
rect.left += x; rect.right -= x;
rect.top += y; rect.bottom -= y;
SetBkColor(hdc, RGB(0, 0, 255));
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
rect.left += x; rect.right -= x;
rect.top += y; rect.bottom -= y;
SetBkColor(hdc, RGB(0, 0, 0));
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
SetBkColor(hdc, clrbak);
EndPaint(hWnd, &ps);
}
else
{
ValidateRect(hWnd,NULL);
}
}
break;
case WM_ERASEBKGND:
return 1;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

Great Performance Decreasement when Moving the GetDC code into a thread

For the next question of Strange Efficiency Decrease when Localizing a Global Variable into a Subthread, resently I found the question of the performance decreasement is actually caused by moving the GetDC code into a thread, where it was originally placed in a WindowProc/WndProc function, but I have no idea with the detail.
Could anyone help me?
After all, here is my code:
Quick one:
#include <stdio.h>
#include <math.h>
#include <windows.h>
#define num 4
#define width 1
double position[num][2] = {{733, 434}, {633, 384}, {733, 284}, {733, 684}};
double velocity[num][2] = {{-5, 2.5}, {5, -2.5}, {0, 2.5}, {10, -2.5}};
COLORREF color[num] = {0xffffff, 0x00ffff, 0x0000ff, 0xffff00};
COLORREF background_color = 0x000000;
double distance;
int count, i, j;
HDC hdc[num];
HPEN hpen[num];
inline double sqr(double x) {return x * x;}
DWORD WINAPI threadProc(LPVOID lpParamter) {
Sleep(1000);
while(1) {
for(i=0; i<num; ++i) {
LineTo(hdc[i], position[i][0], position[i][1]);
position[i][0] += velocity[i][0];
position[i][1] += velocity[i][1];
}
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR pCmdLine, int nCmdShow) {
const char *CLASS_NAME = "SUGEWND";
void *pHWND = &hInstance;
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0, CLASS_NAME, "SUGE",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if(hwnd == NULL) return 0;
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
MSG msg={};
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch(uMsg) {
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc2=BeginPaint(hwnd, &ps);
FillRect(hdc2, &ps.rcPaint, CreateSolidBrush(background_color));
EndPaint(hwnd, &ps);
return 0;
}
case WM_CREATE: {
for(i=0; i<num; ++i) {
hdc[i] = GetDC(hwnd);
hpen[i] = CreatePen(PS_SOLID, width, color[i]);
SelectObject(hdc[i], hpen[i]);
MoveToEx(hdc[i], position[i][0], position[i][1], 0);
}
HANDLE hThread = CreateThread(NULL, 0, threadProc, NULL, 0, NULL);
CloseHandle(hThread);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Slow one:
#include <stdio.h>
#include <math.h>
#include <windows.h>
#define num 4
#define width 1
double position[num][2] = {{733, 434}, {633, 384}, {733, 284}, {733, 684}};
double velocity[num][2] = {{-5, 2.5}, {5, -2.5}, {0, 2.5}, {10, -2.5}};
COLORREF color[num] = {0xffffff, 0x00ffff, 0x0000ff, 0xffff00};
COLORREF background_color = 0x000000;
double simulate_acc = 0.00001;
double distance;
int count, i, j;
HDC hdc[num];
HPEN hpen[num];
inline double sqr(double x) {return x * x;}
DWORD WINAPI threadProc(LPVOID lpParamter) {
HWND hwnd = *(HWND*)lpParamter;
for(i=0; i<num; ++i) {
hdc[i] = GetDC(hwnd);
hpen[i] = CreatePen(PS_SOLID, width, color[i]);
SelectObject(hdc[i], hpen[i]);
MoveToEx(hdc[i], position[i][0], position[i][1], 0);
}
Sleep(1000);
while(1) {
for(i=0; i<num; ++i) {
LineTo(hdc[i], position[i][0], position[i][1]);
position[i][0] += velocity[i][0];
position[i][1] += velocity[i][1];
}
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR pCmdLine, int nCmdShow) {
const char *CLASS_NAME = "SUGEWND";
void *pHWND = &hInstance;
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0, CLASS_NAME, "SUGE",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if(hwnd == NULL) return 0;
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
MSG msg={};
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch(uMsg) {
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc2=BeginPaint(hwnd, &ps);
FillRect(hdc2, &ps.rcPaint, CreateSolidBrush(background_color));
EndPaint(hwnd, &ps);
return 0;
}
case WM_CREATE: {
HANDLE hThread = CreateThread(NULL, 0, threadProc, &hwnd, 0, NULL);
CloseHandle(hThread);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
You are passing a pointer to a local variable to your threadProc. After exiting from WindowProc it's local variables are no more valid.
You can pass HWND by value:
case WM_CREATE: {
HANDLE hThread = CreateThread(NULL, 0, threadProc, (LPVOID)hwnd, 0, NULL);
CloseHandle(hThread);
return 0;
}
and read it's value so:
DWORD WINAPI threadProc(LPVOID lpParamter) {
HWND hwnd = (HWND)lpParamter;

Creating an HBITMAP from pixel buffer then rendering

Ok, so I literally just started using the WinAPI for drawing bitmap images. So if my code is absolute trash, I apologize. Anyways, how the heck am I supposed to do this? Essentially, I want to create an HBITMAP object from a simple unsigned char pixel buffer using CreateBitmap() like this...
HBITMAP hbm = CreateBitmap(width, height, 1, 24, buffer);
(obviously this is 3-bytes per pixel)
That appears to work fine until I try rendering it. For now, I just wanted to keep it simple and draw a 100 by 100 black pixel square on the screen but nothing shows up. I would appreciate it if anyone could point out the mistakes in my atrocious code.
This is my setup (I am aware a lot of things could be improved I just wanted to go as minimalistic as possible for now and actually have something appear on the screen):
HBITMAP hbm = NULL;
void window_init(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static unsigned char buffer[30000] = {0};
hbm = CreateBitmap(100, 100, 1, 24, buffer);
}
void window_draw(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = SelectObject(hdcMem, hbm);
BitBlt(hdc, 100, 100, 100, 100, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: window_init(hwnd, msg, wParam, lParam); break;
case WM_PAINT: window_draw(hwnd, msg, wParam, lParam); break;
case WM_CLOSE: DestroyWindow(hwnd); break;
case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Here's my entire code in case you want to copy it...
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct {
const char *wc_name;
const char *title;
unsigned int width;
unsigned int height;
} Window;
static HBITMAP hbmp = NULL;
static PAINTSTRUCT ps;
void window_create(Window *window, const char *wc_name, const char *title, unsigned int width, unsigned int height) {
window->wc_name = wc_name;
window->title = title;
window->width = width;
window->height = height;
}
void window_init(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static unsigned char buffer[30000] = {0};
hbmp = CreateBitmap(100, 100, 1, 24, buffer);
}
void window_draw(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = SelectObject(hdcMem, hbmp);
BitBlt(hdc, 100, 100, 100, 100, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);;
EndPaint(hwnd, &ps);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: window_init(hwnd, msg, wParam, lParam); break;
case WM_PAINT: window_draw(hwnd, msg, wParam, lParam); 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 nShowCmd) {
Window window;
window_create(&window, "Parent Window", "My Window", 1000, 1000 / 16 * 9);
WNDCLASSEX wc = {0};
MSG msg = {0};
HWND hwnd = NULL;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wc.lpszClassName = window.wc_name;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, "Failed to register application window!", "WC Registration Error!", MB_ICONEXCLAMATION | MB_OK);
return -1;
}
hwnd = CreateWindowEx(
0, window.wc_name, window.title,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
window.width, window.height, NULL, NULL, hInstance, NULL
);
if (!hwnd) {
MessageBox(NULL, "Failed to launch application window!", "HWND Creation Error!", MB_ICONEXCLAMATION | MB_OK);
return -1;
}
ShowWindow(hwnd, SW_MAXIMIZE);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
CreateBitmap produces a handle to a device dependent bitmap unlike CreateDIBSection which is guaranteed to create a device-independent bitmap with the device context if it succeeds, and allows you access to the actual bits in the bitmap, unlike CreateCompatibleBitmap.
How to use CreateDIBSection:
HDC hdc = GetDC(hwnd);
BITMAPINFO bi = { 0 };
static BYTE *bits = NULL;
static unsigned char buffer[30000] = { 0 };
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = 100;
bi.bmiHeader.biHeight = -100;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biCompression = BI_RGB;
hbmp = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
CopyMemory(bits, buffer, 30000);
ReleaseDC(hwnd, hdc);

The pen join style not applied on all corners of a shape

The shape is drawn with the Polyline() function.
The relevant code is here:
void DoDrawing(HWND hwnd) {
LOGBRUSH brush;
COLORREF col = RGB(0, 0, 0);
DWORD pen_style = PS_SOLID | PS_JOIN_MITER | PS_GEOMETRIC;
brush.lbStyle = BS_SOLID;
brush.lbColor = col;
brush.lbHatch = 0;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hPen1 = ExtCreatePen(pen_style, 8, &brush, 0, NULL);
HPEN holdPen = SelectObject(hdc, hPen1);
POINT points[5] = { { 10, 30 }, { 100, 30 }, { 100, 100 }, { 10, 100 }, {10, 30}};
Polyline(hdc, points, 5);
DeleteObject(hPen1);
SelectObject(hdc, holdPen);
EndPaint(hwnd, &ps);
}
The PS_JOIN_MITER is applied on three corners but not on the top-left corner. On that corner the default PS_JOIN_ROUND is used. How to fix this?
The following is a full working example:
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DoDrawing(HWND);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Pens";
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Line joins",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 250, 180, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 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_PAINT:
DoDrawing(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void DoDrawing(HWND hwnd) {
LOGBRUSH brush;
COLORREF col = RGB(0, 0, 0);
DWORD pen_style = PS_SOLID | PS_JOIN_MITER | PS_GEOMETRIC;
brush.lbStyle = BS_SOLID;
brush.lbColor = col;
brush.lbHatch = 0;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hPen1 = ExtCreatePen(pen_style, 8, &brush, 0, NULL);
HPEN holdPen = SelectObject(hdc, hPen1);
POINT points[5] = { { 10, 30 }, { 100, 30 }, { 100, 100 }, { 10, 100 }, {10, 30}};
Polyline(hdc, points, 5);
DeleteObject(hPen1);
SelectObject(hdc, holdPen);
EndPaint(hwnd, &ps);
}
Polyline does not join the first and last point.
Use Polygon(hdc, points, 5) instead of Polyline
Also, select the oldPen in to DC before deleting the existing pen, in this order:
SelectObject(hdc, holdPen);
DeleteObject(hPen1);
(although Windows will forgive you if you don't do it in the right order)

ANSI C & WinAPI: how to get a handle to the window from hook procedure?

I'm writing a very simple program, which prints color of selected pixel.
Here is my code:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define UNICODE
LRESULT CALLBACK mouse_hook_low_level(int nCode, WPARAM wParam, LPARAM lParam)
{
if(wParam == WM_MOUSEMOVE) {
//Need to get a handle to the window!
InvalidateRect(window, NULL, FALSE);
UpdateWindow(window);
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
LRESULT CALLBACK window_process(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC dc = GetDC(NULL);
HDC hdc;
HFONT font;
PAINTSTRUCT ps;
int r, g, b;
POINT p;
RECT background_rect, color_rect;
wchar_t buffer[256];
switch(message) {
case WM_PAINT:
GetCursorPos(&p);
r = GetRValue(GetPixel(dc, p.x, p.y));
g = GetGValue(GetPixel(dc, p.x, p.y));
b = GetBValue(GetPixel(dc, p.x, p.y));
hdc = BeginPaint(window, &ps);
background_rect.left = 0;
background_rect.right = 199;
background_rect.top = 0;
background_rect.bottom = 99;
FillRect(hdc, &background_rect, (HBRUSH)(COLOR_WINDOW+1));
font = CreateFont(
14,
0,
0,
0,
FW_DONTCARE,
FALSE,
FALSE,
FALSE,
DEFAULT_CHARSET,
OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY,
VARIABLE_PITCH,
TEXT("Times New Roman")
);
SelectObject(hdc, font);
swprintf_s(buffer, 256, L"Coordinates: (%d, %d)", p.x, p.y);
TextOut(hdc, 70, 10, buffer, wcslen(buffer));
swprintf_s(buffer, 256, L"RGB: (%d, %d, %d)", r, g, b);
TextOut(hdc, 70, 40, buffer, wcslen(buffer));
color_rect.left = 10;
color_rect.right = 60;
color_rect.top = 10;
color_rect.bottom = 60;
FillRect(hdc, &color_rect, (HBRUSH)CreateSolidBrush(RGB(r, g, b)));
EndPaint(window, &ps);
break;
case WM_CLOSE:
DestroyWindow(window);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
ReleaseDC(window, hdc);
return DefWindowProc(window, message, wParam, lParam);
}
ReleaseDC(window, hdc);
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
LPCWSTR class_name = L"name";
MSG message;
WNDCLASSEX window_class;
HWND window;
HHOOK MouseHook;
window_class.cbSize = sizeof(WNDCLASSEX);
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpfnWndProc = window_process;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
window_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
window_class.lpszMenuName = NULL;
window_class.lpszClassName = class_name;
window_class.hInstance = hInstance;
window_class.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&window_class);
window = CreateWindow(
class_name,
L"Pixel color",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT,
CW_USEDEFAULT,
200,
100,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(window, SW_SHOWNORMAL);
MouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouse_hook_low_level, hInstance, 0);
while(GetMessage(&message, NULL, 0, 0)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
UnhookWindowsHookEx(MouseHook);
return message.wParam;
}
The question is: how can I get a handle to the window for further window update in hook procedure at line 9? And what can you say generally about my code? I'm a student and have a little C-programming experience, could you point out my errors?
Thanks in advance.
First, try WindowFromPoint. If it fails to find a window, i.e. it's not your process window, then enumerate all top-level windows and all its child windows to find a topmost window under the mouse pointer.

Resources