You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WinAPI绘制文字变为透明区域问题:如何在透明窗口背景上绘制不透明纯黑色文字?

WinAPI绘制文字变为透明区域问题:如何在透明窗口背景上绘制不透明纯黑色文字?

看起来你遇到的问题是,在透明窗口的位图上绘制文字时,文字变成了透明区域,而不是你想要的纯黑不透明效果。这是因为GDI在32位带Alpha的DIBSection上使用TRANSPARENT背景模式绘制文字时,只会修改像素的RGB通道,而保留原有的Alpha通道值——如果原位图对应区域的Alpha是0,那么即使RGB设为黑色,显示出来还是透明的。

下面是具体的解决方法,我会用C++代码示例(你可以轻松转换成Rust的winapi绑定代码):

解决思路

我们需要先在一个临时的透明DC上绘制不透明的文字,再通过AlphaBlend把文字混合到目标位图上,这样就能保证文字区域的Alpha通道被设为255(不透明),同时RGB为纯黑色,且不会破坏原位图的其他区域。

修改后的代码示例(C++)

void Redraw(LPCWSTR text, HBITMAP hbitmap, HWND hwnd) {
    BITMAP bm = {0};
    GetObject(hbitmap, sizeof(BITMAP), &bm);

    HDC hdc_screen = GetDC(NULL);
    HDC hdc_mem = CreateCompatibleDC(hdc_screen);
    HGDIOBJ old_bitmap = SelectObject(hdc_mem, hbitmap);

    // 创建目标DIBSection(和你的原代码逻辑一致)
    BITMAPINFO bmi = {0};
    BITMAPINFOHEADER* header = &bmi.bmiHeader;
    header->biSize = sizeof(BITMAPINFOHEADER);
    header->biWidth = bm.bmWidth;
    header->biHeight = -bm.bmHeight;
    header->biPlanes = 1;
    header->biBitCount = 32;
    header->biCompression = BI_RGB;
    header->biSizeImage = 0;

    void* bits = nullptr;
    HBITMAP new_hbitmap = CreateDIBSection(hdc_screen, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
    HDC new_hdc = CreateCompatibleDC(hdc_screen);
    HGDIOBJ old_original = SelectObject(new_hdc, new_hbitmap);

    // 复制原到位图到目标DIB(和你的原代码逻辑一致)
    BitBlt(new_hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdc_mem, 0, 0, SRCCOPY);

    // ---------------------- 新增的绘制文字核心逻辑 ----------------------
    // 1. 创建临时DC,用于绘制不透明文字
    HDC hdc_temp = CreateCompatibleDC(hdc_screen);
    BITMAPINFO temp_bmi = bmi; // 复用目标DIB的格式
    void* temp_bits = nullptr;
    HBITMAP temp_hbitmap = CreateDIBSection(hdc_screen, &temp_bmi, DIB_RGB_COLORS, &temp_bits, NULL, 0);
    HGDIOBJ old_temp_bitmap = SelectObject(hdc_temp, temp_hbitmap);

    // 2. 填充临时DC为全透明(Alpha=0,RGB=0)
    SetTextColor(hdc_temp, 0x00000000);
    SetBkColor(hdc_temp, 0x00000000);
    PatBlt(hdc_temp, 0, 0, bm.bmWidth, bm.bmHeight, PATCOPY);

    // 3. 在临时DC上绘制不透明纯黑文字
    SetTextColor(hdc_temp, 0xFF000000); // Alpha=255,RGB=0,0,0(纯黑不透明)
    SetBkMode(hdc_temp, OPAQUE);
    SetBkColor(hdc_temp, 0x00000000); // 背景设为透明,不干扰原位图
    RECT text_rect = {17, 8, 236, 66};
    DrawTextW(hdc_temp, text, -1, &text_rect, DT_CENTER | DT_VCENTER | DT_WORDBREAK);

    // 4. 用AlphaBlend把临时DC的文字混合到目标DC上
    BLENDFUNCTION blend = {0};
    blend.BlendOp = AC_SRC_OVER;
    blend.SourceConstantAlpha = 255;
    blend.AlphaFormat = AC_SRC_ALPHA;
    AlphaBlend(new_hdc, 0, 0, bm.bmWidth, bm.bmHeight,
               hdc_temp, 0, 0, bm.bmWidth, bm.bmHeight, blend);

    // 清理临时资源
    SelectObject(hdc_temp, old_temp_bitmap);
    DeleteObject(temp_hbitmap);
    DeleteDC(hdc_temp);
    // ---------------------- 新增逻辑结束 ----------------------

    // 后续的UpdateLayeredWindow逻辑(和你的原代码一致)
    RECT window_rect = {0};
    GetWindowRect(hwnd, &window_rect);

    BLENDFUNCTION window_blend = {0};
    window_blend.BlendOp = AC_SRC_OVER;
    window_blend.SourceConstantAlpha = 255;
    window_blend.AlphaFormat = AC_SRC_ALPHA;
    UpdateLayeredWindow(hwnd, hdc_screen,
                        &POINT{window_rect.left, window_rect.top},
                        &SIZE{bm.bmWidth, bm.bmHeight},
                        new_hdc, &POINT{0,0},
                        0, &window_blend, ULW_ALPHA);

    // 清理所有资源
    SelectObject(new_hdc, old_original);
    DeleteDC(new_hdc);
    DeleteObject(new_hbitmap);
    SelectObject(hdc_mem, old_bitmap);
    DeleteDC(hdc_mem);
    ReleaseDC(NULL, hdc_screen);
}

针对你Rust代码的具体修改点

  1. 新增临时DIBSection和DC的创建逻辑,对应Rust中的CreateDIBSectionCreateCompatibleDC调用。
  2. PatBlt填充临时DC为透明状态。
  3. 在临时DC上设置SetTextColor0xFF000000(纯黑不透明),SetBkModeOPAQUESetBkColor0x00000000后绘制文字。
  4. 调用AlphaBlend将临时DC的文字混合到目标DC。
  5. 记得及时清理临时创建的DC和位图资源,避免内存泄漏。

关键说明

  • 0xFF000000这个COLORREF在32位DIBSection中,对应BGRA像素格式的0x000000FF(B=0, G=0, R=0, A=255),正好是纯黑不透明的像素。
  • AlphaBlend会自动识别临时DC中的透明背景,只把不透明的文字区域覆盖到目标位图上,完美保留原位图的其他区域的透明/半透明效果。

内容来源于stack exchange

火山引擎 最新活动