转自: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 版本)
    
  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. // 创建与当前DC兼容的内存DC
  11. hDCMem = CreateCompatibleDC(hDC);
  12. // 创建一块指定大小的位图
  13. hBmpMem = CreateCompatibleBitmap(hDC, rect.right, rect.bottom);
  14. // 将该位图选入到内存DC中,默认是全黑色的
  15. hPreBmp = SelectObject(hDCMem, hMemBmp);
  16. /* 在双缓冲中绘图 */
  17. // 加载背景位图
  18. hBkBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
  19. hBrush = CreatePatternBrush(hBkBmp);
  20. GetClientRect(hWnd, &rect);
  21. FillRect(hDCMem, &rect, hBrush);
  22. DeleteObject(hBrush);
  23. /* 将双缓冲区图像复制到显示缓冲区 */
  24. BitBlt(hDC, 0, 0, rect.right, rect.bottom, hDCMem, 0, 0, SRCCOPY);
  25. /* 释放资源 */
  26. SelectObject(hDCMem, hPreBmp);
  27. DeleteObject(hMemBmp);
  28. DeleteDC(hDCMem);
  29. EndPaint(hWnd, &ps);
  30. break;
  31. }
  32. }
  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. // 创建与当前DC兼容的内存DC
  11. hDCMem = CreateCompatibleDC(hDC);
  12. // 创建一块指定大小的位图
  13. hBmpMem = CreateCompatibleBitmap(hDC, rect.right, rect.bottom);
  14. // 将该位图选入到内存DC中,默认是全黑色的
  15. hPreBmp = SelectObject(hDCMem, hMemBmp);
  16. /* 在双缓冲中绘图 */
  17. // 加载背景位图
  18. hBkBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
  19. hBrush = CreatePatternBrush(hBkBmp);
  20. GetClientRect(hWnd, &rect);
  21. FillRect(hDCMem, &rect, hBrush);
  22. DeleteObject(hBrush);
  23. /* 将双缓冲区图像复制到显示缓冲区 */
  24. BitBlt(hDC, 0, 0, rect.right, rect.bottom, hDCMem, 0, 0, SRCCOPY);
  25. /* 释放资源 */
  26. SelectObject(hDCMem, hPreBmp);
  27. DeleteObject(hMemBmp);
  28. DeleteDC(hDCMem);
  29. EndPaint(hWnd, &ps);
  30. break;
  31. }
  32. }

    使用 Win32 版本时注意释放资源,释放顺序与创建顺序相反。我在使用过程中不小心遗漏了一句上面的 "DeleteObject(hMemBmp);"导致图形显示一段时间后就卡死了,查看内存使用发现内存随时间推移飙升,加上上面这句代码后,就没这个问题了。这也再次提醒我们释放资源是多么重要,成对编程的习惯是多么重要。
图 2  处理几次WM_PAINT消息后内存变化图
     在使用过程中,如果想更新使用双缓冲区显示的区域,可以使用 InvalidateRect(hWnd, &rect, FALSE); ,这里要注意第三个参数一定要设置成 FALSE ,第三个参数表示更新第二个参数指定的区域时是否擦除背景,因为使用双缓冲技术时是直接复制整个缓冲区数据到显示缓冲区,因此无论原有缓冲区里面有什么都会被覆盖,因此第三个参数设置成 
FALSE 有助于提高新能。更主要的原因是,如果先擦除原有缓冲区,会导致中间有一瞬间显示缓冲区被清空(显示为默认背景色),然后等到复制了双缓冲区的数据后再显示新的图像,这将导致闪烁!这与使用双缓冲的本意相违背,所以要注意这一点。
四、双缓冲使用 (C# 版本)
  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. /* 像使用一般的 Graphics 一样绘图 */
  9. Pen pen = new Pen(Color.Gray);
  10. foreach (Step s in m_steps)
  11. {
  12. gc.DrawLine(pen, s.Start, s.End);
  13. }
  14. // 将双缓冲区中的图形渲染到指定画布上 (类似 Win32 中的)BitBlt
  15. backBuffer.Render(control.CreateGraphics());
  16. }
  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. /* 像使用一般的 Graphics 一样绘图 */
  9. Pen pen = new Pen(Color.Gray);
  10. foreach (Step s in m_steps)
  11. {
  12. gc.DrawLine(pen, s.Start, s.End);
  13. }
  14. // 将双缓冲区中的图形渲染到指定画布上 (类似 Win32 中的)BitBlt
  15. backBuffer.Render(control.CreateGraphics());
  16. }

