You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows Aero Glass模糊窗口渲染异常及自定义模糊实现问询

模糊窗口问题修复与自定义模糊实现方案

一、修复当前模糊窗口的异常

问题根源分析

你的代码存在几个关键问题导致边缘异常、文本无法融合:

  1. 窗口背景冲突:窗口类设置了hbrBackground = (HBRUSH)(COLOR_WINDOW + 1),同时WM_PAINT中用不透明画刷填充背景,这会覆盖系统模糊效果,导致模糊叠加、文本无法与背景融合。
  2. 分层窗口与ACCENT策略冲突:使用WS_EX_LAYERED扩展风格同时调用SetLayeredWindowAttributes设置全alpha,会干扰系统 compositor 对模糊效果的渲染,引发边缘异常。
  3. GradientColor参数错误:DWORD(L"FFFFFFFF")是非法的颜色值,正确的ARGB格式应为十六进制数值(如0x00FFFFFF表示无 tint 的纯模糊)。

修正后的完整代码

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>

static TCHAR szWindowClass[] = _T("DesktopApp");
static TCHAR szTitle[] = _T("Blur Window Demo");

HINSTANCE hInst;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(wcex.hInstance, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    // 设置背景画刷为透明,避免覆盖模糊效果
    wcex.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);

    if (!RegisterClassEx(&wcex))
        return 1;

    hInst = hInstance;

    // 移除WS_EX_LAYERED,避免与ACCENT模糊冲突
    HWND hWnd = CreateWindowEx(
        WS_EX_COMPOSITED | WS_EX_APPWINDOW,
        szWindowClass,
        szTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        500, 300,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hWnd)
        return 1;

    // 移除SetLayeredWindowAttributes调用,ACCENT_BLURBEHIND不需要分层窗口alpha
    typedef enum _WINDOWCOMPOSITIONATTRIB
    {
        WCA_UNDEFINED = 0,
        WCA_NCRENDERING_ENABLED = 1,
        WCA_NCRENDERING_POLICY = 2,
        WCA_TRANSITIONS_FORCEDISABLED = 3,
        WCA_ALLOW_NCPAINT = 4,
        WCA_CAPTION_BUTTON_BOUNDS = 5,
        WCA_NONCLIENT_RTL_LAYOUT = 6,
        WCA_FORCE_ICONIC_REPRESENTATION = 7,
        WCA_EXTENDED_FRAME_BOUNDS = 8,
        WCA_HAS_ICONIC_BITMAP = 9,
        WCA_THEME_ATTRIBUTES = 10,
        WCA_NCRENDERING_EXILED = 11,
        WCA_NCADORNMENTINFO = 12,
        WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
        WCA_VIDEO_OVERLAY_ACTIVE = 14,
        WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
        WCA_DISALLOW_PEEK = 16,
        WCA_CLOAK = 17,
        WCA_CLOAKED = 18,
        WCA_ACCENT_POLICY = 19,
        WCA_FREEZE_REPRESENTATION = 20,
        WCA_EVER_UNCLOAKED = 21,
        WCA_VISUAL_OWNER = 22,
        WCA_HOLOGRAPHIC = 23,
        WCA_EXCLUDED_FROM_DDA = 24,
        WCA_PASSIVEUPDATEMODE = 25,
        WCA_LAST = 26
    } WINDOWCOMPOSITIONATTRIB;

    typedef struct _WINDOWCOMPOSITIONATTRIBDATA
    {
        WINDOWCOMPOSITIONATTRIB Attrib;
        PVOID pvData;
        SIZE_T cbData;
    } WINDOWCOMPOSITIONATTRIBDATA;

    typedef enum _ACCENT_STATE
    {
        ACCENT_DISABLED = 0,
        ACCENT_ENABLE_GRADIENT = 1,
        ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
        ACCENT_ENABLE_BLURBEHIND = 3,
        ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
        ACCENT_ENABLE_HOSTBACKDROP = 5,
        ACCENT_INVALID_STATE = 6
    } ACCENT_STATE;

    typedef struct _ACCENT_POLICY
    {
        ACCENT_STATE AccentState;
        DWORD AccentFlags;
        DWORD GradientColor;
        DWORD AnimationId;
    } ACCENT_POLICY;

    typedef BOOL(WINAPI* SetWindowCompositionAttribute)(
        IN HWND hwnd,
        IN WINDOWCOMPOSITIONATTRIBDATA* pwcad);

    SetWindowCompositionAttribute lSetWindowCompositionAttribute;

    auto user32Lib = LoadLibrary(L"user32.dll");
    if (!user32Lib)
        return 1;

    lSetWindowCompositionAttribute = (SetWindowCompositionAttribute)GetProcAddress(user32Lib, "SetWindowCompositionAttribute");
    if (!lSetWindowCompositionAttribute)
        return 1;

    if (lSetWindowCompositionAttribute)
    {
        // 修正GradientColor:0x00FFFFFF表示无颜色tint,纯模糊;若需半透明白色tint用0xCCFFFFFF
        ACCENT_POLICY accent = { 
            ACCENT_ENABLE_BLURBEHIND, 0, 0x00FFFFFF, 0
        };
        WINDOWCOMPOSITIONATTRIBDATA data;
        data.Attrib = WCA_ACCENT_POLICY;
        data.pvData = &accent;
        data.cbData = sizeof(accent);
        lSetWindowCompositionAttribute(hWnd, &data);
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    FreeLibrary(user32Lib); // 补充释放库
    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    TCHAR greeting[] = _T("Hello, Blur Window!");

    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // 移除FillRect,避免覆盖模糊背景
        // 设置文本背景透明,让文本与模糊背景融合
        SetBkMode(hdc, TRANSPARENT);
        TextOut(hdc,
            5, 5,
            greeting, _tcslen(greeting));
        
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }

    return 0;
}

