如何在WINAPI GDI中实现文字淡入淡出效果并去除位图黑色背景?
解决透明文字淡入淡出的问题
你的问题根源在于创建的32位位图没有正确处理Alpha通道——文字之外的黑色区域并没有被标记为完全透明,而AlphaBlend默认忽略位图自身的Alpha通道,导致黑色背景跟着文字一起显示并变淡。我们需要两步来解决:
- 给位图的文字区域设置完全不透明的Alpha通道,背景区域设为完全透明
- 让
AlphaBlend同时使用位图自身的Alpha通道和全局淡出透明度
修改后的关键代码
1. 更新NUMBERINFO结构体(保存位图宽高方便后续使用)
typedef struct { UINT ticks; HBITMAP buffer; int width; // 添加:保存位图宽度 int height; // 添加:保存位图高度 } NUMBERINFO;
2. 重写CreateNumber函数(正确处理Alpha通道)
HWND CreateNumber(HWND parent, const char *text, int x, int y, COLORREF color) { NUMBERINFO *ni = malloc(sizeof(*ni)); HDC hdc = GetDC(NULL); SIZE s; GetTextExtentPoint32(hdc, text, strlen(text), &s); ni->width = s.cx; ni->height = s.cy; // 创建带Alpha通道的32位DIBSection BITMAPINFO bmi; memset(&bmi, 0, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = s.cx; bmi.bmiHeader.biHeight = s.cy; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biSizeImage = s.cx * s.cy * 4; COLORREF *pvBits; HBITMAP buffer = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**)&pvBits, NULL, 0); // 初始化所有像素为完全透明(Alpha=0) memset(pvBits, 0, s.cx * s.cy * 4); // 创建单色掩码位图,用于区分文字和背景 HBITMAP maskBmp = CreateBitmap(s.cx, s.cy, 1, 1, NULL); HDC maskDc = CreateCompatibleDC(hdc); HBITMAP oldMaskBmp = SelectObject(maskDc, maskBmp); // 掩码DC:白色背景,黑色文字 SetBkColor(maskDc, RGB(255,255,255)); SetTextColor(maskDc, RGB(0,0,0)); TextOut(maskDc, 0, 0, text, strlen(text)); // 绘制文字到DIBSection HDC bufferDc = CreateCompatibleDC(hdc); HBITMAP oldBmp = SelectObject(bufferDc, buffer); SetBkMode(bufferDc, TRANSPARENT); SetTextColor(bufferDc, color); TextOut(bufferDc, 0, 0, text, strlen(text)); // 给文字区域设置Alpha通道为0xFF(完全不透明) for (int y = 0; y < s.cy; y++) { for (int x = 0; x < s.cx; x++) { if (GetPixel(maskDc, x, y) == RGB(0,0,0)) { // 文字像素 int pixelIdx = y * s.cx + x; pvBits[pixelIdx] |= 0xFF000000; // 设置Alpha为255 } } } // 清理资源 SelectObject(maskDc, oldMaskBmp); DeleteDC(maskDc); DeleteObject(maskBmp); SelectObject(bufferDc, oldBmp); DeleteDC(bufferDc); ReleaseDC(NULL, hdc); ni->buffer = buffer; return CreateWindow(WC_NUMBER, text, WS_VISIBLE | WS_CHILD, x - (s.cx >> 1), y - (s.cy >> 1), s.cx, s.cy, parent, NULL, NULL, ni); }
3. 修改NumberProc中的WM_PAINT和WM_DESTROY消息处理
LRESULT CALLBACK NumberProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { NUMBERINFO *info = (NUMBERINFO*) GetWindowLongPtr(hWnd, GWLP_USERDATA); switch(msg) { // ... 其他消息处理不变 case WM_DESTROY: DeleteObject(info->buffer); // 释放位图资源 free(info); // 释放结构体内存 return 0; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); RECT r; GetClientRect(hWnd, &r); HDC bufferDc = CreateCompatibleDC(hdc); HBITMAP oldBmp = SelectObject(bufferDc, info->buffer); BLENDFUNCTION bfn; bfn.BlendOp = AC_SRC_OVER; bfn.BlendFlags = 0; bfn.SourceConstantAlpha = info->ticks * 0xFF / NUMBER_TICKS; // 全局淡出透明度 bfn.AlphaFormat = AC_SRC_ALPHA; // 启用位图自身的Alpha通道 AlphaBlend(hdc, 0, 0, r.right, r.bottom, bufferDc, 0, 0, info->width, info->height, bfn); SelectObject(bufferDc, oldBmp); DeleteDC(bufferDc); EndPaint(hWnd, &ps); return 0; } } return DefWindowProc(hWnd, msg, wParam, lParam); }
关键修改说明
- Alpha通道初始化:创建DIBSection后用
memset把所有像素设为0,也就是Alpha=0(完全透明) - 掩码位图:用来精准区分文字和背景区域,避免手动计算文字边界
- 设置文字Alpha:遍历掩码位图,把文字对应的像素Alpha设为255,确保文字不透明
- AlphaBlend参数:设置
AlphaFormat = AC_SRC_ALPHA,让系统同时使用位图自身的Alpha通道(控制背景透明)和SourceConstantAlpha(控制整体淡出)
这样修改后,文字会保持透明背景,同时随着计时器触发逐渐变淡消失,完美解决你的问题。
内容的提问来源于stack exchange,提问作者Lost
相关产品推荐
相关产品推荐

