双缓冲(Double Buffer)原理和使用【转】

转自:http://blog.csdn.net/acs713/article/details/16359551

原文出自:http://blog.csdn.net/xiaohui_hubei/article/details/16319249

一、双缓冲作用

     

     双缓冲甚至是多缓冲,在许多情况下都很有用。一般需要使用双缓冲区的地方都是由于“生产者”和“消费者”供需不一致所造成的。这样的情况在很多地方后可能会发生,使用多缓冲可以很好的解决。我举几个常见的例子:

    例 1. 在网络传输过程中数据的接收,有时可能数据来的太快来不及接收导致数据丢失。这是由于“发送者”和“接收者”速度不一致所致,在他们之间安排一个或多个缓冲区来存放来不及接收的数据,让速度较慢的“接收者”可以慢慢地取完数据不至于丢失。

     例2. 再如,计算机中的三级缓存结构:外存(硬盘)、内存、高速缓存(介于CPU和内存之间,可能由多级)。从左到右他们的存储容量不断减小,但速度不断提升,当然价格也是越来越贵。作为“生产者”的 CPU 处理速度很快,而内存存取速度相对CPU较慢,如果直接在内存中存取数据,他们的速度不一致会导致 CPU  能力下降。因此在他们之间又增加的高速缓存来作为缓冲区平衡二者速度上的差异。

     例3. 在图形图像显示过程中,计算机从显示缓冲区取数据然后显示,很多图形的操作都很复杂需要大量的计算,很难访问一次显示缓冲区就能写入待显示的完整图形数据,通常需要多次访问显示缓冲区,每次访问时写入最新计算的图形数据。而这样造成的后果是一个需要复杂计算的图形,你看到的效果可能是一部分一部分地显示出来的,造成很大的闪烁不连贯。而使用双缓冲,可以使你先将计算的中间结果存放在另一个缓冲区中,但全部的计算结束,该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区。

      例1 中使用双缓冲是为了防止数据丢失,例2 中使用双缓冲是为了提高 CPU 的处理效率,而例3使用双缓冲是为了防止显示图形时的闪烁延迟等不良体验。

二、双缓冲原理

     这里,主要以双缓冲在图形图像显示中的应用做说明。  

     

    上面例3中提到了双缓冲的主要原理,这里通过一个图再次理解一下:

    图 1  双缓冲示意图

    注意,显示缓冲区是和显示器一起的,显示器只负责从显示缓冲区取数据显示。我们通常所说的在显示器上画一条直线,其实就是往该显示缓冲区中写入数据。显示器通过不断的刷新(从显示缓冲区取数据),从而使显示缓冲区中数据的改变及时的反映到显示器上。

     这也是显示复杂图形时造成闪烁的原因,比如你现在要显示从屏幕中心向外发射的一簇射线,你开始编写代码用一个循环从0度开始到360度,每隔一定角度画一条从圆心开始向外的直线。你每次画线其实是往显示缓冲区写入数据,如果你还没有画完,显示器就从显示缓冲区取数据显示图形,此时你看到的是一个不完整的图形,然后你继续画线,等到显示器再次取显示缓冲区数据显示时,图形比上次完整了一些,依次下去直到显示完整的图形。你看到图形不是一次性完整地显示出来,而是每次显示一部分,从而造成闪烁。

     原理懂了,看下 demo 就知道怎么用了。下面先介绍 Win32 API 和 C# 中如何使用双缓冲,其他环境下由于没有用到所以没写,等用到了再在下面补充,不过其他环境下过程也基本相似。

三、双缓冲使用 (Win32 版本)

    

