双缓冲模式下无法使用透明画刷调用FillRect实现透明效果
问题描述
无法通过FillRect实现位图透明效果,已知创建的位图并非单色(灰色画刷可正常工作),但不知道如何检查它是彩色还是灰度图。位图默认背景为黑色,尝试将其改为透明但多次尝试均无效,当前效果是矩形随鼠标移动,但背景为黑色,完全看不到后方的其他窗口。
问题代码
#include<windows.h> #include<iostream> int main() { // Init DC HWND Wnd = GetDesktopWindow();//GetConsoleWindow(); HDC ScreenDC = GetDC(Wnd); // Init Rectangle RECT ClientRect; GetClientRect(Wnd, &ClientRect); // Init Double Buffer HDC MemDC = CreateCompatibleDC(ScreenDC); HBITMAP MemBM = CreateCompatibleBitmap(ScreenDC, ClientRect.right - ClientRect.left, ClientRect.bottom - ClientRect.top); HBITMAP OldBM = (HBITMAP)SelectObject(MemDC, MemBM); // Create Brush and Pen HPEN Pen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); HBRUSH ClearBrush = (HBRUSH)GetStockObject(GRAY_BRUSH); // Set Brush and Pen SelectObject(MemDC, Pen); SelectObject(MemDC, ClearBrush); POINT p; while(!GetAsyncKeyState(VK_RETURN)) { // Clear and Draw GetCursorPos(&p); FillRect(MemDC, &ClientRect, ClearBrush); Rectangle(MemDC, p.x, p.y, p.x+20, p.y+20); BitBlt(ScreenDC, 0, 0, ClientRect.right - ClientRect.left, ClientRect.bottom + ClientRect.left, MemDC, 0, 0, SRCCOPY); } SelectObject(MemDC, OldBM); DeleteObject(ClearBrush); DeleteObject(Pen); DeleteObject(OldBM); DeleteObject(MemBM); DeleteDC(MemDC); ReleaseDC(Wnd, ScreenDC); return 0; }
解决方案
核心问题分析
当前代码用SRCCOPY将整个内存DC的内容(包含纯色背景)覆盖到屏幕,导致后方窗口被遮挡。要实现透明,需要让内存DC中的指定颜色(比如黑色)在复制时被忽略,或者只绘制矩形区域而不覆盖背景。
实现透明的修改方案
使用TransparentBlt函数(需链接msimg32.lib),将内存DC中的黑色设为透明色,仅绘制红色矩形:
#include<windows.h> #include<iostream> #pragma comment(lib, "msimg32.lib") // 链接TransparentBlt所需库 int main() { HWND Wnd = GetDesktopWindow(); HDC ScreenDC = GetDC(Wnd); // 创建内存DC和仅对应矩形大小的位图,节省资源 HDC MemDC = CreateCompatibleDC(ScreenDC); HBITMAP MemBM = CreateCompatibleBitmap(ScreenDC, 20, 20); HBITMAP OldBM = (HBITMAP)SelectObject(MemDC, MemBM); // 创建红色画笔、黑色画刷(黑色作为透明色) HPEN Pen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); HBRUSH BlackBrush = (HBRUSH)GetStockObject(BLACK_BRUSH); SelectObject(MemDC, Pen); SelectObject(MemDC, BlackBrush); POINT p; while(!GetAsyncKeyState(VK_RETURN)) { GetCursorPos(&p); // 清除内存DC为透明色(黑色) FillRect(MemDC, &RECT{0,0,20,20}, BlackBrush); // 在内存DC绘制红色矩形 Rectangle(MemDC, 0, 0, 20, 20); // 透明复制到屏幕,黑色会被忽略 TransparentBlt(ScreenDC, p.x, p.y, 20, 20, MemDC, 0, 0, 20, 20, RGB(0,0,0)); Sleep(10); // 降低CPU占用率 } // 清理资源 SelectObject(MemDC, OldBM); DeleteObject(BlackBrush); DeleteObject(Pen); DeleteObject(MemBM); DeleteDC(MemDC); ReleaseDC(Wnd, ScreenDC); return 0; }
如何检查位图是彩色还是灰度图
通过GetObject获取位图的BITMAP结构,查看像素位深度即可判断:
BITMAP bmpInfo; GetObject(MemBM, sizeof(BITMAP), &bmpInfo); int bitsPerPixel = bmpInfo.bmBitsPixel * bmpInfo.bmPlanes; if (bitsPerPixel >= 24) { // 彩色位图 } else if (bitsPerPixel == 8) { // 灰度位图(8位调色板) } else if (bitsPerPixel == 1) { // 单色位图 }
额外注意点
- 原代码中
BitBlt的高度参数错误,写成了ClientRect.bottom + ClientRect.left,正确应为ClientRect.bottom - ClientRect.top; - 添加
Sleep(10)可减少循环的CPU占用; - 使用
TransparentBlt必须链接msimg32.lib,否则会出现链接错误。
内容的提问来源于stack exchange,提问作者JinxedGrimф
相关产品推荐
相关产品推荐

