VC双缓冲绘图技术介绍

原创 2015年07月10日 10:52:22

    双缓冲绘图,它是一种基本的图形图像绘图技术。首先,它在内存中创建一个与屏幕绘图区域一致的对象,然后将图形绘制到内存中的这个对象上,最后把这个对象上的图形数据一次性地拷贝并显示到屏幕上。这种技术能够大大地提高绘图的速度,减少卡顿和闪屏的问题。

Tip:
去看看吧!1.
我们为什么要使用双缓冲技术来进行绘图?
    在应用程序开发中,当图像信息数据量很大时,绘图可能需要几秒钟甚至更长的时间,这时,应用程序可能会出现卡顿的现象。另外,如果窗体在响应WM_PAINT消息的同时也要进行复杂的图形处理,那么窗体在重绘时就会由于频繁的刷新而引起闪烁现象,而使用双缓冲技术就能有效地解决以上问题。

闪烁问题:
    窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后再调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是我们就看到了闪烁现象。
    如果只是仅仅去掉背景色的填充,的确无论怎样重绘图形都不会闪了。但是那样的话,窗体画面往往会变的乱七八糟。因为每次绘制图象的时候都没有将原来的图象清除,造成了图象的残留,叠加了新图形。所以单纯的禁止背景重绘是不够的,我们还要进行重新绘图。绘图函数可使用 BitBlt,它支持图形块的复制,速度很快。

双缓冲绘图实现方式:
    首先把要显示的图形先在内存中绘制好,然后再一次性地将内存中的图形一个点一个点地覆盖到屏幕上去(这个过程非常快,因为是非常完整的内存拷贝),以至于用背景色擦除界面后再贴图到屏幕上也不会闪烁了。
    步骤:

  1. 在内存中创建与画布一致的缓冲区;
  2. 在缓冲区绘图;
  3. 将缓冲区位图拷贝到当前画布并显示到屏幕上;
  4. 释放内存缓冲区。

流程图:
双缓冲绘图流程图

代码实现:
    这里在VC++开发平台使用MFC开发,一般在OnDraw和OnPaint函数中进行图像绘制。
    首先屏蔽背景刷新,背景刷新其实是在响应WM_ERASEBKGND消息,我们只要把OnEraseBkgnd函数返回值改为TRUE就行了。

//`直接绘图`,这里重绘会出现卡顿现象
void CDoubleBufferDlg::DrawItem(CDC* pDC)
{
    ASSERT_VALID(pDC);

    CRect rcClient;   
    pDC->GetClipBox(rcClient);  

    CPen pen(PS_SOLID, 1, RGB(178,178,178));
    CPen* pOldPen = NULL;

    pOldPen = pDC->SelectObject(&pen);

    pDC->Ellipse(rcClient);

    for(int j=0;j<1000;j++)  
    {
        pDC->MoveTo(0,0);
        pDC->LineTo(rcClient.Width(),rcClient.Height());

        pDC->MoveTo(rcClient.Width(),0);
        pDC->LineTo(0,rcClient.Height());
    }

    //绘图完成后的清理     
    pDC->SelectObject(pOldPen); 
    pOldPen->DeleteObject();
}
//`双缓冲绘图`
void CDoubleBufferDlg::DrawItemWithDoubleBuffer(CDC* pDC)
{
    ASSERT_VALID(pDC);

    CRect rcClient;
    pDC->GetClipBox(rcClient);  

    // 定义一个内存显示设备上下文对象
    CDC MemDC; 

    // 定义一个GDI位图对象
    CBitmap MemBitmap;

    // 创建一个与指定设备(这里是屏幕)兼容的内存设备上下文环境(DC)         
    MemDC.CreateCompatibleDC(pDC);  

    // 建立一个与屏幕显示兼容的位图,位图的大小可选用窗口客户区的大小  
    MemBitmap.CreateCompatibleBitmap(pDC, rcClient.Width(), rcClient.Height()); 

    // 将位图对象选入到内存显示设备上下文中,只有选择了才能进行绘图      
    CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap);         

    // 先用白色背景色将位图清除干净,否则是黑色。  
    MemDC.FillSolidRect(0, 0, rcClient.Width(), rcClient.Height(), RGB(255,255,255)); 

    // 定义画笔,颜色为灰色
    CPen pen(PS_SOLID, 1, RGB(178,178,178));
    CPen* pOldPen = NULL;

    // 把画笔对象选定到指定的设备上下文环境中
    pOldPen = MemDC.SelectObject(&pen);

    //-----------------------------------------绘图操作
    // 需放在BitBlt函数前

    // 画椭圆
    MemDC.Ellipse(rcClient);

    // 画对角线,循环次数多,没有双缓冲会卡顿
    for(int i=0;i<1000;i++)  
    {
        MemDC.MoveTo(0,0);
        MemDC.LineTo(rcClient.Width(), rcClient.Height());

        MemDC.MoveTo(rcClient.Width(), 0);
        MemDC.LineTo(0, rcClient.Height());
    }
    //-----------------------------------------绘图操作

    // 将内存中的图拷贝到屏幕上进行显示   
    pDC->BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &MemDC, 0, 0, SRCCOPY);    

    // 绘图完成后的清理     
    MemDC.SelectObject(pOldPen);
    MemDC.SelectObject(pOldBit);  

//使用GetDC()要用ReleaseDC
    ::ReleaseDC(this->m_hWnd, MemDC);
    pOldPen->DeleteObject();
    MemBitmap.DeleteObject(); 
}

效果图:
双缓冲绘图

