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

双缓冲模式下无法使用透明画刷调用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ф

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 07:20:39