[cpp] view plaincopyprint?

  1. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.     HDC hDC, hDCMem;  
  4.     HBITMAP hBmpMem, hPreBmp;  
  5.     switch (message)  
  6.     {  
  7.     case WM_PAINT:  
  8.         hDC = BeginPaint(hWnd, &ps);  
  9.           
  10.         /* 创建双缓冲区 */  
  11.         // 创建与当前DC兼容的内存DC  
  12.         hDCMem = CreateCompatibleDC(hDC);         
  13.         // 创建一块指定大小的位图  
  14.         hBmpMem = CreateCompatibleBitmap(hDC, rect.right, rect.bottom);       
  15.         // 将该位图选入到内存DC中,默认是全黑色的  
  16.         hPreBmp = SelectObject(hDCMem, hMemBmp);      
  17.           
  18.         /* 在双缓冲中绘图 */  
  19.         // 加载背景位图  
  20.         hBkBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));     
  21.         hBrush = CreatePatternBrush(hBkBmp);  
  22.         GetClientRect(hWnd, &rect);  
  23.         FillRect(hDCMem, &rect, hBrush);  
  24.         DeleteObject(hBrush);  
  25.           
  26.         /* 将双缓冲区图像复制到显示缓冲区 */  
  27.         BitBlt(hDC, 0, 0, rect.right, rect.bottom, hDCMem, 0, 0, SRCCOPY);  
  28.           
  29.         /* 释放资源 */  
  30.         SelectObject(hDCMem, hPreBmp);  
  31.         DeleteObject(hMemBmp);  
  32.         DeleteDC(hDCMem);  
  33.         EndPaint(hWnd, &ps);  
  34.         break;  
  35.     }  
  36. }  

[cpp] view plain copy print?

  1. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.     HDC hDC, hDCMem;  
  4.     HBITMAP hBmpMem, hPreBmp;  
  5.     switch (message)  
  6.     {  
  7.     case WM_PAINT:  
  8.         hDC = BeginPaint(hWnd, &ps);  
  9.           
  10.         /* 创建双缓冲区 */  
  11.         // 创建与当前DC兼容的内存DC  
  12.         hDCMem = CreateCompatibleDC(hDC);         
  13.         // 创建一块指定大小的位图  
  14.         hBmpMem = CreateCompatibleBitmap(hDC, rect.right, rect.bottom);       
  15.         // 将该位图选入到内存DC中,默认是全黑色的  
  16.         hPreBmp = SelectObject(hDCMem, hMemBmp);      
  17.           
  18.         /* 在双缓冲中绘图 */  
  19.         // 加载背景位图  
  20.         hBkBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));     
  21.         hBrush = CreatePatternBrush(hBkBmp);  
  22.         GetClientRect(hWnd, &rect);  
  23.         FillRect(hDCMem, &rect, hBrush);  
  24.         DeleteObject(hBrush);  
  25.           
  26.         /* 将双缓冲区图像复制到显示缓冲区 */  
  27.         BitBlt(hDC, 0, 0, rect.right, rect.bottom, hDCMem, 0, 0, SRCCOPY);  
  28.           
  29.         /* 释放资源 */  
  30.         SelectObject(hDCMem, hPreBmp);  
  31.         DeleteObject(hMemBmp);  
  32.         DeleteDC(hDCMem);  
  33.         EndPaint(hWnd, &ps);  
  34.         break;  
  35.     }  
  36. }  

    使用 Win32 版本时注意释放资源,释放顺序与创建顺序相反。我在使用过程中不小心遗漏了一句上面的 "DeleteObject(hMemBmp);"导致图形显示一段时间后就卡死了,查看内存使用发现内存随时间推移飙升,加上上面这句代码后,就没这个问题了。这也再次提醒我们释放资源是多么重要,成对编程的习惯是多么重要。

图 2  处理几次WM_PAINT消息后内存变化图

     在使用过程中,如果想更新使用双缓冲区显示的区域,可以使用 InvalidateRect(hWnd, &rect, FALSE); ,这里要注意第三个参数一定要设置成 FALSE ,第三个参数表示更新第二个参数指定的区域时是否擦除背景,因为使用双缓冲技术时是直接复制整个缓冲区数据到显示缓冲区,因此无论原有缓冲区里面有什么都会被覆盖,因此第三个参数设置成 
FALSE 有助于提高新能。更主要的原因是,如果先擦除原有缓冲区,会导致中间有一瞬间显示缓冲区被清空(显示为默认背景色),然后等到复制了双缓冲区的数据后再显示新的图像,这将导致闪烁!这与使用双缓冲的本意相违背,所以要注意这一点。