其他版本后续用到时再补充。
参考资料:
文中用到的 Win32 API 在MSDN中的说明:
C# 中使用double buffer 

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

  1. 双缓冲(Double Buffer)原理和使用

    转自双缓冲(Double Buffer)原理和使用 一.双缓冲作用            双缓冲甚至是多缓冲,在许多情况下都很有用.一般需要使用双缓冲区的地方都是由于"生产者"和& ...

  2. STM32的bulk双缓冲传输速度的讨论,硬件的坑永远填不完

    详情:http://bbs.21ic.com/forum.php?mod=viewthread&tid=109584   USB 1.0的最高12Mbps. USB 2.0的高速模式480Mb ...

  3. GDI双缓冲绘图

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

  4. 双缓冲技术(Double Buffering)(1、简介和源代码部分)

    这一节实在是有些长,翻译完后统计了一下,快到2w字了.考虑到阅读的方便和网络的速度,打算把这节分为5个部分,第一部分为双缓冲技术的一个 简介和所有的代码,如果能够看懂代码,不用看译文也就可以了.第二部 ...

  5. Win32双缓冲画图原理

    网上有许多文章讲述了如何使用Visual C++程序实现双缓冲,都是用C++面向对象语言写的,可能对很多没有接触过面向对象语言的C语言初学者来说理解起来有些困难,并且有些好心人也只是把源代码贴上去,不 ...

  6. win32下的双缓冲绘图技术

    一:双缓冲原理 为了解决窗口刷新频率过快所带来的闪烁问题,利用双缓冲技术进行绘图.所谓双缓冲技术,就是将资源加载到内存,然后复制内存数据到设备DC(这个比较快),避免了直接在设备DC上绘图(这个比较慢 ...

  7. VC使用双缓冲制作绘图控件

    最近用VC做了一个画图的控件.控件在使用的时候遇到点问题.在控件里画了图之后切换到其他页面,等再切换回来的时候,发现控件里画的图都不见了.这是因为VC里面,当缩小.遮挡页面后客户区域就会失效,当再次显 ...

  8. c# GDI画图 双缓冲画图分析

    双缓冲绘图分析  1.Windows 绘图原理  我们在 Windows 环境下看到各种元素,如菜单.按钮.窗口.图像,从根本上说,都是“画”出来的.这时的屏幕,就相当于一块黑板,而 Windows ...

  9. 双缓冲类里的OnPaint与OnSize,以及构造函数的关系

    代码摘自wx\lib\agw\knobctrl.py一点体会是,OnSize作为class的函数,被放在构造函数里执行,会先于OnPaint执行.测试结果是,初始启动后,会执行8次OnSize(为什么 ...

随机推荐

  1. poj1182食物链

    Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到 ...

  2. POJ:2566-Bound Found(尺取变形好题)

    Bound Found Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 5408 Accepted: 1735 Special J ...

  3. vue 组件轮播联动

    组件轮播联动我使用的是 el-carousel 组件,具体代码如下: <el-carousel trigger="click" :interval="3000&qu ...

  4. spark提交运算原理

    前面几天元旦过high了,博客也停了一两天,哈哈,今天我们重新开始,今天我们介绍的是spark的原理 首先先说一个小贴士: spark中,对于var count = 0,如果想使count自增,我们不 ...

  5. Kafka写入流程和副本策略

    Kafka写入流程: 1.producer 先从 zookeeper 的 "/brokers/.../state" 节点找到该 partition 的 leader 2. prod ...

  6. laravel5.5服务提供器

    目录 1. 编写服务提供器 1.1 注册方法 register 1.1.1 简单绑定 1.1.2 绑定单例 1.1.3 绑定实例 1.1.4 绑定初始数据 1.2 引导方法 boot 2. 注册服务提 ...

  7. .swp文件的恢复

    .swp 编辑文件的过程中会出现这个隐藏文件. 文件如果正常保存,.swp就会自动删除.如果不正常退出,比如关机,.swp就会留下来. linux下: ls -all 可以查看隐藏文件 命令: vi ...

  8. 一个初学者的辛酸路程-基于Django写BBS项目

    前言 基于Django的学习 详情 登录界面 找个模板 http://v3.bootcss.com/examples/signin/ 右键,检查源码     函数 def login(request) ...

  9. VMware下Linux配置局域网和外网访问

    我想尝试的是利用本机的ip+port来访问虚拟机上的web服务器,因为这样的话,我就能够将我的web服务器部署成为一个能让外网访问的服务器了,首先说下我的环境: 主机:系统win7,ip地址172.1 ...

  10. Spring Web MVC 笔记

    Spring Web MVC 流程 Dispatcher Servlet 这是一个前端分派 Servlet(前端控制器模式),外部所有的请求都会先到达这里,然后由其将请求分派给其他组件进行实际的处理. ...