TransparentBlt的结果不正确

时间:2014-05-02 15:50:27

标签: c++ winapi bitmap

我正在开发一个透明的树视图控件。为了实现透明度,我已将子类化为树,并覆盖了WM_PAINT(在我的WM_ERASEBKGND处理程序中,我只返回TRUE。滚动,鼠标滚轮和其他相关消息得到妥善处理)。 为了使树的背景透明,我使用以下算法(基于this CodeGuru 文章):

  1. 让树在内存DC中执行默认绘制(保存在memDC中)。

  2. 在另一个内存DC中获取父级的背景(保存在finalDC中)。

  3. 映射树和父坐标,这样我就可以获取父级背景位图的正确部分。

  4. 使用TransparentBlt和树的背景颜色(TransparentBlt( finalDC, ... , memDC, ... );)合并这两个图像。


  5. 在父窗口中,我实现了WM_PRINTCLIENT,因此我可以通过简单的::SendMessage( GetParent(hwnd), WM_PRINTCLIENT, (WPARAM)finalDC, (LPARAM)(PRF_CLIENT) );调用将其背景复制到内存DC(步骤2)。我得到的结果在 Windows XP Windows7 上都是正确的:

    enter image description here


    我通过::DefSubclassProc( hwnd, WM_PAINT, (WPARAM)memDC, 0 );调用获取树的默认位图(步骤1)。在这里,结果在 Windows XP Windows7 上都是正确的:

    Windows XP 上(我不知道为什么上传的图片会丢失复选框,一切在我的计算机上看起来都很好):

    enter image description here

    Windows7 上(我不知道为什么上传的图片遗漏了复选框,一切在我的电脑上都很好):

    enter image description here


    然而,在TransparentBlt()通话后,最终图片未正确绘制:

    Windows XP上复选框是问题 - > enter image description here

    Windows7 上,一些白色留在字母中 - > enter image description here


    这些图片是将位图从设备上下文导出到文件中的结果(我已修改this code以实现此目的)。

    以下是WM_PAINT的代码段:

    case WM_PAINT:
        {
            // usual stuff
            PAINTSTRUCT ps;
    
            RECT rcClient = {0};
            GetClientRect( hwnd, &rcClient );
    
            HDC hdc = BeginPaint( hwnd, &ps );
    
            // create helper memory DCs 
            HDC memDC = CreateCompatibleDC(hdc), finalDC = CreateCompatibleDC(hdc);
    
            // create helper bitmaps
            HBITMAP memBmp,  // default tree's paint
                finalBmp,    // parent's background image
                bmpOld, bmpOldFinal;  // needed for cleanup
    
            memBmp = CreateCompatibleBitmap( hdc, 
                rcClient.right - rcClient.left,
                rcClient.bottom - rcClient.top );
    
            bmpOld = (HBITMAP)SelectObject( memDC, memBmp );
    
            // map parent and child rectangles
    
            RECT rcParent; 
            GetClientRect( GetParent(hwnd), &rcParent );
    
            // upper left corners of the treeview, parent window
            POINT ptTreeUL, ptParentUL; 
    
            // map tree's coordinates
            ptTreeUL.x = rcClient.left;
            ptTreeUL.y = rcClient.top;
    
            ClientToScreen( hwnd, &ptTreeUL );
    
            // map parent's coordinates
            ptParentUL.x = rcParent.left;
            ptParentUL.y = rcParent.top;
    
            ScreenToClient( GetParent(hwnd), &ptParentUL );
    
            /********* get parent's background image *******/
    
            finalBmp = CreateCompatibleBitmap( hdc, 
                rcParent.right - rcParent.left,
                rcParent.bottom - rcParent.top );
    
            bmpOldFinal = (HBITMAP)SelectObject( finalDC, finalBmp );
    
            ::SendMessage( GetParent(hwnd), WM_PRINTCLIENT,(WPARAM)finalDC, 
                (LPARAM)(PRF_CLIENT) );
    
            /********* capture default tree image *********/
    
            ::DefSubclassProc( hwnd, WM_PAINT, (WPARAM)memDC, 0 );
    
            // get tree's background color 
    
            COLORREF clrMask = TreeView_GetBkColor(hwnd);
    
            if( clrMask == -1 )  // this means tree uses default system color
                clrMask = ::GetSysColor(COLOR_WINDOW);
    
            /**** combine tree's default image with parent's background ****/
            /**** so we can erase default background with parent's background ****/
    
            TransparentBlt( finalDC, 
                ptParentUL.x + ptTreeUL.x, 
                ptParentUL.y + ptTreeUL.y, 
                rcClient.right - rcClient.left, 
                rcClient.bottom - rcClient.top,
                memDC, 
                0, 0, 
                rcClient.right - rcClient.left, 
                rcClient.bottom - rcClient.top,
                clrMask );
    
            // draw the result into tree's DC
            BitBlt( hdc, 
                0, 0, 
                rcClient.right - rcClient.left, 
                rcClient.bottom - rcClient.top,
                finalDC, 
                ptParentUL.x + ptTreeUL.x, 
                ptParentUL.y + ptTreeUL.y, SRCCOPY);
    
            // cleanup
            SelectObject( memDC, bmpOld );
            DeleteDC( memDC );
            DeleteObject( memBmp );
            SelectObject( finalDC, bmpOldFinal );
            DeleteDC( finalDC );
            DeleteObject( finalBmp );
    
            EndPaint( hwnd, &ps );
        }
        return 0L;
    

    如何将默认树位图与父背景正确组合,实现透明度而不会产生视觉伪影?

    编辑:

    我能够通过抛弃TransparentBlt()并自己做事来解决复选框问题。

    ClearType字体仍然是个问题,工件仍然存在。面具创建是个问题。成员 arx 表示,背景组合对此负责。如果我可以创建适当的面具,那么我的问题就会得到解决。

    以下是整个子类过程:

    LRESULT CALLBACK TreeProc( HWND hwnd, UINT message, 
        WPARAM wParam, LPARAM lParam, 
        UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
    {
        switch (message)
        {
        // handle messages that paint tree without WM_PAINT
        case WM_TIMER:  // handles autoscrolling when item is partially visible
        case TVM_DELETEITEM:
        case TVM_INSERTITEM: 
        case WM_MOUSEWHEEL: 
        case WM_HSCROLL:  
        case WM_VSCROLL:  
            {
                ::SendMessage( hwnd, WM_SETREDRAW, (WPARAM)FALSE, 0 );
    
                LRESULT lres = ::DefSubclassProc( hwnd, message, wParam, lParam );
    
                ::SendMessage( hwnd, WM_SETREDRAW, (WPARAM)TRUE, 0 );
    
                ::RedrawWindow( hwnd, NULL, NULL, 
                    RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW );
    
                return lres;
            }
        case WM_PAINT:
            {
                // usual stuff
    
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint( hwnd, &ps );
    
                // get client coordinates of parent and tree window
    
                RECT rcClient = {0}, rcParent = {0};
    
                GetClientRect( hwnd, &rcClient );
                GetClientRect( GetParent(hwnd), &rcParent );
    
                // create helper DCs and bitmaps
    
                HDC memDC = CreateCompatibleDC(hdc),    //default tree paint
                    finalDC = CreateCompatibleDC(hdc),  // parent's WM_PRINTCLIENT
                    maskDC = CreateCompatibleDC(hdc);   // DC that holds monochrome mask
    
                HBITMAP memBmp,  // default tree's paint
                    finalBmp,    // parent's background image
                    maskBmp,      // monochrome mask
                    bmpOld, bmpOldFinal, bmpOldMask;  // needed for cleanup
    
                memBmp = CreateCompatibleBitmap( hdc, rcClient.right - rcClient.left,
                    rcClient.bottom - rcClient.top );
    
                bmpOld = (HBITMAP)SelectObject( memDC, memBmp );
    
                /****** get parent's background image *******/
    
                finalBmp = CreateCompatibleBitmap( hdc, rcParent.right - rcParent.left,
                    rcParent.bottom - rcParent.top );
    
                bmpOldFinal = (HBITMAP)SelectObject( finalDC, finalBmp );
    
                ::SendMessage( GetParent(hwnd), WM_PRINTCLIENT,(WPARAM)finalDC,
                    (LPARAM)(PRF_CLIENT) );
    
                /****** capture default tree image *********/
    
                ::SendMessage( hwnd, WM_PRINTCLIENT,(WPARAM)memDC, (LPARAM)(PRF_CLIENT) );
    
                /********** create monochrome mask *******/
    
                // get tree's background color 
    
                COLORREF clrMask = TreeView_GetBkColor(hwnd);
    
                if( clrMask == -1 )
                    clrMask = ::GetSysColor(COLOR_WINDOW);
    
                maskBmp = CreateBitmap( rcClient.right - rcClient.left,
                   rcClient.bottom - rcClient.top, 1, 1, NULL );
    
                bmpOldMask = (HBITMAP)SelectObject( maskDC, maskBmp );
    
                SetBkColor( memDC, clrMask ); // this color becomes white, all others black
    
                BitBlt( maskDC, 0, 0, rcClient.right - rcClient.left, 
                    rcClient.bottom - rcClient.top, memDC, 0, 0, SRCCOPY );
    
                /***** map tree's coordinates to parent window *****/
    
                POINT ptTreeUL;
    
                ptTreeUL.x = rcClient.left;
                ptTreeUL.y = rcClient.top;
    
                ClientToScreen( hwnd, &ptTreeUL );
                ScreenToClient( GetParent(hwnd), &ptTreeUL );
    
                /***** creating transparent background ********/
    
                // mask the original image
    
                SetBkColor( memDC, RGB( 0, 0, 0 ) ); 
                SetTextColor( memDC, RGB( 255, 255, 255 ) );
    
                BitBlt( memDC, 0, 0, 
                    rcClient.right - rcClient.left, 
                    rcClient.bottom - rcClient.top,
                    maskDC, 0, 0, SRCAND );
    
                // create transparent treeview image 
    
                SetBkColor( finalDC, RGB ( 255, 255, 255 ) ); 
                SetTextColor( finalDC, RGB ( 0, 0, 0 ) );
    
                BitBlt( finalDC, ptTreeUL.x, ptTreeUL.y, 
                    rcClient.right - rcClient.left, 
                    rcClient.bottom - rcClient.top,
                    maskDC, 0, 0, SRCAND );
    
                BitBlt( finalDC, ptTreeUL.x, ptTreeUL.y, 
                    rcClient.right - rcClient.left, 
                    rcClient.bottom - rcClient.top,
                    memDC, 0, 0, SRCPAINT );
    
                // display the result 
    
                BitBlt( hdc, 0, 0, 
                    rcClient.right - rcClient.left, 
                    rcClient.bottom - rcClient.top,
                    finalDC, ptTreeUL.x, ptTreeUL.y, SRCCOPY );
    
                /***************** cleanup ******************/
    
                SelectObject( memDC, bmpOld );
                DeleteDC( memDC );
                DeleteObject( memBmp );
    
                SelectObject( finalDC, bmpOldFinal );
                DeleteDC( finalDC );
                DeleteObject( finalBmp );
    
                SelectObject( maskDC, bmpOldMask );
                DeleteDC( maskDC );
                DeleteObject( maskBmp );
    
                EndPaint( hwnd, &ps );
            }
            return 0L;
        case WM_ERASEBKGND:
            return TRUE;
        case WM_NCDESTROY:
            ::RemoveWindowSubclass( hwnd, TreeProc, 0 );
            return ::DefSubclassProc( hwnd, message, wParam, lParam);
        }
        return ::DefSubclassProc( hwnd, message, wParam, lParam);
    }
    

    在父窗口中绘制来自WM_PAINT的图片以回复WM_PRINTCLIENT(请记住使用(HDC)wParam代替BeginPaint,并使用return (LRESULT)0;)。我画了一个渐变,如上图所示。

    在父窗口中,您必须在WM_NOTIFY处理程序中添加以下内容:

    case WM_NOTIFY:
        {
            if( ((LPNMHDR)lParam)->code == TVN_SELCHANGING )
            {
                InvalidateRect( ((LPNMHDR)lParam)->hwndFrom, NULL, FALSE );
                break;
            }
    
            if( ((LPNMHDR)lParam)->code == TVN_ITEMEXPANDING )
            {
                InvalidateRect( ((LPNMHDR)lParam)->hwndFrom, NULL, FALSE );
                break;
            }
        }
        break;
    

    只有字体仍有待修复。

    编辑结束

1 个答案:

答案 0 :(得分:3)

在Windows 7上,使用消除锯齿对文本进行平滑处理。 Windows不会在纯色背景上绘制黑色文本的像素,它会在黑色和背景颜色之间进行混合。

TransparentBlt将单一纯色视为透明色。因此,它不会将文本的抗锯齿边缘视为透明,因此这些边缘在最终位图中可见。

要解决此问题,您可以选择禁用了抗锯齿的字体,但显然这会为您提供更多块状文本。

XP上的问题是复选框的角落与背景颜色相同。您可以通过将背景更改为不会发生碰撞的颜色(例如品红色)来解决此问题。