DWM窗口透明

memetao 于 2023-08-26 发布

Color Blending

ColorBlending 发生在PixelShader的下一个阶段: Output Merger Stage.

默认情况下, 混合功能时被关闭的。

方程

Color = SrcColor * SrcBlend + DestColor * DestBlend

假设4维向量SrcColor=(Rs, Gs, Bs, As), SrcBlend=(S1, S2, S3, S4), DestColor=(Rd, Gd, Bd, Ad),DestBlend(D1, D2, D3, D4),则混合颜色Color可用4维向量表示为: maColor = (Rs * S1 + Rd * D1, Gs * S2 + Gd * D2, Bs * S3 + Bd * D3, As * S4 + Ad * D4)

窗口透明

即使texture已经填好了每个像素点的alpha值, 当提交给swapChain的时候还是不生效的, 因为窗口有”底色”。

应用如下的代码, 可以让dwm阶段应用alpha值:

#include <windows.h>
#include <dwmapo.h>

static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD) {
    typedef LONG(WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*, ULONG, ULONGLONG);
    static PFN_RtlVerifyVersionInfo RtlVerifyVersionInfoFn = NULL;
    if (RtlVerifyVersionInfoFn == NULL)
        if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll"))
            RtlVerifyVersionInfoFn =
                (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo");
    if (RtlVerifyVersionInfoFn == NULL)
        return FALSE;

    RTL_OSVERSIONINFOEXW versionInfo = {};
    ULONGLONG conditionMask = 0;
    versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
    versionInfo.dwMajorVersion = major;
    versionInfo.dwMinorVersion = minor;
    VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
    VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
    return (RtlVerifyVersionInfoFn(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION,
                                   conditionMask) == 0)
               ? TRUE
               : FALSE;
}

#define _IsWindowsVistaOrGreater()                                                                 \
    _IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA
#define _IsWindows8OrGreater()                                                                     \
    _IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8
#define _IsWindows8Point1OrGreater()                                                               \
    _IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE
#define _IsWindows10OrGreater()                                                                    \
    _IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00),                                     \
                               0) // _WIN32_WINNT_WINTHRESHOLD / _WIN32_WINNT_WIN10
void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) {
    if (!_IsWindowsVistaOrGreater())
        return;

    BOOL composition;
    if (FAILED(::DwmIsCompositionEnabled(&composition)) || !composition)
        return;

    BOOL opaque;
    DWORD color;
    if (_IsWindows8OrGreater() ||
        (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque)) {
        HRGN region = ::CreateRectRgn(0, 0, -1, -1);
        DWM_BLURBEHIND bb = {};
        bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
        bb.hRgnBlur = region;
        bb.fEnable = TRUE;
        ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb);
        ::DeleteObject(region);
    }
    else {
        DWM_BLURBEHIND bb = {};
        bb.dwFlags = DWM_BB_ENABLE;
        ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb);
    }
}

alpha

特别注意

如果要应用到子窗口上, 子窗口需要指定WS_POPUP属性:

    WNDCLASSEX wc;
    // clear out the window class for use
    ZeroMemory(&wc, sizeof(WNDCLASSEX));
    // fill in the struct with the needed information
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = NULL;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    // wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = "WindowClass1";
    // register the window class
    RegisterClassEx(&wc);
    // create the window and use the result as the handle
    HWND hWnd1 =
        CreateWindowEx(NULL,
                       "WindowClass1",               // name of the window class
                       "Our First Windowed Program", // title of the window
                       NULL,                         // window style
                       GetSystemMetrics(SM_CXSCREEN) / 4, GetSystemMetrics(SM_CYSCREEN) / 4,
                       GetSystemMetrics(SM_CXSCREEN) / 2, GetSystemMetrics(SM_CYSCREEN) / 2,
                       NULL,         // we have no parent window, NULL
                       NULL,         // we aren't using menus, NULL
                       wc.hInstance, // application handle
                       NULL);        // used with multiple windows, NULL
    ShowWindow(hWnd1, SW_SHOWNOACTIVATE);
    UpdateWindow(hWnd1);
    // the handle for the window, filled by a function
    HWND hWnd2 =
        CreateWindowEx(NULL,
                       "WindowClass1",                   // name of the window class
                       "Child",                          // title of the window
                       WS_CHILD | WS_POPUP | WS_CAPTION, // window style
                       GetSystemMetrics(SM_CXSCREEN) / 4, GetSystemMetrics(SM_CYSCREEN) / 4,
                       GetSystemMetrics(SM_CXSCREEN) / 2, GetSystemMetrics(SM_CYSCREEN) / 2,
                       hWnd1,        // we have no parent window, NULL
                       NULL,         // we aren't using menus, NULL
                       wc.hInstance, // application handle
                       NULL);        // used with multiple windows, NULL

参考