VC界面绘制双缓存

转载请注明原文网址:

http://www.cnblogs.com/xianyunhe/archive/2011/11/20/2255811.html

1、闪屏的问题
在GDI的绘图系统中,每调用一次区域绘图操作,如FillRect、BitBlt等,图形显示系统就会在屏幕中对指定的区域进行一次刷新操作。如果频繁的进行区域绘制操作的操作的话,我们就会发现,屏幕会出现闪屏。
使用下面的代码对闪屏的问题进行测试,在XP系统闪屏尤其严重,在Win7系统,闪屏问题有所改善。Win7系统在绘制效率上有所提升。

void CDoubleBufferView::DrawDirect(CDC* pDC)
{
    ASSERT_VALID(pDC);
 
    /*用渐变色粉刷背景*/
    CRect rect(0,0,0,0);
    CSize szTotal = GetTotalSize();
    for (int i = 0; i <= szTotal.cy; i++)
    {
        rect.left = 0;
        rect.right = szTotal.cx;
        rect.top = i*10;
        rect.bottom = (i+1)*10;
        pDC->FillSolidRect(&rect, RGB((i*10)%255,0,0));
    }
 
    /*绘制图片*/
    CDC dcPic;
    dcPic.CreateCompatibleDC(pDC);
    CBitmap bmpImport;
    bmpImport.LoadBitmap(IDB_BITMAP1);
    CBitmap* pOldBmp = dcPic.SelectObject(&bmpImport);
    BITMAP bitmap;
    bmpImport.GetBitmap(&bitmap);
    int iWidth = bitmap.bmWidth;
    int iHeight = bitmap.bmHeight;
    pDC->BitBlt(0, 0, iWidth, iHeight,
        &dcPic, 0, 0, SRCCOPY);
    dcPic.SelectObject(pOldBmp);
 
    /*释放资源*/
    bmpImport.DeleteObject();
    dcPic.DeleteDC();
}

2、双缓存
产生闪屏的原因是类似于多进程之间的通信问题,每次DC的绘图操作,都要把相关的显示数据发送到显卡,显卡处理后,在显示器上显示。借鉴提升多线程之间的通信效率的解决方法,可通过减少与显卡之间的交互次数来提升绘制的效率。这也就是双缓存的思路。双缓存的原理是先把更新操作中所有绘制数据先写入内存,然后再调用BitBlt或StretchBlt一次性的把所有数据发送到显卡中。
用一个比喻来说,没有用双缓存就像老师讲课时在黑板上使用粉笔写板书,学生能看到老师写板书的整个过程,如果把黑板看做是一个屏幕的话,老师在写板书的过程,就是一个闪屏的过程。
使用了双缓存,就像老师采用了多媒体教学,老师就可以提前在家把板书用PPT做好,上课时只要一页一页的翻PPT就可以了,学生们是看不到PPT制作的过程,也就会有闪屏的问题了。
可采用了如下代码来实现双缓存。

void CDoubleBufferView::DrawWithBufferInefficient(CDC* pDC)
{
    ASSERT_VALID(pDC);
 
    /*创建内存DC*/
    CDC dcMemory;
    dcMemory.CreateCompatibleDC(pDC);
    dcMemory.SetBkColor(pDC->GetBkColor());
     
    /*设置内存DC的画板,大小整个窗口一样*/
    CSize szTotal = GetTotalSize();
    CBitmap bmpMemory;
    bmpMemory.CreateCompatibleBitmap(pDC,
        szTotal.cx, szTotal.cy);
    dcMemory.SelectObject(&bmpMemory);
     
    /*设置内存DC的起始点*/
    dcMemory.SetViewportOrg(0, 0);
     
    /*粉刷背景*/
    dcMemory.FillSolidRect(0, 0, szTotal.cx, szTotal.cy, pDC->GetBkColor());
     
    DrawDirect(&dcMemory);
     
    /*把内存DC复制到输入DC中*/
    pDC->BitBlt(0, 0, szTotal.cx, szTotal.cy,
        &dcMemory, 0, 0, SRCCOPY);
     
    /*释放资源*/
    bmpMemory.DeleteObject();
    dcMemory.DeleteDC();
}

