Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draw semitransparently in invisible layered window

My goal is to have a fullscreen overlaying invisible "canvas" on which I can draw using win32's various drawing functions.

The way I am currently attempting it is this:

WNDCLASSA myclass = { 0 };
myclass.lpfnWndProc = WindowProc3;
myclass.hInstance = GetModuleHandle(0);
myclass.lpszClassName = "MyCanvas";
myclass.hbrBackground = CreateSolidBrush(0xFEEDBEEF);
myclass.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassA(&myclass);

...

HWND wnd = CreateWindowExA(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, "MyCanvas", 0, WS_POPUP | WS_VISIBLE, 0, 0, screen_width, screen_height, 0, 0, GetModuleHandle(0), 0);
SetLayeredWindowAttributes(wnd, 0xFEEDBEEF, 0, LWA_COLORKEY);

Although this serves as a canvas, hours of googling later, I am still unable to draw on it semitransparently.

I have added a screenshot of what my program is currently displaying as I am writing this. What I would like to be able to do is, for example, make the black box in the top right corner (drawn with Rectangle) semitransparent so as to reveal the stackoverflow page content below it.

This is a question I found that I was hopeful about, but the resulting text is just a blended combination of the background color ((COLORREF)0xFEEDBEEF) and text color. Other things I have found have either just made the element fully invisible, done nothing at all, or required some library like MFC. I want to only use win32 functions if at all possible, as I would like to be able to achieve the highest FPS possible.

I do not care if this doesn't work on all Windows versions as long as it does on 7 up to 10.

Current state of the problem

like image 622
Lupe Avatar asked Mar 10 '23 05:03

Lupe


1 Answers

If you only need transparency for a rectangular area where all pixels either have the same transparency (aka alpha) value or are completely transparent, you can use SetLayeredWindowAttributes() with a combination of alpha value and/or color key.

UpdateLayeredWindow() is the way to go if you need to be able to define transparency per-pixel.

For that you have to create memory DC and select a 32bpp bitmap into it. You may use the buffered paint API to ease the task. Raymond Chen has a blog post with a code sample about that.

You can draw into the memory DC, but you can't use most of GDI API for that, because GDI ignores the alpha channel (transparency). I suggest using GDI+ which allows you to specify the alpha values.

After you have completed drawing into the memory DC, you would call UpdateLayeredWindow() and pass that memory DC as the argument for the hdcSrc parameter to make the result visible on screen.

Illustration of possible effects:

SetLayeredWindowAttributes with LWA_ALPHA

SetLayeredWindowAttributes( hwnd, 0, 176, LWA_ALPHA ); 

SetLayeredWindowAttributes with LWA_COLORKEY

SetLayeredWindowAttributes( hwnd, colorkey, 0, LWA_COLORKEY ); 

SetLayeredWindowAttributes with LWA_ALPHA|LWA_COLORKEY

SetLayeredWindowAttributes( hwnd, colorkey, 176, LWA_ALPHA|LWA_COLORKEY ); 

UpdateLayeredWindow

UpdateLayeredWindow( ... )

Note the antialiased edge of the shape and the transparency gradient in the last example. Things like that are only possible with UpdateLayeredWindow().

like image 154
zett42 Avatar answered Mar 27 '23 02:03

zett42