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

如何在透明背景上绘制HCURSOR并避免黑色背景填充?

问题

需求:在填充透明色的表面上绘制HCURSOR,仅显示光标本身,不填充黑色背景;计划通过逐像素检查颜色来跳过透明区域。

现有基础代码:

CURSORINFO cursor = { sizeof( cursor ) };

if( GetCursorInfo( &cursor ) && cursor.flags == CURSOR_SHOWING )
    ...

尝试使用DrawIcon()和DrawIconEx()函数,但绘制时会出现黑色背景。具体实现代码如下:

CURSORINFO cursor = { sizeof( cursor ) };

if( GetCursorInfo( &cursor ) && cursor.flags == CURSOR_SHOWING )
{
    ICONINFO info = { sizeof( info ) };

    if( GetIconInfo( cursor.hCursor, &info ) )
    {
        HWND hWnd = GetDesktopWindow();
        HDC hDC = GetWindowDC( hWnd );
        HDC hdcMem = CreateCompatibleDC( hDC );
        BITMAP bmpCursor = { 0 };
        GetObject( info.hbmColor ? info.hbmColor : info.hbmMask,
            sizeof( bmpCursor ), &bmpCursor );
        HBITMAP hBitmap = CreateCompatibleBitmap( hdcMem, bmpCursor.bmWidth, bmpCursor.bmHeight );
        auto original = SelectObject( hdcMem, hBitmap );
        RECT br;
        br.left = br.top = 0;
        br.right = bmpCursor.bmWidth;
        br.bottom = bmpCursor.bmHeight;
        HBRUSH hBrush = CreateSolidBrush( RGB( 1, 1, 1 ) );
        FillRect( hdcMem, &br, hBrush );
        DeleteObject( hBrush );
        DrawIconEx( hdcMem, 0, 0, cursor.hCursor, 0, 0, 0, nullptr, DI_DEFAULTSIZE | DI_NORMAL );

        QImage img( bmpCursor.bmWidth, bmpCursor.bmHeight, QImage::Format_ARGB32 );
        img.fill( Qt::transparent );

        std::set< COLORREF > s;

        for( int x = 0; x < bmpCursor.bmWidth; ++x )
        {
            for( int y = 0; y < bmpCursor.bmHeight; ++y )
            {
                COLORREF c = GetPixel( hdcMem, x, y );
                s.insert( c );

                if( c != RGB( 1, 1, 1 ) )
                {
                    auto qc = QColor( QRgba64::fromArgb32( c ) );
                    qc.setAlpha( 255 );
                    img.setPixelColor( x, y, qc );
                }
            }
        }

        cursorPos = r.intersects( { QPoint( cursor.ptScreenPos.x - info.xHotspot,
                    cursor.ptScreenPos.y - info.yHotspot ),
                QSize( bmpCursor.bmWidth, bmpCursor.bmHeight ) } ) ?
            QPoint( cursor.ptScreenPos.x - info.xHotspot - r.x(),
                cursor.ptScreenPos.y - info.yHotspot - r.y() ) :
            QPoint( -1, -1 );
        w = cursorPos.x() != -1 ? bmpCursor.bmWidth : 0;
        h = cursorPos.y() != -1 ? bmpCursor.bmHeight : 0;
        cursorImage = img.copy();

        if( info.hbmMask )
            DeleteObject( info.hbmMask );

        if( info.hbmColor )
            DeleteObject( info.hbmColor );

        SelectObject( hdcMem, original );

        DeleteDC( hdcMem );
        DeleteObject( hBitmap );

    }

    if( cursor.hCursor )
        DeleteObject( cursor.hCursor );
}

但发现std::set<COLORREF> s中只有0和FFFFFF值,DrawIconEx将所有RGB(1,1,1)像素替换成了0,请问如何避免这种情况?

解决方案

问题根源

DrawIconEx会利用光标自带的掩码(mask)处理透明区域,默认将掩码对应的区域绘制为黑色(RGB(0,0,0)),直接覆盖了你预先填充的RGB(1,1,1)背景,这就是集合中只有0和FFFFFF的原因。

可行解决方法

  1. 使用带Alpha通道的DIB位图
    放弃CreateCompatibleBitmap,创建支持Alpha通道的设备无关位图(DIB),这样绘制光标时能直接保留透明信息,无需手动替换颜色:

    BITMAPINFO bmi = { 0 };
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = bmpCursor.bmWidth;
    bmi.bmiHeader.biHeight = -bmpCursor.bmHeight; // 以顶部为坐标原点
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;
    bmi.bmiHeader.biSizeImage = bmpCursor.bmWidth * bmpCursor.bmHeight * 4;
    
    void* pBits = nullptr;
    HBITMAP hBitmap = CreateDIBSection(hdcMem, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
    
  2. 利用光标掩码判断透明区域
    从ICONINFO获取的hbmMask是单色位图,可直接用它判断透明区域,无需依赖自定义背景色:

    // 创建掩码位图的DC
    HDC hdcMask = CreateCompatibleDC(hdcMem);
    HBITMAP oldMask = (HBITMAP)SelectObject(hdcMask, info.hbmMask);
    
    for( int x = 0; x < bmpCursor.bmWidth; ++x )
    {
        for( int y = 0; y < bmpCursor.bmHeight; ++y )
        {
            COLORREF c = GetPixel(hdcMem, x, y);
            // 掩码中白色对应不透明区域,黑色对应透明区域
            COLORREF maskColor = GetPixel(hdcMask, x, y);
            if (maskColor == RGB(255,255,255))
            {
                auto qc = QColor(QRgba64::fromArgb32(c));
                qc.setAlpha(255);
                img.setPixelColor(x, y, qc);
            }
            // 透明区域保持QImage初始的透明状态即可
        }
    }
    
    SelectObject(hdcMask, oldMask);
    DeleteDC(hdcMask);
    
  3. 修复代码错误
    GetCursorInfo返回的cursor.hCursor属于系统资源,不要调用DeleteObject销毁,直接删除代码中if( cursor.hCursor ) DeleteObject( cursor.hCursor );这一行。


内容的提问来源于stack exchange,提问作者Igor Mironchik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 03:12:06