MFC中的双缓冲技术(解决绘图闪烁问题)
在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; //内存中承载临时图象的位图
bmp.CreateCompatibleBitmap(&dcMem,rect.Width(),rect.Height());//创建兼容位图
dcMem.CreateCompatibleDC(pDC); //依附窗口DC创建兼容内存DC
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(); //删除位图
由于复杂的画图操作转入后台,我们看到的是速度很快的复制操作,自然也就消除了闪烁现象。
相关文章
- 数说故事再获IDC认可,入选“数据智能决策”热点技术代表企业
- 网络攻防技术复习
- 特定领域知识图谱融合方案:技术知识前置【一】-文本匹配算法、知识融合学术界方案、知识融合
- 0基础入门Linux运维技术(0基础linux运维)
- 解决Linux驱动的轮询技术(linux驱动轮询)
- 极速解决:Redis缓存技术处理数组(redis缓存数组)
- 解决Oracle性能问题的有效优化技术(oracle优化案例)
- 应用Oracle序列化技术解决数据一致性(oracle序列化)
- Oracle 动态分区:智能解决技术之道(oracle动态分区)
- Linux下反汇编技术探索(linux反汇编软件)
- 百度朱光:将技术基因注入百度金融,是我们未来三五年要做的事
- 深度探索Linux高精度定时器技术,精准解决计时精度问题!(linux高精度定时器)
- 安装Linux:从光盘上解决你的技术问题(光盘linux系统)
- 解决Oracle远程导入技术指南(oracle远程导入)
- 玩 VR 不知道定位追踪技术?你需要这篇文章补补课
- SQL Server数据库深度技术宝典(sqlserver课本)
- MySQL散表技术:提高查询效率的新方式(mysql 散表)
- 连接MySQL与Java实现长连接技术研究(mysqljava长)
- 解决ODBC连接oracle问题的技术分享(odbc 无oracle)
- 使用MySQL远程连接解决BT技术限制(bt mysql远程连接)
- 解决JPA技术将Oracle数据访问变慢的方法(jpa oracle 慢)
- 技术问题MySQL服务器频繁重启,无法启动的解决方法(mysql一直重启不了)
- 写时拷贝技术 用Redis解决不确定性问题(写时拷贝 redis)
- 技术分享解决MySQL无法写入空值问题(mysql不能写进空值)
- Oracle中ADG技术实现容错性冗余数据存储(Oracle中adg全称)
- 使用 Redis 超时处理技术实现更优性能(redis 超时解决方法)
- Redis缓存技术管理队列的实践(redis记录队列)
- 利用Redis进行缓存技术,解决性能问题(redis 解决什么问题)
- 前Oculus技术大神加盟华为,Snapchat收购AR公司 | 沉浸感周刊