在透明 window 中绘制半透​​明矩形

Drawing a semi-transparent rectangle in a transparent window

我在 Whosebug 中找到了这段代码,它确实使用位图在透明 window 上绘制了一个矩形。但不知何故我无法改变矩形的透明度。更准确地说,我可以,但它变得更暗了。就好像位图本身有黑色背景一样。如何使矩形透明?

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);
    }

完整代码

#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.”

因为你的背景设置为:

windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);

当然你可以设置成NULL_BRUSH让它看起来透明:

windowClass.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);

但是当你移动它的时候,你会发现这并不是让window透明,而是停止绘制背景:

作为@Ben 的回答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.

需要参考Layered Windows

你不能直接在window上添加矩形,你应该使用分层window,然后添加你需要的。

这是一个示例,您可以参考它:

#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);
}