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

---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

双缓冲绘图和窗口控件的绘制——ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 .的更多相关文章

  1. C#-gdi绘图,双缓冲绘图,Paint事件的触发

    一. 画面闪烁问题与双缓冲技术 1.1 导致画面闪烁的关键原因分析: 1  绘制窗口由于大小位置状态改变进行重绘操作时 绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面 ...

  2. GDI双缓冲绘图

    一.简介 在进行复杂图形绘制时,若直接在屏幕DC上进行绘制,则会出现明显的闪烁.闪烁产生的原因是当绘制的图形较为 复杂时,图形绘制过程中就被刷新到屏幕上,导致结果断断续续地显示出来.双缓冲绘图的原理是 ...

  3. MFC双缓冲绘图实例

    本人之前一直了解双缓冲绘图的基本原理,但是在研究很久之后才大概知道具体的使用过程,本文将详细介绍本人在实际项目中使用双缓冲绘图的案例. 实现功能:主界面显示某张包含人脸的图片,通过dlib detec ...

  4. mfc双缓冲绘图

    1.要求 在界面加载本地图片并显示,每过100ms改变一张图片显示 2.现象 通过定时器控制CImage,Load,Draw,Destroy,会非常的卡顿.因为Load图片时,会是非常大的数据[所有C ...

  5. MFC双缓冲绘图(2015.09.24)

    问题引入: 最近在尝试编写贪吃蛇游戏时遇到这么一个问题:当系统以较快频率向窗口发送WM_PAINT消息时,调用OnPaint()函数在窗口中绘制图形就会发生闪烁现象. 问题分析: 当我们把绘图过程放在 ...

  6. 陈灯WGF双缓冲绘图框架

    “木丸子童屋”,专售各类儿童玩具,价格优惠,请大家多多支持:http://shop65552598.taobao.com/ WGF(windows graphic foundation)为window ...

  7. 【MFC】MFC绘制动态曲线,用双缓冲绘图技术防闪烁

    摘自:http://zhy1987819.blog.163.com/blog/static/841427882011614103454335/ MFC绘制动态曲线,用双缓冲绘图技术防闪烁   2011 ...

  8. MFC--自己优化滚动条的双缓冲绘图方法

        2010-01-09 18:45 MFC--自己优化的双缓冲绘图方法 自己通过尝试,用修改视图坐标的方法, 优化了双缓冲绘图,实现起来并不复杂. 在介绍这个方法前,重新介绍一下窗口和视口的概念 ...

  9. [Qt2D绘图]-06QPainter的复合模式&&双缓冲绘图&&绘图中的其他问题

    本篇读书笔记主要记录QPainter的复合模式&&双缓冲绘图&&绘图中的其他问题   大纲:     复合模式     双缓冲绘图     绘图中的其他问题       ...

随机推荐

  1. STL之list(双向链表)

    一,概述 List将元素按顺序储存在链表中. 与向量(vectors)相比, 它允许快速的插入和删除,但是随机访问却比较慢. list 不仅是一个双向链表,而且是一个环状双向链表 二,使用 #incl ...

  2. [C++参考]私有成员变量的理解

    私有成员变量的概念,在脑海中的现象是,以private关键字声明,是类的实现部分,不对外公开,不能在对象外部访问对象的私有成员变量. 然而,在实现拷贝构造函数和赋值符函数时,在函数里利用对象直接访问了 ...

  3. PHP学习笔记9-生成图片

    用PHP代码在网页上生成图片 <?php /** * Created by PhpStorm. * User: Administrator * Date: 2015/6/29 * Time: 2 ...

  4. QT学习 之 三维饼图绘制

    QT里没有相应统计图形的绘制组件 只有手工自己画 效果如图 void aaq::paintEvent( QPaintEvent* ev ) { QPainter painter(this); // 去 ...

  5. Nginx Rewrite规则初探(转)

    Nginx  rewrite(nginx url地址重写)Rewrite 主要的功能就是实现URL的重写,Nginx的Rewrite规则采用Pcre,perl兼容正则表达式的语法规则匹配,如果需要Ng ...

  6. LINUX下使用crontab进行RMAN备份实验

    之前写了脚本,手动执行可以,使用crontab总是无法运行成功,今天下午花了两个小时实验,完成如下: 注意事项:脚本完成首先手动执行,确定可以正常执行. 在crontab中使用,要注意以下几点: 1. ...

  7. setuptools,easy_install使用

    当需要安装第三方python包时,可能会用到easy_install命令.easy_install是由PEAK(Python Enterprise Application Kit)开发的setupto ...

  8. 【操作系统】:Main features of the X86-64

    The combination of the new hardware supplied by Intel and AMD ,and the new versions of GCC targeting ...

  9. fstream,ifstream,ofstream 详解与用法

    fstream,istream,ofstream 三个类之间的继承关系 fstream :(fstream继承自istream和ofstream)1.typedef basic_fstream< ...

  10. 高级爬虫工程师(Spider)-美团网-拉勾网-最专业的互联网招聘平台

    高级爬虫工程师(Spider)-美团网-拉勾网-最专业的互联网招聘平台 高级爬虫工程师(Spider)