August Vishnevsky
August Vishnevsky

Reputation: 79

Drawing a semi-transparent rectangle in a transparent window

I found this code in stackoverflow and it really draws a rectangle on a transparent window using a bitmap. But somehow I cannot change the transparency of the rectangle. More precisely, I can, but it gets darker. As if the bitmap itself has a black background. How do I make the rectangle transparent?

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

Full code

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

Upvotes: 2

Views: 1312

Answers (1)

Zeus
Zeus

Reputation: 3880

“As if the bitmap itself has a black background.”

Because your background is set to:

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

Of course you can set it to NULL_BRUSH to make it look transparent:

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

enter image description here

But when you move it, you will find that this is not to make the window transparent, but to stop drawing the background:

enter image description here

As @Ben answers 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.

And you need to refer to Layered Windows

You can't add rectangles directly on the window, you should use a layered window, and then add what you need.

Here is a sample that you can refer to it:

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

Upvotes: 2

Related Questions