【MFC】MFC绘图不闪烁——双缓冲技术
[转自:http://blog.163.com/yuanlong_zheng@126/blog/static/49846449201033093333394/]
在VC/MFC用CDC绘图时,频繁的刷新,屏幕会出现闪烁的现象,CPU时间占用率相当高,绘图效率极低,很容易出现程序崩溃。
所谓双缓冲技术,下面是百度百科的解释:
我们看电视时,看到的屏幕称为OSD层,也就是说,只有在OSD层上显示图像我们才能看到。现在,我需要创建一个虚拟的、看不见但是可以在上面画图(比如说画点、线)的OSD层,我称之为offscreen(后台缓冲区)。这个offscreen存在于内存中,我们在上面画图,这个offscreen上面的东西可以显示在OSD层上,需要一个创建这个offscreen的函数,返回这个offscreen的句柄(整型指针)、宽度、高度、指向新建offscreen数据缓冲区的指针,该缓冲区是一个在函数外创建的offscreen的数据缓冲区,大小是offscreen的高度*宽度*每个像素点数据的大小。闪烁是图形编程的一个常见问题。需要多重复杂绘制操作的图形操作会导致呈现的图像闪烁或具有其他不可接受的外观。双缓冲的使用解决这些问题。双缓冲使用内存缓冲区来解决由多重绘制操作造成的闪烁问题。当启用双缓冲时,所有绘制操作首先呈现到内存缓冲区,而不是屏幕上的绘图图面。所有绘制操作完成后,内存缓冲区直接复制到与其关联的绘图图面。因为在屏幕上只执行一个图形操作,所以消除了由复杂绘制操作造成的图像闪烁。
在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲技术。
因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是我们就看到了闪烁现象。
我们会很自然的想到,避免背景色的填充是最直接的办法。但是那样的话,窗体上会变的一团糟。因为每次绘制图象的时候都没有将原来的图象清除,造成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。所以单纯的禁止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快,于是我们想到了使用BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。以上也就是双缓冲绘图的基本的思路。
先按普通做图的方法进行编程。即在视类的OnDraw函数中添加绘图代码。在此我们绘制若干同心圆,代码如下:
CBCDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CPoint ptCenter;
CRect rect,ellipseRect;
GetClientRect(&rect);
ptCenter = rect.CenterPoint();
for(int i=20;i>0;i--)
{
ellipseRect.SetRect(ptCenter,ptCenter);
ellipseRect.InflateRect(i*10,i*10);
pDC->Ellipse(ellipseRect);
}
编译运行程序,尝试改变窗口大小,可以发现闪烁现象。
在双缓冲方法中,首先要做的是屏蔽背景刷新。背景刷新其实是在响应WM_ERASEBKGND消息。我们在视类中添加对这个消息的响应,可以看到缺省的代码如下:
BOOL CMYView::OnEraseBkgnd(CDC* pDC)
{
return CView::OnEraseBkgnd(pDC);
}
是调用父类的OnEraseBkgnd函数,我们屏蔽此调用,只须直接return TRUE;即可。
下面是内存缓冲作图的步骤。
CPoint ptCenter;
CRect rect,ellipseRect;
GetClientRect(&rect);
ptCenter = rect.CenterPoint();
CDC dcMem; //用于缓冲作图的内存DC
CBitmap bmp; //内存中承载临时图象的位图
dcMem.CreateCompatibleDC(pDC); //依附窗口DC创建兼容内存DC
bmp.CreateCompatibleBitmap(&dcMem,rect.Width(),rect.Height());//创建兼容位图
dcMem.SelectObject(&bmp); //将位图选择进内存DC
dcMem.FillSolidRect(rect,pDC->GetBkColor());//按原来背景填充客户区,不然会是黑色
for(int i=20;i>0;i--) //在内存DC上做同样的同心圆图象
{
ellipseRect.SetRect(ptCenter,ptCenter);
ellipseRect.InflateRect(i*10,i*10);
dcMem.Ellipse(ellipseRect);
}
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,SRCCOPY);//将内存DC上的图象拷贝到前台
dcMem.DeleteDC(); //删除DC
bm.DeleteObject(); //删除位图
由于复杂的画图操作转入后台,我们看到的是速度很快的复制操作,自然也就消除了闪烁现象。
【MFC】MFC绘图不闪烁——双缓冲技术的更多相关文章
- VC 绘图,使用双缓冲技术实现
VC 绘图,使用双缓冲技术实现 - Cloud-Datacenter-Renewable Energy-Big Data-Model - 博客频道 - CSDN.NET VC 绘图,使用双缓冲技术实现 ...
- MFC中的双缓冲技术(解决绘图闪烁问题)
转自 MFC绘图不闪烁——双缓冲技术[转] 在VC/MFC用CDC绘图时,频繁的刷新,屏幕会出现闪烁的现象,CPU时间占用率相当高,绘图效率极低,很容易出现程序崩溃. 所谓双缓冲技术,下面是百度百科的 ...
- 《MFC游戏开发》笔记六 图像双缓冲技术:实现一个流畅的动画
本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9334121 作者:七十一雾央 新浪微博:http:/ ...
- c++双缓冲技术,以避免闪烁绘图
当数据量非常大时,画图可能须要几秒钟甚至更长的时间,并且有时还会出现闪烁现象,为了解决这些问题.可採用双缓冲技术来画图. 双缓冲即在内存中创建一个与屏幕画图区域一致的对象,先将图形绘制到内存中的这个对 ...
- Java中用双缓冲技术消除闪烁
在Java编写具有连贯变化的窗口程序时,通常的办法是在子类中覆盖父类的paint(Graphics)方法,在方法中使用GUI函数实现窗口重绘的过程.连贯变换的窗口会不断地调用update(Graphi ...
- C#中利用双缓冲技术解决绘图闪屏问题。
这段时间在做一个小型游戏,在界面显示的时候用到了一些图形.一开始涉及到的图形全都用控件的背景图片代替了.这样游戏运行的时候存在的一个很大的问题是游戏运行很慢.小组成员费尽周折,即将放弃,每一个成员都愁 ...
- Android开发之用双缓冲技术绘图
双缓冲技术主要用在画图,动画效果上,其原理就是:将资源先载入到缓冲区,然后再将缓冲区整个载入到View上面去. 双缓冲技术可以有效防止闪烁,提高显示质量. DrawView.java: package ...
- Win32 GDI 非矩形区域剪裁,双缓冲技术
传统的Win32通过GDI提供图形显示的功能,包括了基本的绘图功能,如画线.方块.椭圆等等,高级功能包括了多边形和Bezier的绘制.这样app就不用关心那些图形学的细节了,有点类似于UNIX上的X- ...
- 双缓冲技术(Double Buffering)(1、简介和源代码部分)
这一节实在是有些长,翻译完后统计了一下,快到2w字了.考虑到阅读的方便和网络的速度,打算把这节分为5个部分,第一部分为双缓冲技术的一个 简介和所有的代码,如果能够看懂代码,不用看译文也就可以了.第二部 ...
随机推荐
- 用cmd运行php代码、socket
一.用cmd运行php代码,首先要对电脑进行配置: 1.右击计算机-属性-高级系统设置-环境变量,我们需要添加环境变量. 2.在Administrator的用户变量(U)下点击新建,弹出对话框,变量名 ...
- 大数据生态,哪些框架需要全部启动,哪些只启动master,仅为汇总
主从,只需要在master节点启动 hadoop hbase 单机启动 hive 其他,需要启动每个节点 zookeeper kafka flume presto
- 单元测试JUnit 4
介绍 JUnit 4.x 是利用了 Java 5 的特性(Annotation)的优势,使得测试比起 3.x 版本更加的方便简单,JUnit 4.x 不是旧版本的简单升级,它是一个全新的框架,整个 ...
- hadoop16---反射
框架配置文件中,从字符串获取类和实例,调他的方法. 通过反射的方式可以获取class对象中的属性.方法.构造函数等,一下是实例: package cn.itcast_04_reflect; impor ...
- shell 学习一
一.shell脚本 打开文本编辑器(可以使用vi/vim命令来创建文件),新建一个文件test.sh,扩展名为sh(sh代表shell),扩展名并不影响脚本执行 #!/bin/bash echo &q ...
- 把 html标签转化为 html标签代码
HttpUtility.HtmlEncode(table.Rows[0]["footnote"].ToString());
- Linux yum源地址
----------------------------------Linux yum源地址------------------------------ Zabbix 3.0 yum源 rpm -iv ...
- vue2.0 transition 多个元素嵌套使用过渡
在做vue的demo的时候遇到一个问题,多个嵌套的元素如何设置transition? 我的代码:代码中元素整体做平移,里面的inner中做旋转,实现一个圆形滚动的效果 <transition n ...
- OpenVpn部署使用easy-rsa-3.0
OpenVpn简介 OpenVPN是一个用于创建虚拟专用网络(Virtual Private Network加密通道的免费开源软件.使用OpenVPN可以方便地在家庭.办公场所.住宿酒店等不同网络访问 ...
- lamp架构之升级php版本
当你看到这篇文章的时候 YHSPY.COM 服务器上的PHP版本已经从 5.4.27 升级到了 7.0.4,这是一个重大的飞跃.一路升级遇到了很多问题.官方声称PHP7最大的升级就是在语言性能上的提升 ...