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代码的具体修改点
- 新增临时DIBSection和DC的创建逻辑,对应Rust中的
CreateDIBSection、CreateCompatibleDC调用。 - 用
PatBlt填充临时DC为透明状态。 - 在临时DC上设置
SetTextColor为0xFF000000(纯黑不透明),SetBkMode为OPAQUE,SetBkColor为0x00000000后绘制文字。 - 调用
AlphaBlend将临时DC的文字混合到目标DC。 - 记得及时清理临时创建的DC和位图资源,避免内存泄漏。
关键说明
0xFF000000这个COLORREF在32位DIBSection中,对应BGRA像素格式的0x000000FF(B=0, G=0, R=0, A=255),正好是纯黑不透明的像素。AlphaBlend会自动识别临时DC中的透明背景,只把不透明的文字区域覆盖到目标位图上,完美保留原位图的其他区域的透明/半透明效果。
内容来源于stack exchange




