• 双缓冲绘图和窗口控件的绘制——ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 .


    双缓冲绘图和窗口控件的绘制

    ---ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误

     

    cheungmine


    我们通常使用ATL COM组件,生成一个带窗口的ActiveX控件,然后希望在这个窗口中绘制我们的图像、图形等数据,然而ATL向导生成的代码中包含很多错误,下面是其自动向导生成的代码:

        HRESULT OnDraw(ATL_DRAWINFO& di)
        {
            RECT& rc = *(RECT*)di.prcBounds;
            // 将剪辑区域设置为 di.prcBounds 指定的矩形
            HRGN hRgnOld = NULL;
            if (GetClipRgn(di.hdcDraw, hRgnOld) != 1)
                hRgnOld = NULL;
            bool bSelectOldRgn = false;

            HRGN hRgnNew = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);

            if (hRgnNew != NULL)
            {
                bSelectOldRgn = (SelectClipRgn(di.hdcDraw, hRgnNew) != ERROR);
            }

            Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);
            SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE);
            LPCTSTR pszText = _T("ATL 8.0 : Canvas");
    #ifndef _WIN32_WCE
            TextOut(di.hdcDraw,
                (rc.left + rc.right) / 2,
                (rc.top + rc.bottom) / 2,
                pszText,
                lstrlen(pszText));
    #else
            ExtTextOut(di.hdcDraw,
                (rc.left + rc.right) / 2,
                (rc.top + rc.bottom) / 2,
                ETO_OPAQUE,
                NULL,
                pszText,
                ATL::lstrlen(pszText),
                NULL);
    #endif

            if (bSelectOldRgn)
                SelectClipRgn(di.hdcDraw, hRgnOld);

            return S_OK;
        }


    请注意这里面包含一个错误,改正之后的代码(红色字体):

        HRESULT OnDraw(ATL_DRAWINFO& di)
        {
            RECT& rc = *(RECT*)di.prcBounds;
            // 将剪辑区域设置为 di.prcBounds 指定的矩形
            HRGN hRgnOld = NULL;
            if (GetClipRgn(di.hdcDraw, hRgnOld) != 1)
                hRgnOld = NULL;
            bool bSelectOldRgn = false;

            HRGN hRgnNew = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);

            if (hRgnNew != NULL)
            {
                bSelectOldRgn = (SelectClipRgn(di.hdcDraw, hRgnNew) != ERROR);
            }

            Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);
            SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE);
            LPCTSTR pszText = _T("ATL 8.0 : Canvas");
    #ifndef _WIN32_WCE
            TextOut(di.hdcDraw,
                (rc.left + rc.right) / 2,
                (rc.top + rc.bottom) / 2,
                pszText,
                lstrlen(pszText));
    #else
            ExtTextOut(di.hdcDraw,
                (rc.left + rc.right) / 2,
                (rc.top + rc.bottom) / 2,
                ETO_OPAQUE,
                NULL,
                pszText,
                ATL::lstrlen(pszText),
                NULL);
    #endif


            if (bSelectOldRgn)
                SelectClipRgn(di.hdcDraw, hRgnOld);

     

            // 删除剪辑区域
            ::DeleteObject(hRgnNew);  // Add by cheungmine. MUST!!

     

            return S_OK;
        }

     

    注意其中绿色的代码,你应该完全注释掉这种绘制的逻辑,而采用双缓冲。因此,ATL自动生成的OnDraw代码是不适合实际的绘图控件的。下面的代码是我更改之后的,增加了双缓冲机制:

        void MyDrawCode (HDC hdc, RECT &rc)

        {

    Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
            SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE);
            LPCTSTR pszText = _T("ATL 8.0 : Canvas");
    #ifndef _WIN32_WCE
            TextOut(hdc,
                (rc.left + rc.right) / 2,
                (rc.top + rc.bottom) / 2,
                pszText,
                lstrlen(pszText));
    #else
            ExtTextOut(hdc,
                (rc.left + rc.right) / 2,
                (rc.top + rc.bottom) / 2,
                ETO_OPAQUE,
                NULL,
                pszText,
                ATL::lstrlen(pszText),
                NULL);
    #endif

        }


        void DbBufferDraw(HDC hdcDraw, RECT &rcClip)
        {
            HDC         hMemDC  = ::CreateCompatibleDC(hdcDraw);             
            ATLASSERT(hMemDC);       
            HBITMAP  hBmpNew = ::CreateCompatibleBitmap(hdcDraw, WidthRect(rcClip), HeightRect(rcClip));           
            ATLASSERT(hBmpNew);
            HBITMAP  hBmpOld = (HBITMAP) ::SelectObject(hMemDC, hBmpNew);

     

              // 添加自己的绘制代码

            MyDrawCode(hMemDC, rcClip);

            if (IsWindow()) {
                ::BitBlt ( hdcDraw,
                         rcClip.left,
                         rcClip.top,
                         WidthRect(rcClip), HeightRect(rcClip),
                         hMemDC, 
                         rcClip.left,
                         rcClip.top,
                       SRCCOPY );
            }
            else {
                ::BitBlt ( hdcDraw,
                         rcClip.left + m_rcPos.left,
                         rcClip.top  + m_rcPos.top,
                         WidthRect(rcClip), HeightRect(rcClip),
                         hMemDC, 
                         rcClip.left,
                         rcClip.top,
                       SRCCOPY );
            }

            // 释放 hMemDC
            ::SelectObject(hMemDC, hBmpOld);
            ::DeleteObject(hBmpNew);
            ::DeleteDC(hMemDC);
        }


        HRESULT OnDraw(ATL_DRAWINFO& di)
        {
            RECT& rc = *(RECT*)di.prcBounds;
            // 将剪辑区域设置为 di.prcBounds 指定的矩形
            HRGN hRgnOld = NULL;
            if (GetClipRgn(di.hdcDraw, hRgnOld) != 1)
                hRgnOld = NULL;
            bool bSelectOldRgn = false;

            HRGN hRgnNew = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);

            if (hRgnNew != NULL)
            {
                bSelectOldRgn = (SelectClipRgn(di.hdcDraw, hRgnNew) != ERROR);
            }

            // 双缓冲
         
    DbBufferDraw(di.hdcDraw, rc);


            if (bSelectOldRgn)
                SelectClipRgn(di.hdcDraw, hRgnOld);

            // 删除剪辑区域
            ::DeleteObject(hRgnNew);  // Add by cheungmine. MUST!!

            return S_OK;
        }

     

    按上面的修改,烦人的闪烁没了。另外,在OnDarw中,没必要把全部绘制代码放入 MyDrawCode 中。因为 MyDrawCode 如果执行时间较长,则 OnDraw会显得很慢。因此,光是双缓冲还不够,因为OnDraw被调用的时候,都是系统激发的,我们只需要把原来保存的绘制图片直接绘制到hMemDC中即可,也就是, MyDrawCode中不可以如本例所示的那样,放置实际绘制的代码,而是只把图片重新拷贝到hdc上即可,如:

        void MyDrawCode (HDC hdc, RECT &rc)

        {

            m_BkgndMap.CopyTo(hdc, rc);

        }

     

        m_BkgndMap 可以是自己实现的Image或CImage等图像类。

     

    因此,在一个基本的绘图系统中,至少需要3个缓冲层次:


    第一层:控件窗口HDC(无窗口控件也是存在HDC的)

    第二层:控件窗口HDC的兼容MemDC,即:HDC hMemDC  = ::CreateCompatibleDC(hdcDraw);

    第三层:后台图片HDC包装类: m_BkgndMap

     

    关于如何创建这样的ActiveX 窗口控,请看我的相关文章:

    http://blog.csdn.net/cheungmine/archive/2007/10/10/1818913.aspx

    from:http://blog.csdn.net/ubuntu64fan/article/details/5917979

  • 相关阅读:
    【转】Pandas学习笔记(七)plot画图
    【转】Pandas学习笔记(六)合并 merge
    【转】Pandas学习笔记(五)合并 concat
    【转】Pandas学习笔记(四)处理丢失值
    【转】Pandas学习笔记(三)修改&添加值
    【转】Pandas学习笔记(二)选择数据
    17秋 SDN课程 第二次上机作业
    17秋 SDN课程 第三次上机作业
    17秋 SDN课程 第一次上机作业
    Ubuntu 14.04 安装sublime
  • 原文地址:https://www.cnblogs.com/lidabo/p/3483118.html
Copyright © 2020-2023  润新知