问题引入:

  最近在尝试编写贪吃蛇游戏时遇到这么一个问题:当系统以较快频率向窗口发送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)的更多相关文章

  1. MFC双缓冲绘图实例

    本人之前一直了解双缓冲绘图的基本原理,但是在研究很久之后才大概知道具体的使用过程,本文将详细介绍本人在实际项目中使用双缓冲绘图的案例. 实现功能:主界面显示某张包含人脸的图片,通过dlib detec ...

  2. mfc双缓冲绘图

    1.要求 在界面加载本地图片并显示,每过100ms改变一张图片显示 2.现象 通过定时器控制CImage,Load,Draw,Destroy,会非常的卡顿.因为Load图片时,会是非常大的数据[所有C ...

  3. MFC双缓冲解决图象闪烁[转]

    转载网上找到的一篇双缓冲的文章,很好用.http://www.cnblogs.com/piggger/archive/2009/05/02/1447917.html__________________ ...

  4. 【MFC】MFC绘制动态曲线,用双缓冲绘图技术防闪烁

    摘自:http://zhy1987819.blog.163.com/blog/static/841427882011614103454335/ MFC绘制动态曲线,用双缓冲绘图技术防闪烁   2011 ...

  5. 陈灯WGF双缓冲绘图框架

    “木丸子童屋”,专售各类儿童玩具,价格优惠,请大家多多支持:http://shop65552598.taobao.com/ WGF(windows graphic foundation)为window ...

  6. C#-gdi绘图,双缓冲绘图,Paint事件的触发

    一. 画面闪烁问题与双缓冲技术 1.1 导致画面闪烁的关键原因分析: 1  绘制窗口由于大小位置状态改变进行重绘操作时 绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面 ...

  7. 双缓冲绘图和窗口控件的绘制——ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 .

    双缓冲绘图和窗口控件的绘制 ---ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 cheungmine 我们通常使用ATL COM组件,生成一个带窗口的ActiveX控件,然后 ...

  8. 简单的 "双缓冲" 绘图的例子(研究一下)

    所谓双缓冲就是先画到内存画布(如: TBitmap), 然后再转帖到目的地. 譬如下面小程序: procedure TForm1.FormCreate(Sender: TObject); begin ...

  9. 简单的 "双缓冲" 绘图的例子

    http://www.cnblogs.com/del/archive/2010/04/19/1715779.html 所谓双缓冲就是先画到内存画布(如: TBitmap), 然后再转帖到目的地. 譬如 ...

随机推荐

  1. google closure继承模块三:goog.base()源码分析

    直接看代码吧: base: function (me, opt_methodName, var_args) { var caller = arguments.callee.caller; if (ca ...

  2. java语言程序设计(一)-2

    (一)jdk安装及环境变量配置 1.jdk下载 下载地址http://www.oracle.com/technetwork/java/javase/downloads/index.html,下载SE标 ...

  3. 使linux服务器默认使用中文字符集zh_CN.UTF-8

    一.问题描述和相关概念 linux服务器的字符集设置可能影响到网站页面出现 “???” 等问号乱码,还有可能导致文件中的汉字部分出现乱码. locales设置:语言设置选项   linux真的是一个非 ...

  4. C语言的编译过程、安装gcc编译器以及设置环境变量

    以我对C语言编译过程的了解,我用了一点时间画了一个图,提供给大家参考一下,希望有些能对您的问题提上帮助. 前几天刚初步学习了C语言的编译过程,感触挺深的.在C语言中头文件其实起了一个很大的作用. 1. ...

  5. JAVA vo pojo javabean dto区别

    JavaBean 是一种JAVA语言写成的可重用组件.为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器.JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性 ...

  6. 安全协议系列(二)----CCM与CCMP

    CCMP(CTR with CBC-MAC Protocol) 是 IEEE 802.11i 中推出使用基于 AES 的 CCM 模式的安全加密协议.与原来脆弱的 WEP 算法及临时补救措施 TKIP ...

  7. 如何利用Matlab进行ROC分析

    ROC曲线基本知识: 判断分类器的工作效率需要使用召回率和准确率两个变量. 召回率:Recall,又称"查全率", 准确率:Precision,又称"精度".& ...

  8. 基于.NET的CAD二次开发学习笔记一:CAD开发入门

    1.AutoCAD .NET API由不同的DLL文件组成,它们提供用于访问图形文件或AutoCAD应用程序的包含丰富的类.结构.方法和事件.每一个DLL文件都定义不同的使用基于功能的库组织组件的命名 ...

  9. 2014西安现场赛F题 UVALA 7040

    地址 题意:求在m种颜色中挑选k种颜色,给n个花朵涂色有几种方法. 分析:画图可以发现,基本的公式就是k ×(k-1)^(n-1).但这仅保证了相邻颜色不同,总颜色数不超过k种,并没有保证恰好出现k种 ...

  10. Linux内核启动过程start_kernel分析

    虽然题目是start_kernel分析,但是由于我在ubuntu环境下配置实验环境遇到了一些问题,我觉得有必要把这些问题及其解决办法写下来. 首先我使用的是Ubuntu14.04 amx64,以下的步 ...