3、取消擦除背景
当我们使用了上面的双缓存技术后,我们看到闪屏问题有所缓解,但是有些操作仍然会导致闪屏,比如在有滚动条的视图窗口拖动滚动条时。为什么会产生这样的原因呢?先来分析一下界面的绘制原理。
当我们需要对窗口绘制时,可调用UpdateWindow、RedrawWindow、Invalidate或InvalidateRect等函数。通过查看这些函数的MSDN中得知,有些窗口绘制函数中,有一个是否擦除背景的选项。如果要擦除背景,那一次绘制就要进行两部操作,也就是要跟显卡交互两次,一是擦除背景,一是重新绘制图形,那双缓存的作用就失去了。
如果我们再绘制图形的时候,自己对背景进行一次粉刷,也就没有必要再使用擦除背景,同时也能保证双缓存的效果。
取消擦除北京的方法主要有两种:
(1)取消重绘时的擦除选项。
如使用Invalidate(FALSE)。
这种方法虽然有效,但是需要对所有的重绘函数进行处理,难以完全取消擦除背景。
(2)截断擦除消息。
背景的擦除是通过WM_ERASEBKGND消息来完成。于是,我们只要截获了该消息,就能彻底取消擦除背景。
可在窗口类中为WM_ERASEBKGND提供消息响应函数,然后直接返回TRUE。

BOOL CDoubleBufferView::OnEraseBkgnd(CDC* pDC)
{
    // TODO: Add your message handler code here and/or call default
     
    return TRUE;
    //return CScrollView::OnEraseBkgnd(pDC);
}

4、绘制效率的提升
在刷新界面的时候,刷新的区域越小,刷新效率更高,因此,在刷新界面的时候,我们应该尽量较少不必要的刷新。操作系统也会对界面的刷新操作进行优化,如拉动滚动条的时候,并不是对整个界面进行刷新,而只是对已经无效的区域中换上新的图形,然后再在屏幕调整图形区域在界面上的位置。因此,就有一个裁剪区域的概念,在重绘的过程中,只有裁剪区域需要重绘。因此,我们在双缓存中,也只需对裁剪区域重绘。可通过CDC::GetClipBox来获得裁剪区域的大小。
因此,对双缓存的优化代码如下所示:

void CDoubleBufferView::DrawWithBufferEfficient(CDC* pDC)
{
    ASSERT_VALID(pDC);
 
    /*创建内存DC*/
    CDC dcMemory;
    dcMemory.CreateCompatibleDC(pDC);
    dcMemory.SetBkColor(pDC->GetBkColor());
     
    /*设置内存DC的画板,大小与输入DC的裁剪区域一样*/
    /*只对裁剪区域进行重新绘制*/
    CRect rectClip(0,0,0,0);
    pDC->GetClipBox(&rectClip);
    CBitmap bmpMemory;
    bmpMemory.CreateCompatibleBitmap(pDC,
        rectClip.Width(), rectClip.Height());
    dcMemory.SelectObject(&bmpMemory);
     
    /*设置内存DC的起始点*/
    dcMemory.SetViewportOrg(-1*rectClip.left, -1*rectClip.top);
 
    /*粉刷背景*/
    dcMemory.FillSolidRect(&rectClip, pDC->GetBkColor());
 
    DrawDirect(&dcMemory);
 
    /*把内存DC复制到输入DC中*/
    pDC->BitBlt(rectClip.left, rectClip.top, rectClip.Width(), rectClip.Height(),
        &dcMemory, rectClip.left, rectClip.top, SRCCOPY);
     
    /*释放资源*/
    bmpMemory.DeleteObject();
    dcMemory.DeleteDC();
}

5、工程源代码下载

http://files.cnblogs.com/xianyunhe/DoubleBuffer.rar