四、双缓冲使用 (C# 版本)

[csharp] view plaincopyprint?

  1. public void Show(System.Windows.Forms.Control control)  
  2. {  
  3.     Graphics gc = control.CreateGraphics();  
  4.     // 创建缓冲图形上下文 (类似 Win32 中的CreateCompatibleDC)  
  5.     BufferedGraphicsContext dc = new BufferedGraphicsContext();   
  6.     // 创建指定大小缓冲区 (类似 Win32 中的 CreateCompatibleBitmap)  
  7.     BufferedGraphics backBuffer = dc.Allocate(gc, new Rectangle(new Point(0, 0), control.Size));       
  8.               
  9.     /* 像使用一般的 Graphics 一样绘图 */  
  10.     Pen pen = new Pen(Color.Gray);  
  11.     foreach (Step s in m_steps)  
  12.     {  
  13.         gc.DrawLine(pen, s.Start, s.End);  
  14.     }  
  15.       
  16.     // 将双缓冲区中的图形渲染到指定画布上 (类似 Win32 中的)BitBlt  
  17.     backBuffer.Render(control.CreateGraphics());     
  18. }  

[csharp] view plain copy print?

  1. public void Show(System.Windows.Forms.Control control)  
  2. {  
  3.     Graphics gc = control.CreateGraphics();  
  4.     // 创建缓冲图形上下文 (类似 Win32 中的CreateCompatibleDC)  
  5.     BufferedGraphicsContext dc = new BufferedGraphicsContext();   
  6.     // 创建指定大小缓冲区 (类似 Win32 中的 CreateCompatibleBitmap)  
  7.     BufferedGraphics backBuffer = dc.Allocate(gc, new Rectangle(new Point(0, 0), control.Size));       
  8.               
  9.     /* 像使用一般的 Graphics 一样绘图 */  
  10.     Pen pen = new Pen(Color.Gray);  
  11.     foreach (Step s in m_steps)  
  12.     {  
  13.         gc.DrawLine(pen, s.Start, s.End);  
  14.     }  
  15.       
  16.     // 将双缓冲区中的图形渲染到指定画布上 (类似 Win32 中的)BitBlt  
  17.     backBuffer.Render(control.CreateGraphics());     
  18. }  

其他版本后续用到时再补充。

参考资料:

文中用到的 Win32 API 在MSDN中的说明:

CreateCompatibleDC

CreateCompatibleBitmap 

BitBlt

C# 中使用double buffer 

http://msdn.microsoft.com/en-us/library/ms229622(v=vs.110).aspx

时间: 2024-08-29 05:50:41

双缓冲(Double Buffer)原理和使用【转】的相关文章

关于j2me game双缓冲实现探讨

双缓冲技术的应用很广泛,设计游戏的时候更是需要它. 在midp1.0中,api中并没有game这个包,看到网上很多人在讨论设计游戏的时候会出现图片断裂,屏幕闪烁等问题. 我经过这几天的学习整理下自己的学习心得,用来抛砖,希望对此有研究高手们相互讨论.让我也学习学习. 双缓冲的原理可以这样形象的理解:把电脑屏幕看作一块黑板.首先我们在内存环境中建立一个"虚拟"的黑板,然后在这块黑板上绘制复杂的图形,等图形全部绘制完毕的时候,再一次性的把内存中绘制好的图形"拷贝"到另一

实例解说双缓冲

本文配套源码 昨天在论坛上,有人问起双缓冲的实现问题,想起网上这方面资料比较凌乱,而且多是DirectX相关的,今天特地在这里给大家简要的介绍一下双缓冲技术及其在VC++的GDI绘图环境下的实现. 1.Windows绘图原理 我们在Windows环境下看到各种元素,如菜单.按钮.窗口.图像,从根本上说,都是"画"出来的.这时的屏幕,就相当于一块黑板,而Windows下的各种GDI要素,如画笔.画刷等,就相当于彩色粉笔了.我们在黑板上手工画图时,是一笔一划的,电脑亦然.只不过电脑的速度比

java的双缓冲技术

Java的强大特性让其在游戏编程和多媒体动画处理方面也毫不逊色.在Java游戏编程和动画编程中最常见的就是对于屏幕闪烁的处理.本文从J2SE的一个再现了屏幕闪烁的Java Appilication简单动画实例展开,对屏幕闪烁的原因进行了分析,找出了闪烁成因的关键:update(Graphics g)函数对于前端屏幕的清屏.由此引出消除闪烁的方法--双缓冲.双缓冲是计算机动画处理中的传统技术,在用其他语言编程时也可以实现.本文从实例出发,着重介绍了用双缓冲消除闪烁的原理以及双缓冲在Java中的两种

java-关于JAVA绘图双缓冲技术

问题描述 关于JAVA绘图双缓冲技术 package two; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.Rectangle; import javax.swing.JFrame; public class G1P2 extends JFrame{ final int WIDTH = 900,HEIGHT = 650; double p1Speed = 0.5,p

双缓冲解决VC++绘图时屏幕闪烁_C 语言

通常来说程序根据需要调用Invalidate(FALSE)使窗口客户区无效引起重绘,然后在窗口OnPaint函数(基于文档视图的程序则是OnDraw)中进行稳定绘图就行了.但是,我们在OnPaint中进行多重绘制(画背景.棋盘.棋子等),前后绘制的反差造成了闪烁现象.以前知道Java中解决屏幕闪烁问题是用双缓冲的方法,现在发现在vc++中也是可以这么做的.简单来说,双缓冲就是先把需要绘制的东西全部一口气画在内存中,最后把内存中的数据搬到屏幕上显示. 最近做中国象棋,绘制界面时遇到些问题,绘图过程

Visual C++中实现双缓冲的基本原理

双缓冲的原理可以这样形象的理解:把电脑屏幕看作一块黑板.首先我们在内存环境中建立一个"虚拟"的黑板,然后在这块黑板上绘制复杂的图形,等图形全部绘制完毕的时候,再一次性的把内存中绘制好的图形"拷贝"到另一块黑板(屏幕)上.采取这种方法可以提高绘图速度,极大的改善绘图效果. 例如在OnDraw()函数中可以如下所述实现双缓冲,其主要步骤分为四步:       CPen Pen;       Pen.CreatePen(PS_INSIDEFRAME,1,RGB(225,2

c++- 我用VS2010的GDI+写了双缓冲为什么还是画面抖动?

问题描述 我用VS2010的GDI+写了双缓冲为什么还是画面抖动? public: Graphics *gg; Graphics g=dc.GetSafeHdc(); gg->DrawImage(bk,0,0); gg->DrawImage(zj,x,y,0+frame*100,0,100,120,UnitPixel); g.DrawImage(&bb,0,0); 解决方案 Graphics g=dc.GetSafeHdc();此处的dc是兼容dc吗?若不是,建议使用兼容dc. 解决方

C#绘图双缓冲技术总结

GDI+的双缓冲问题终于搞定了, 真是松了一口气!一直以来的误区:.net1.1 和 .net 2.0 在处理控件双缓冲上是有区别的..net 1.1 中,使用:this.SetStyle(ControlStyles.DoubleBuffer, true); .net 2.0中,使用:this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);怪不说老是提示参数无效,一直也不知道是这个问题,呵呵 要知道,图元无闪烁的实现和图元的绘制方法没有多

VC6下的GDI+双缓冲

一.概述 这段时间在研究GDI+双缓冲的实现方法,在网上花了很多时间都没有找到合适的示例,特别是针对VC6的.后来通过对网上资料的分析,和对SDK/MFC的学习,实现了VC6下的GDI+双缓冲,把它写出来与大家分享,希望可以找到更好的实现方法. GDI+的一个优点就是可以直接使用jpg图像,在这个示例中,我没有把图像文件放在资源中,而是动态读取,一是由于项目需求,图像数目是未知的:二是由于动态读文件效率低下,使用双缓冲更能体现出优势. 二.分析 在实现过程中,犯了一个错误,把读图像的方法放在了O