知识概念(摘自百度百科):
GDI
    在Windows操作系统下,绝大多数具备图形界面的应用程序都离不开GDI,我们利用GDI所提供的众多函数就可以方便的在屏幕、打印机及其它输出设备上输出图形,文本等操作。那我们GDI到底是什么呢?
    GDI是Graphics Device Interface的缩写,含义是图形设备接口,它的主要任务是负责系统与绘图程序之间的信息交换,处理所有Windows程序的图形输出。它的出现使程序员无需要关心硬件设备及设备驱动,就可以将应用程序的输出转化为硬件设备上的输出,实现了程序开发者与硬件设备的隔离,大大方便了开发工作。GDI有以下几个特点:

  1. 不允许程序直接访问物理显示硬件,通过称为“设备环境(DC)”的抽象接口间接访问显示硬件;
  2. 程序需要与显示硬件(显示器、打印机等) 进行通讯时,必须首先获得与特定窗口相关联的设备环境;
  3. 用户无需关心具体的物理设备类型;
  4. Windows参考设备环境的数据结构完成数据的输出。

    以上的设备环境,就是我们的DC(device context)。

DC(device context)
    在Windows环境中,各程序的输出必须限制在自己的窗口中。
    GDI用一种简单的机制保证在窗口中画图的各程序遵循这个规则。这种机制即为设备描述表(DC);当Windows程序在屏幕、打印机或其它设备上画图时,它并不是将像素直接输出到设备上,而是将图绘制到由设备描述表表示的逻辑意义上的”显示平面”上去。设备描述表是深寓于Windows中的一种数据结构,它包含GDI需要的所有关于显示平面情况的描述字段,包括相连的物理设备和各种各样的状态信息。
    设备描述表,又称为设备上下文,或者设备环境,它是一个定义一组图形对象(画笔等等)及其属性、影响输出的图形方式(数据)结构。windows提供设备描述表,用于应用程序和物理设备之间进行交互,从而提供了应用程序设计的平台无关性。
    设备描述表是一种数据结构,它包括了一个设备(如显示器和打印机)的绘制属性相关的信息。所有的绘制操作通过设备描述表进行。设备描述表与大多 WIN32结构不同,应用程序不能直接访问设备描述表,只能由各种相关API函数通过设备描述表的句柄间接访问该结构。
    设备描述表总是与某种系统硬件设备相关。比如屏幕设备描述表与显示设备相关,打印机设备描述表与打印设备相关等等。
    屏幕设备描述表,一般我们简单地称其为设备描述表。它与显示设备具有一定的对应关系,在windows GDI界面下,它总是相关于某个窗口或这窗口上的某个显示区域。通常意义上窗口的设备描述表,一般指的是窗口的客户区,不包括标题栏、菜单栏所占有的区域,而对于整个窗口来说,其设备描述表严格意义上来讲应该称为窗口设备描述表,它包含窗口的全部显示区域。二者的操作方法完全一致,所不同的仅仅是可操作的范围不同而已。
    windows 窗口一旦创建,它就自动地产生了与之相对应的设备描述表数据结构,用户可运用该结构,实现对窗口显示区域的GDI操作,如划线、写文本、绘制位图、填充等,并且所有这些操作均要通过设备描述表句柄来进行。


  1. 这是第一次用 Markdown博客,故借用脚注纪念一下吧,哈哈!
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

MFC VC 双缓冲绘图基本原理与实现,详细解释

MFC VC 双缓冲绘图基本原理与实现,详细解释 MFC做了一些时间了,不得不面对 的是在界面上画图的。 当然你可以直接搜索到能用的代码,并且基本能满足要求。不过这样总不是学习的态度。本着学习...

双缓冲技术绘图

之前在做图形绘制的时候,发现在拖动图形时候,会出现闪烁的情况,后来就上网找了一下shuanghuan...

双缓冲区绘图操作的实现

在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲...

绘图(四,view之绘图双缓冲)

前言以下双缓冲的一些定义均是引用其他作者,不好意思,因为自己还没想出比较好的定义去描述双缓冲,同时也会引用一下其他作者的代码。关键最重要的是,我不认为,写别人已经写过的技术博客,是没有用的,也许对别人...

[MFC]OnPaint双缓冲绘图

void CXButton::OnPaint() { CRect PaintRect; CPaintDC dc(this); CDC MemDC; CBitmap MemBitmap; /...
  • Sidyhe
  • Sidyhe
  • 2015年09月16日 17:43
  • 1910

MFC中的GDI/GDI+和双缓冲绘图

1、GDI概述   GDI在全称是Graphics Device Interface,即图形设备接口。是图形显示与实际物理设备之间的桥梁。         GDI使得用户无需关心具体设备的细节,...

MFC 双缓冲 GDI+ Graphics 在图片上画图写字 避免闪烁

缘起:需要在MFC Static控件上显示持续变化的数据,控件背景为某类型图片的一部分。 之前...

MFC 双缓冲 画图

首先说明作图时,会闪烁的原因:  我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,总是先用背景色将...
  • zhoxier
  • zhoxier
  • 2012年05月07日 14:56
  • 6638

MFC中重写OnPaint实现双缓冲绘图

在VC/MFC用CDC绘图时,频繁的刷新,屏幕会出现闪烁的现象,CPU时间占用率相当高,绘图效率极低,很容易出现程序崩溃。在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM...
  • zollll
  • zollll
  • 2017年02月05日 14:14
  • 772

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

MFC绘制动态曲线,用双缓冲绘图技术防闪烁   转自 http://zhy1987819.blog.163.com/blog/static/841427882011614103454335/ ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:VC双缓冲绘图技术介绍
举报原因:
原因补充:

(最多只允许输入30个字)