Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a transparent window in C++ Win32

I'm creating what should be a very simple Win32 C++ app whose sole purpose it to ONLY display a semi-transparent PNG. The window shouldn't have any chrome, and all the opacity should be controlled in the PNG itself.

My problem is that the window doesn't repaint when the content under the window changes, so the transparent areas of the PNG are "stuck" with what was under the window when the application was initially started.

Here's the line where I setup the new window:

hWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_POPUP, 0, height/2 - 20, 40, 102, NULL, NULL, hInstance, 0); 

For the call to RegisterClassEx, I have this set for the background:

wcex.hbrBackground = (HBRUSH)0; 

Here is my handler for WM_PAINT message:

 case WM_PAINT:  {    hdc = BeginPaint(hWnd, &ps);    Gdiplus::Graphics graphics(hdc);    graphics.DrawImage(*m_pBitmap, 0, 0);    EndPaint(hWnd, &ps);    break;  } 

One thing to note is that the application is always docked to the left of the screen and doesn't move. But, what's underneath the application may change as the user opens, closes or moves windows under it.

When the application first starts, it looks perfect. The transparent (and simi-transparent) parts of the PNG show through perfectly. BUT, when the background underneath the application changes, the background DOESN'T update, it just stays the same from when the application first started. In fact, WM_PAINT (or WM_ERASEBKGND does not get called when the background changes).

I've been playing with this for quite a while and have gotten close to getting 100% right, but not quite there. For instance, I've tried setting the background to (HBRUSH) NULL_BRUSH and I've tried handling WM_ERASEBKGND.

What can be done to get the window to repaint when the contents under it changes?

like image 362
adoss Avatar asked Oct 19 '10 15:10

adoss


People also ask

How do I make a window transparent?

You can press the shortcut (ALT+A, which is changeable by clicking the toolbar icon) or you can hover your mouse over the top of a window and click the down arrow that appears, then select "Opacity." WindowTop also has the click-through feature that Peek Through has.


1 Answers

I was able to do exactly what I wanted by using the code from Part 1 and Part 2 of this series:

Displaying a Splash Screen with C++


  • Part 1: Creating a HBITMAP archive
  • Part 2: Displaying the window archive

Those blog posts are talking about displaying a splash screen in Win32 C++, but it was almost identical to what I needed to do. I believe the part that I was missing was that instead of just painting the PNG to the window using GDI+, I needed to use the UpdateLayeredWindow function with the proper BLENDFUNCTION parameter. I'll paste the SetSplashImage method below, which can be found in Part 2 in the link above:

void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash) {   // get the size of the bitmap   BITMAP bm;   GetObject(hbmpSplash, sizeof(bm), &bm);   SIZE sizeSplash = { bm.bmWidth, bm.bmHeight };    // get the primary monitor's info   POINT ptZero = { 0 };   HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);   MONITORINFO monitorinfo = { 0 };   monitorinfo.cbSize = sizeof(monitorinfo);   GetMonitorInfo(hmonPrimary, &monitorinfo);    // center the splash screen in the middle of the primary work area   const RECT & rcWork = monitorinfo.rcWork;   POINT ptOrigin;   ptOrigin.x = 0;   ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2;    // create a memory DC holding the splash bitmap   HDC hdcScreen = GetDC(NULL);   HDC hdcMem = CreateCompatibleDC(hdcScreen);   HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash);    // use the source image's alpha channel for blending   BLENDFUNCTION blend = { 0 };   blend.BlendOp = AC_SRC_OVER;   blend.SourceConstantAlpha = 255;   blend.AlphaFormat = AC_SRC_ALPHA;    // paint the window (in the right location) with the alpha-blended bitmap   UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash,       hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);    // delete temporary objects   SelectObject(hdcMem, hbmpOld);   DeleteDC(hdcMem);   ReleaseDC(NULL, hdcScreen); } 
like image 88
adoss Avatar answered Sep 30 '22 05:09

adoss