【MFC】VC界面绘制双缓存的更多相关文章

  1. VC图形绘制双缓存的代码复用性讨论

    在前文中已经讨论了如何实现界面绘制双缓存的问题,前文网址如下: http://www.2cto.com/kf/201111/112429.html 双缓存的主要思路是:先把图形绘制到内存DC中,然后再 ...

  2. MFC/VC++ UI界面美化技术

    1.     工具: 1.1设备环境类: Windows下的绘图操作说到底就是DC操作.DC(Device Context设备环境)对象是一个抽象的作图环境,可能是对应屏幕,也可能是对应打印机或其它. ...

  3. C#使用双缓存减少界面闪烁

    场景:一个panel中动态加载多个自定义控件item,类似QQ聊天窗口 问题:加载panel时界面会卡顿,先显示阴影再显示界面:移动滚动条时item会闪烁 解决方法: panel 添加方法,减少界面闪 ...

  4. C++开发人脸性别识别教程(7)——搭建MFC框架之界面绘制

    在之前的博客中我们已经将项目中用到的算法表述完成,包含人脸检測算法以及四种性别识别算法,在这篇博客中我们将着手搭建主要的MFC框架. 一.框架概况 在这篇博文中我们将搭建最主要的MFC框架.绘制MFC ...

  5. MFC 双缓存绘图

    在SDI应用程序中,当我们需要时刻动态刷新界面的时候,如果我们一直使用,UpdateAllView()那么就会出现屏幕不停闪烁.闪屏非常严重,特别是一直在动态刷新的时候.并且在闪屏的过程中 我们根本就 ...

  6. MFC+OpenGL基础绘制<转>

    转载地址:https://blog.csdn.net/u013232740/article/details/47904115 ------------------------------------- ...

  7. 【MFC】MFC绘图不闪烁——双缓冲技术

    MFC绘图不闪烁——双缓冲技术[转] 2010-04-30 09:33:33|  分类: VC|举报|字号 订阅 [转自:http://blog.163.com/yuanlong_zheng@126/ ...

  8. Android图形显示系统——上层显示1:界面绘制大纲

    Android显示之应用界面绘制 越到上层,跟业务关联越直接.代码就越繁杂.Android上层显示的代码正是如此.此外,java语言本身繁复的特点(比C语言多了满屏的try-catch,比C++少了析 ...

  9. 【View层】界面绘制

    [引用]:http://www.jianshu.com/p/c5fc8c6b967a [View层]IOS纯代码绘制界面(一) 字数2303 阅读385 评论2 喜欢16 IOS开发中界面绘制占据了绝 ...

随机推荐

  1. Javascript中call()和apply()的用法 ----2

    前言 call 和 apply 都是为了改变某个函数运行时的 context 即上下文而存在的,换句话说,就是为了改变函数体内部 this 的指向.call 和 apply二者的作用完全一样,只是接受 ...

  2. jmeter常用插件安装

    转载:http://www.cnblogs.com/danqiu/p/6119156.html 下载地址:http://jmeter-plugins.org/downloads/all/ PerfMo ...

  3. 使用Python实现基于图像识别的iOS自动化测试

    相对于Android来说,iOS比较封闭.这一点,在设计和评估自动化测试方案的时候感觉尤其强烈.iOS平台上没有特别好用的自动化测试工具.苹果针对iOS提供了UI Automation的Instrum ...

  4. linux 挂在新硬盘

    记录一下    全忘了..... PS 测试服务器的主板太差劲了,没有多余的电源接口,只能把光驱的电源拿出来,才能让硬盘使用.把硬盘装好后,我们用 fdisk -l 查看下: 图中可以看出 /dev/ ...

  5. web API help pages with Swagger / OpenAPI

    https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetc ...

  6. 堆与堆排序、Top k 问题

     堆排序与快速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法.学习堆排序前,先讲解下什么是数据结构中的二叉堆. 二叉堆的定义 二叉堆是完全二叉树或者是近似完全二叉树. 二叉堆满 ...

  7. Floyd算法--一个人的旅行

    2017-07-27 22:37:32 writer:pprp 题目如下: 虽然草儿是个路痴(就是在杭电待了一年多,居然还会在校园里迷路的人,汗~),但是草儿仍然很喜欢旅行,因为在旅途中 会遇见很多人 ...

  8. Permutations,全排列

    问题描述:给定一个数组,数字中数字不重复,求所有全排列. 算法分析:可以用交换递归法,也可以用插入法. 递归法:例如,123,先把1和1交换,然后递归全排列2和3,然后再把1和1换回来.1和2交换,全 ...

  9. 子tab里面新增tab(top.jQuery)

    top.jQuery应用. var jq = top.jQuery; if (jq("#centre").tabs("exists", "预览页面&q ...

  10. scala学习手记29 - 偏应用函数

    调用函数可以说成是将函数应用于实参.如果传入所有的预期的参数,就完全应用了这个函数.如果只传入几个参数,就会得到一个偏应用函数. 偏应用函数是一个特殊的概念,在scala中它是使用val定义的,但是在 ...