关键修改说明

  • 窗口背景设为NULL_BRUSH,避免不透明背景覆盖模糊效果。
  • 移除WS_EX_LAYERED和SetLayeredWindowAttributes,消除与系统模糊的冲突。
  • 修正GradientColor为合法ARGB值,控制模糊后的颜色 tint。
  • WM_PAINT中设置文本背景透明(SetBkMode(hdc, TRANSPARENT)),移除FillRect,让文本直接叠加在模糊背景上。

二、自行实现模糊:获取窗口后方帧缓冲

可以通过捕获桌面对应区域的位图来自行实现模糊,步骤如下:

核心思路

  1. 获取窗口在屏幕上的位置和尺寸。
  2. 从桌面DC捕获对应区域的位图。
  3. 对位图执行模糊算法(如高斯模糊)。
  4. 将模糊后的位图绘制到窗口上,再叠加自定义内容。

代码示例(捕获桌面区域+简单模糊)

// 辅助函数:捕获窗口后方的桌面位图
HBITMAP CaptureBehindWindow(HWND hWnd)
{
    RECT windowRect;
    GetWindowRect(hWnd, &windowRect);
    int width = windowRect.right - windowRect.left;
    int height = windowRect.bottom - windowRect.top;

    // 获取桌面DC
    HDC hDesktopDC = GetDC(NULL);
    HDC hMemDC = CreateCompatibleDC(hDesktopDC);
    HBITMAP hBitmap = CreateCompatibleBitmap(hDesktopDC, width, height);
    HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);

    // 捕获桌面对应区域
    BitBlt(hMemDC, 0, 0, width, height, hDesktopDC, windowRect.left, windowRect.top, SRCCOPY);

    // 恢复DC
    SelectObject(hMemDC, hOldBitmap);
    DeleteDC(hMemDC);
    ReleaseDC(NULL, hDesktopDC);

    return hBitmap;
}

// 简单高斯模糊实现(示例框架,需补充完整算法)
void BlurBitmap(HBITMAP hBitmap, int blurRadius)
{
    BITMAP bmp;
    GetObject(hBitmap, sizeof(BITMAP), &bmp);
    // 此处可使用第三方库(如OpenCV)或自行编写高斯模糊逻辑
    // 核心:遍历每个像素,计算周围blurRadius范围内的像素平均值并替换
}

// 在WM_PAINT中使用
case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    RECT clientRect;
    GetClientRect(hWnd, &clientRect);
    
    // 捕获后方桌面位图
    HBITMAP hBehindBmp = CaptureBehindWindow(hWnd);
    if (hBehindBmp)
    {
        // 执行模糊
        BlurBitmap(hBehindBmp, 10);
        
        // 绘制模糊后的背景
        HDC hMemDC = CreateCompatibleDC(hdc);
        HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hBehindBmp);
        BitBlt(hdc, 0, 0, clientRect.right, clientRect.bottom, hMemDC, 0, 0, SRCCOPY);
        
        // 恢复资源
        SelectObject(hMemDC, hOldBmp);
        DeleteDC(hMemDC);
        DeleteObject(hBehindBmp);
    }

    // 叠加文本
    SetBkMode(hdc, TRANSPARENT);
    TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
    EndPaint(hWnd, &ps);
    break;

注意事项

  • 窗口移动或后方内容变化时,需要重新捕获位图(可通过WM_MOVE、WM_SIZE或定时器触发更新)。
  • 自行捕获的方式性能低于系统原生模糊,若追求性能优先,建议使用修正后的系统ACCENT模糊方案。
  • 若窗口是透明分层窗口,捕获前需临时隐藏窗口(ShowWindow(hWnd, SW_HIDE)),避免捕获到窗口自身,捕获后再显示。

内容的提问来源于stack exchange,提问作者BrokenBootLoader

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 09:59:51