MFC双缓冲绘图(2015.09.24)
问题引入:
最近在尝试编写贪吃蛇游戏时遇到这么一个问题:当系统以较快频率向窗口发送WM_PAINT消息时,调用OnPaint()函数在窗口中绘制图形就会发生闪烁现象。
问题分析:
当我们把绘图过程放在OnPaint()函数中时(放在OnDraw()函数中也是如此,因为OnDraw()会被OnPaint()调用),由于频繁收到系统的WM_PAINT消息,窗口需要执行重绘。而重绘过程首先是执行了窗口内容的擦除(用当前背景色的画刷对窗口重新绘制),然后再根据绘图语句在窗口客户区中对窗口内容进行重绘。由于频率较快,当前窗口中会产生背景色和窗口内容的反复交替,二者的色差造成了闪烁的效果。
问题解决:
双缓冲绘图:双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上的过程。
先发上原来OnPaint()函数中的绘图代码:
void CMainWindow::OnPaint()
{
CClientDC dc(this); CPen White_Pen(PS_SOLID, , RGB(, , ));
CPen *pOldPen = dc.SelectObject(&White_Pen);
CBrush White_Brush(RGB(, , ));
CBrush *pOldBrush = dc.SelectObject(&White_Brush); //Draw background
dc.Rectangle(&m_rcBack); //Draw scoreboard
dc.SelectStockObject(GRAY_BRUSH);
dc.Rectangle(&m_rcScoreBoard); //Draw wall
dc.SelectStockObject(BLACK_BRUSH); int i;
for (i = ; i < MAP_SIZE_CX; ++i)
{
dc.Rectangle(m_rcGameMap[i][]);
dc.Rectangle(m_rcGameMap[i][MAP_SIZE_CY - ]);
}
for (i = ; i < MAP_SIZE_CY - ; ++i)
{
dc.Rectangle(m_rcGameMap[][i]);
dc.Rectangle(m_rcGameMap[MAP_SIZE_CX - ][i]);
} dc.SelectObject(pOldPen);
dc.SelectObject(pOldBrush);
}
由于出现闪烁的问题将上述代码转写到OnEraseBkgnd()函数中(并且添加了WM_ERASEBKGND的消息映射,顾名思义,就是系统通知窗口擦除客户区的消息),同时在OnPaint()中对OnEraseBkgnd()显式调用(这里要用CPaintDC类,之前用CClientDC类导致WM_PAINT消息在消息队列里造成死循环,原因是因为CClientDC类的构造函数和析构函数里没有像CPaintDC类一样调用::BeginPaint()和::EndPaint()),代码如下:
void CMainWindow::OnPaint()
{
CPaintDC dc(this);
OnEraseBkgnd(&dc);
}
BOOL CMainWindow::OnEraseBkgnd(CDC *pDC)
{
CRect rect;
CDC dcMem;
GetClientRect(&rect);;
CBitmap bmp; dcMem.CreateCompatibleDC(pDC);
bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
CBitmap *pOldBit = dcMem.SelectObject(&bmp);
dcMem.FillSolidRect(rect, RGB(, , )); //
//Draw
//
CPen White_Pen(PS_SOLID, , RGB(, , ));
CPen *pOldPen = dcMem.SelectObject(&White_Pen);
CBrush White_Brush(RGB(, , ));
CBrush *pOldBrush = dcMem.SelectObject(&White_Brush); //Draw background
dcMem.Rectangle(&m_rcBack); //Draw scoreboard
dcMem.SelectStockObject(GRAY_BRUSH);
dcMem.Rectangle(&m_rcScoreBoard); //Draw wall
dcMem.SelectStockObject(BLACK_BRUSH); int i;
for (i = ; i < MAP_SIZE_CX; ++i)
{
dcMem.Rectangle(m_rcGameMap[i][]);
dcMem.Rectangle(m_rcGameMap[i][MAP_SIZE_CY - ]);
}
for (i = ; i < MAP_SIZE_CY - ; ++i)
{
dcMem.Rectangle(m_rcGameMap[][i]);
dcMem.Rectangle(m_rcGameMap[MAP_SIZE_CX - ][i]);
} pDC->BitBlt(, , rect.Width(), rect.Height(), &dcMem, , , SRCCOPY); dcMem.DeleteDC();
bmp.DeleteObject();
return TRUE;
}
对比上面两段代码发现,二者的差别仅仅在于前者(OnPaint()函数)是直接对客户区对象(dc)直接进行绘制,而后者(OnEraseBkgnd()函数)是先在内存中开辟了一块基于当前客户区大小的缓冲区(dcMem),用dcMem这一对象取代dc进行相同的绘制操作(二者有相似的成员函数),在内存中绘制完成后再通过如下语句将内存中绘制好的图形直接拷贝到窗口客户区,因为是直接一步完成所以避免了原来的擦除和重绘过程,也就解决了闪烁的问题。
pDC->BitBlt(, , rect.Width(), rect.Height(), &dcMem, , , SRCCOPY);
总结:
当窗口客户区需要绘制复杂图形时,如果内存条件允许的话最好采取双缓冲的绘制方法,一方面能解决窗口绘制时闪烁的问题,另一方面,因为减少了窗口反复的擦除过程,也在一定程度上减少了响应时间,是一种用空间换取时间的做法。
参考:
①:http://blog.csdn.net/aaahuanian/article/details/7844522
MFC双缓冲绘图(2015.09.24)的更多相关文章
- MFC双缓冲绘图实例
本人之前一直了解双缓冲绘图的基本原理,但是在研究很久之后才大概知道具体的使用过程,本文将详细介绍本人在实际项目中使用双缓冲绘图的案例. 实现功能:主界面显示某张包含人脸的图片,通过dlib detec ...
- mfc双缓冲绘图
1.要求 在界面加载本地图片并显示,每过100ms改变一张图片显示 2.现象 通过定时器控制CImage,Load,Draw,Destroy,会非常的卡顿.因为Load图片时,会是非常大的数据[所有C ...
- MFC双缓冲解决图象闪烁[转]
转载网上找到的一篇双缓冲的文章,很好用.http://www.cnblogs.com/piggger/archive/2009/05/02/1447917.html__________________ ...
- 【MFC】MFC绘制动态曲线,用双缓冲绘图技术防闪烁
摘自:http://zhy1987819.blog.163.com/blog/static/841427882011614103454335/ MFC绘制动态曲线,用双缓冲绘图技术防闪烁 2011 ...
- 陈灯WGF双缓冲绘图框架
“木丸子童屋”,专售各类儿童玩具,价格优惠,请大家多多支持:http://shop65552598.taobao.com/ WGF(windows graphic foundation)为window ...
- C#-gdi绘图,双缓冲绘图,Paint事件的触发
一. 画面闪烁问题与双缓冲技术 1.1 导致画面闪烁的关键原因分析: 1 绘制窗口由于大小位置状态改变进行重绘操作时 绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面 ...
- 双缓冲绘图和窗口控件的绘制——ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 .
双缓冲绘图和窗口控件的绘制 ---ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 cheungmine 我们通常使用ATL COM组件,生成一个带窗口的ActiveX控件,然后 ...
- 简单的 "双缓冲" 绘图的例子(研究一下)
所谓双缓冲就是先画到内存画布(如: TBitmap), 然后再转帖到目的地. 譬如下面小程序: procedure TForm1.FormCreate(Sender: TObject); begin ...
- 简单的 "双缓冲" 绘图的例子
http://www.cnblogs.com/del/archive/2010/04/19/1715779.html 所谓双缓冲就是先画到内存画布(如: TBitmap), 然后再转帖到目的地. 譬如 ...
随机推荐
- 初学者的python学习笔记2——作业篇
这次的作业是商城展示,最开始的确是一脸懵逼,后来研究了一下网上的例子,总算是搞出来了一个(其实算是网上列子的简化版?). 最开始是想要使用字典去做,发现显示列表的时候怎么也无法实现序号和名称对应,原因 ...
- Djanog结合jquery实现ajax
最近想在使用django的基础上通过jquery实现页面局部刷新的功能,研究了两天,终于是解决了这个问题,下面把方法步骤记录下来,以备以后重用. 在项目中通过两种形式实现了ajax: 第一种方法:we ...
- 利用netperf、iperf、mtr测试网络
1.netperf安装和使用 netperf安装 # tar -xzvf netperf-.tar.gz # cd netperf- # ./configure # make # make insta ...
- 转:js中this、call、apply的用法
(一)关于this首先关于this我想说一句话,这句话记住了this的用法你也就差不多都能明白了:this指的是当前函数的对象.这句话可能比较绕,我会举出很多例子和这句话呼应的!(看下文)1.首先看下 ...
- js限制文本框只能输入数字方法小结(转)
这篇文章主要分享下js代码限制文本框中只能输入数字的多个实例,学习下js控制文本框中输入数字的方法,需要的朋友可以参考下 有时需要限制文本框输入内容的类型,本节分享下正则表达式限制文本框只能输入数 ...
- 《C++primer》v5 第5章 语句 读书笔记 习题答案
5.1 空语句只有一个";".如果什么也不想做可以使用空语句. 5.2 用花括号{}括起来的叫块,也叫复合语句.有多条语句作用在同一个作用域时,需要用花括号括起来. 5.3 降低了 ...
- Python Beautiful Soup学习之HTML标签补全功能
Beautiful Soup是一个非常流行的Python模块.该模块可以解析网页,并提供定位内容的便捷接口. 使用下面两个命令安装: pip install beautifulsoup4 或者 sud ...
- window10的优缺点
windows10的体验随笔 为了体验科技前沿,前段时间升级了windows10 优点: 首先换了windows10就回不去了, 1.开始菜单的回归是众向所归,而且也加了迷你的一些菜单元素,值得称 ...
- 团队开发——冲刺2.f
冲刺阶段二(第六天) 1.昨天做了什么? 编写软件测试计划书第二部分:游戏中新增3个道具(变大.变小.延时). 2.今天准备做什么? 1) 编写软件计划书第三阶段(项目任务.实施计划.风险管理): 2 ...
- 如何用极路由+OpenWrt+SDR电视棒搭建SDR服务器
0×00 前言 近期因为有个从异地捕获无线信号的需求,便尝试着用OpenWrt+公网IP搭建了一台SDR服务器.如果有小伙伴嫌SDR硬件天线看起来太乱.或者电脑没有足够的USB接口也可在局域网搭建SD ...