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

基于Win32 API与Direct2D实现带透明阴影的圆形窗口方案咨询

最优实现方案:Direct2D圆形窗口+透明穿透阴影

核心思路

结合WS_EX_LAYERED窗口样式,用UpdateLayeredWindow替代SetLayeredWindowAttributes,配合Direct2D离屏渲染生成带Alpha通道的位图实现正确透明混合;通过WM_NCHITTEST消息区分圆形交互区域与阴影穿透区域,同时解决闪烁问题。


实现步骤

1. 窗口创建与样式配置

  • 注册窗口类时设置hbrBackground = NULL,禁用GDI背景绘制
  • 创建窗口时使用以下样式:
    • 主样式:WS_POPUP | WS_VISIBLE(无标题栏的自定义窗口)
    • 扩展样式:WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP(分层窗口+禁用DWM重定向位图,减少闪烁)
HWND CreateCustomWindow(HINSTANCE hInstance, int x, int y, int width, int height) {
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = L"Direct2DCircleWindow";
    wc.hbrBackground = NULL; // 禁用GDI背景
    RegisterClassEx(&wc);

    return CreateWindowEx(
        WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP,
        L"Direct2DCircleWindow",
        L"",
        WS_POPUP | WS_VISIBLE,
        x, y, width, height,
        NULL, NULL, hInstance, NULL
    );
}

2. Direct2D离屏渲染配置

创建ID2D1BitmapRenderTarget作为离屏画布,确保支持Alpha通道,依次绘制高斯模糊阴影与圆形主体:

  • 清空画布为全透明
  • 绘制比圆形大的半透明椭圆作为阴影源,再应用高斯模糊效果
  • 绘制不透明的圆形主体,最终生成带Alpha通道的位图
ID2D1Bitmap* RenderCircleWithShadow(ID2D1Factory* pD2DFactory, ID2D1HwndRenderTarget* pHwndRT, float radius, int width, int height) {
    ID2D1BitmapRenderTarget* pBitmapRT = nullptr;
    pHwndRT->CreateCompatibleRenderTarget(
        D2D1::SizeF(width, height),
        &pBitmapRT
    );

    // 清空为全透明
    pBitmapRT->Clear(D2D1::ColorF(D2D1::ColorF::Transparent));

    // 创建阴影画刷(半透明黑色)
    ID2D1SolidColorBrush* pShadowBrush = nullptr;
    pBitmapRT->CreateSolidColorBrush(
        D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.3f),
        &pShadowBrush
    );

    // 绘制阴影源(比圆形大的椭圆)
    pBitmapRT->DrawEllipse(
        D2D1::Ellipse(D2D1::Point2F(width/2.0f, height/2.0f), radius+10.0f, radius+10.0f),
        pShadowBrush,
        0.0f
    );

    // 应用高斯模糊效果
    ID2D1Effect* pBlurEffect = nullptr;
    pD2DFactory->CreateEffect(CLSID_D2D1GaussianBlur, &pBlurEffect);
    pBlurEffect->SetInput(0, pBitmapRT->GetBitmap());
    pBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, 8.0f);
    pBitmapRT->DrawImage(pBlurEffect);

    // 创建主体画刷(示例蓝色)
    ID2D1SolidColorBrush* pMainBrush = nullptr;
    pBitmapRT->CreateSolidColorBrush(
        D2D1::ColorF(0.2f, 0.5f, 1.0f, 1.0f),
        &pMainBrush
    );

    // 绘制圆形主体
    pBitmapRT->FillEllipse(
        D2D1::Ellipse(D2D1::Point2F(width/2.0f, height/2.0f), radius, radius),
        pMainBrush
    );

    // 获取最终位图
    ID2D1Bitmap* pFinalBitmap = nullptr;
    pBitmapRT->GetBitmap(&pFinalBitmap);

    // 释放临时资源
    pShadowBrush->Release();
    pMainBrush->Release();
    pBlurEffect->Release();
    pBitmapRT->Release();

    return pFinalBitmap;
}

3. 更新分层窗口与透明混合

使用UpdateLayeredWindow将离屏渲染的位图更新到窗口,利用位图自身的Alpha通道实现正确的透明混合:

void UpdateLayeredWindowFromBitmap(HWND hwnd, ID2D1Bitmap* pBitmap, int x, int y) {
    D2D1_SIZE_U bitmapSize = pBitmap->GetPixelSize();
    HDC hdcScreen = GetDC(NULL);
    HBITMAP hBitmap = nullptr;

    // 将Direct2D位图转换为HBITMAP
    pBitmap->GetHBITMAP(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f), &hBitmap);

    BLENDFUNCTION blend = {0};
    blend.BlendOp = AC_SRC_OVER;
    blend.SourceConstantAlpha = 255; // 完全使用位图自身的Alpha通道
    blend.AlphaFormat = AC_SRC_ALPHA;

    POINT ptSrc = {0, 0};
    POINT ptDst = {x, y};
    SIZE sizeWnd = {bitmapSize.width, bitmapSize.height};

    // 更新分层窗口
    UpdateLayeredWindow(
        hwnd, hdcScreen, &ptDst, &sizeWnd,
        CreateCompatibleDC(hdcScreen), &ptSrc, 0, &blend, ULW_ALPHA
    );

    // 释放资源
    DeleteDC(CreateCompatibleDC(hdcScreen));
    DeleteObject(hBitmap);
    ReleaseDC(NULL, hdcScreen);
}

4. 鼠标穿透逻辑实现

在窗口过程中处理WM_NCHITTEST消息,判断鼠标位置:

  • 圆形区域内返回HTCLIENT,接收鼠标事件
  • 阴影区域返回HTTRANSPARENT,让系统将鼠标事件传递给下方窗口
  • 处理WM_ERASEBKGND返回TRUE,禁止系统擦除背景避免闪烁
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static float radius = 100.0f;
    static int width = 240;
    static int height = 240;

    switch(msg) {
        case WM_NCHITTEST: {
            POINT pt = {LOWORD(lParam), HIWORD(lParam)};
            ScreenToClient(hwnd, &pt);

            // 计算鼠标是否在圆形范围内
            float dx = pt.x - width/2.0f;
            float dy = pt.y - height/2.0f;
            if (dx*dx + dy*dy <= radius*radius) {
                return HTCLIENT;
            }
            // 阴影区域,允许鼠标穿透
            return HTTRANSPARENT;
        }
        case WM_ERASEBKGND:
            return TRUE; // 禁用系统背景擦除
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
}

方案优势

  1. 解决混合异常:通过UpdateLayeredWindow+带Alpha通道的Direct2D位图,实现阴影与桌面/下方窗口的正确透明混合,避免SetLayeredWindowAttributes的黑色背景混合问题。
  2. 精准鼠标穿透:通过WM_NCHITTEST区分交互区域和穿透区域,仅阴影部分允许鼠标穿透,圆形主体正常接收交互。
  3. 消除闪烁:使用WS_EX_NOREDIRECTIONBITMAP禁用DWM重定向,配合离屏渲染和WM_ERASEBKGND处理,彻底解决窗口移动时的闪烁问题。
  4. 纯Direct2D实现:全程使用Direct2D绘制,不依赖GDI/GDI+,符合需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:00:53