Drawing a partially transparent GDI+ Bitmap to a borderless window using UpdateLayeredWindow

Deadly 提交于 2019-12-21 06:42:30

问题


I'm trying to create a non-rectangular window with semi-transparent pixels. The image does not come from a PNG but is drawn on-the-fly using GDI+ calls.

I create the window as follows:

WNDCLASSEX wc = WNDCLASSEX();
wc.cbSize = sizeof(wc);
HINSTANCE instance = GetModuleHandle(nullptr);
std::wstring classname(L"gditest ui window class");

if (!GetClassInfoEx(instance, classname.c_str(), &wc)) {
    //wc.cbSize;
    //wc.style = CS_DROPSHADOW;
    wc.lpfnWndProc = process_messages;
    //wc.cbClsExtra;
    //wc.cbWndExtra;
    wc.hInstance = instance;
    wc.hIcon;
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    //wc.hbrBackground;
    //wc.lpszMenuName;
    wc.lpszClassName = classname.c_str();
    wc.hIconSm;

    if (!RegisterClassEx(&wc))
        throw GetLastError();
}

m_window = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_LAYERED,
    classname.c_str(), L"User Interface",
    WS_VISIBLE,
    CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
    HWND_DESKTOP, 0, instance, this);

if (!m_window)
    throw GetLastError();

update_window();

The update_window() function looks like this:

void user_interface::update_window()
{
    RECT r;
    GetClientRect(m_window, &r);

    Bitmap buf(r.right - r.left, r.bottom - r.top, PixelFormat32bppARGB);

    Graphics gfx(&buf);
    Rect rect(r.left, r.top, r.right - r.left, r.bottom - r.top);
    SolidBrush b(Color(0x7f00ff00));
    gfx.FillRectangle(&b, rect);

/*  CLSID clsid;
    UINT numbytes = 0, numenc = 0;
    GetImageEncodersSize(&numenc, &numbytes);

    std::vector<char> encoders(numbytes, 0);
    ImageCodecInfo *encoderptr = (ImageCodecInfo *)&encoders[0];
    GetImageEncoders(numenc, numbytes, encoderptr);

    clsid = encoderptr[4].Clsid;

    buf.Save(L"test.png", &clsid);
*/
    HDC gfxdc = gfx.GetHDC();
    HDC scrndc = GetDC(HWND_DESKTOP);

    BLENDFUNCTION blend;
    blend.BlendOp = AC_SRC_OVER;
    blend.BlendFlags = 0;
    blend.SourceConstantAlpha = 255;
    blend.AlphaFormat = AC_SRC_ALPHA;

    POINT src = POINT(), dst;
    SIZE size;

    GetWindowRect(m_window, &r);
    dst.x = r.left;
    dst.y = r.top;
    size.cx = buf.GetWidth();
    size.cy = buf.GetHeight();

    if (!UpdateLayeredWindow(m_window, scrndc, &dst, &size, gfxdc, &src, 0, &blend, ULW_ALPHA)) {
        throw GetLastError();
    }

    ReleaseDC(HWND_DESKTOP, scrndc);
    gfx.ReleaseHDC(gfxdc);
}

The commented piece of code saves the Bitmap object to a PNG, which I wrote just to confirm the bitmap is drawn properly.

No errors occur, however the result on screen is not what I intended. Instead of a nice 50% transparent green square I get a barely visible white square:

.

Another weird thing is that clicks on the window fall through to whatever is underneath, eventhough it is slightly visible...

What am I doing wrong here?


回答1:


Managed to solve this myself by rewriting the update_window() method as such:

void user_interface::update_window()
{
    RECT r;
    GetClientRect(m_window, &r);

    HDC scrndc = GetDC(HWND_DESKTOP);
    HDC memdc = CreateCompatibleDC(scrndc);
    HBITMAP bmp = CreateCompatibleBitmap(scrndc, r.right - r.left, r.bottom - r.top);
    HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, bmp);

    Graphics gfx(memdc);
    Rect rect(r.left, r.top, r.right - r.left, r.bottom - r.top);
    SolidBrush b(Color(0x7f00ff00));
    gfx.FillRectangle(&b, rect);

    BLENDFUNCTION blend;
    blend.BlendOp = AC_SRC_OVER;
    blend.BlendFlags = 0;
    blend.SourceConstantAlpha = 255;
    blend.AlphaFormat = AC_SRC_ALPHA;

    POINT src = POINT(), dst;
    SIZE size;

    size.cx = r.right - r.left;
    size.cy = r.bottom - r.top;

    GetWindowRect(m_window, &r);
    dst.x = r.left;
    dst.y = r.top;

    if (!UpdateLayeredWindow(m_window, scrndc, &dst, &size, memdc, &src, 0, &blend, ULW_ALPHA)) {
        throw GetLastError();
    }

    SelectObject(memdc, oldbmp);
    DeleteObject(bmp);
    DeleteDC(memdc);
    ReleaseDC(HWND_DESKTOP, scrndc);
}

Probably not the most efficient way to do it, but it works. Could probably keep the HBITMAP, memdc and Graphics object around for longer. Figuring this out is left as an exercise for the reader. ;)




回答2:


The source bitmap for UpdateLayeredWindow() must be compatible to the screen, otherways your visual result is hardware-dependent. GDI and GDIplus seem to be able to draw into any bitmap, but bitblt operations generally cannot handle different (i.e. incompatible) formats. The reason is speed.

Unluckily, Windows documentation doesn't point out these facts very clearly.



来源:https://stackoverflow.com/questions/14315869/drawing-a-partially-transparent-gdi-bitmap-to-a-borderless-window-using-updatel

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!