MFC双缓冲+提升绘图效率方法(号称三缓冲):自定义静态背景不频繁擦除(★firecat推荐★)
发布日期:2021-06-30 18:20:42
浏览次数:2
分类:技术文章
本文共 4688 字,大约阅读时间需要 15 分钟。
应用场景是:绘制一个运动小球,在大屏幕滚动,屏幕背景图也是一个自定义绘制的(填充色,线条和文字等组成)。如图所示。红色是小球,在大背景里动态移动,而背景是静态的,静止不动。
1、MFC克服C++窗体重绘时的闪烁问题,用到的技巧是双缓冲。双缓冲原理网上的文章好多,这里不赘述。
//---------------------------------MFC双缓冲//---------------------------------void CViewImage::OnPaint(){ CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CFormView::OnPaint() OnPrepareDC(&dc); OnDraw(&dc); //调用了OnDraw}void CViewImage::OnDraw(CDC *pDC){ CDC dcMemory; //图形重绘,双缓冲防止闪屏 CRect rect; GetClientRect(&rect); CBitmap bmp; dcMemory.CreateCompatibleDC(pDC); bmp.CreateCompatibleBitmap(pDC, rect.right, rect.bottom); CBitmap *pOldBitmap = dcMemory.SelectObject(&bmp); dcMemory.SetBkMode(TRANSPARENT); DrawBackgroundImage(&dcMemory);//绘制自定义背景图 DrawBall(&dcMemory);//绘制小球 pDC->BitBlt(rect.left, rect.top, rect.right, rect.bottom, &dcMemory, 0, 0, SRCCOPY); dcMemory.SelectObject(pOldBitmap); dcMemory.DeleteDC(); bmp.DeleteObject();}BOOL CViewImage::OnEraseBkgnd(CDC* pDC){ //Invalidate(FALSE)不擦除背景,直接画,它只会向消息队列中添加了WM_PAINT消息。 //Invalidate(TRUE)擦除背景,它会向消息队列中添加了WM_ERASEBKGND和WM_PAINT两个消息。 //可见:Invalidate(FALSE)不会清空之前所画图像。 //如果你想用Invalidate(TRUE)来实现Invalidate(FALSE)一样的效果, //你可以添加对WM_ERASEBKGND消息响应的函数, //修改OnEraseBkgnd函数的返回值为:return TRUE; //返回值return TRUE就是不擦除背景,此时Invalidate(TRUE)与Invalidate(FALSE)的效果是一样的。 return TRUE;//禁止背景重绘; true表示已处理背景刷新,false表示需要在OnPaint里处理 //return CFormView::OnEraseBkgnd(pDC);}
2、本文要解决的是提升绘图效率,不要让背景频繁擦除。本方法可以认为是MFC三缓冲绘图。(#^.^#)
在上面的源码OnDraw函数中,每次都需要绘制自定义背景图和小球图。每隔50毫秒通过Invalidate函数发送WM_PAINT消息刷新屏幕。但实际运行过程中发现,每次绘制自定义背景太占CPU(4%,4核CPU),有什么更好的办法吗?
办法就是不要每次都重绘背景图,因为它是静止不动的。于是想到将背景预先绘制到一个内存设备区域,然后在OnDraw中将该内存设备环境中的内容复制到当前的内存设备环境中。
方法1:画在已知的内存型bmp
//---------------------------------方法1:---------------------------------定义类的成员变量CDC dcMemory;//图形重绘,双缓冲防止闪屏CBitmap bmp;CBitmap *pOldBitmap;void CViewImage::initBackgroundImage(CDC* pDC){ if (!bFirst) { return; } CRect rect; GetClientRect(&rect); dcMemory.CreateCompatibleDC(pDC); bmp.CreateCompatibleBitmap(pDC, rect.right, rect.bottom); pOldBitmap = dcMemory.SelectObject(&bmp); dcMemory.SetBkMode(TRANSPARENT); //dcMemory.FillSolidRect(0, 0, rect.right, rect.bottom, RGB(255, 255, 255));//默认是黑色 DrawBackgroundImage(&dcMemory);//绘制自定义背景图}void CViewImage::destroyBackgroundImage(){ dcMemory.SelectObject(pOldBitmap); dcMemory.DeleteDC(); bmp.DeleteObject();}void CViewImage::OnDraw(CDC* pDC)//方法1:画在已知的内存型bmp{ initBackgroundImage(CDC* pDC);//使用布尔量控制只绘制一次,但是遇到OnSize消息时,背景图需要重绘 CRect rect; GetClientRect(&rect); CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); pOldBitmap = dcCompatible.SelectObject(&bmp); DrawBall(&dcCompatible);//绘制小球 pDC->BitBlt(rect.left, rect.top, rect.right, rect.bottom, &dcCompatible, 0, 0, SRCCOPY); dcCompatible.SelectObject(pOldBitmap); dcCompatible.DeleteDC();}
方法2:一个CDC拷贝到另一个CDC
//---------------------------------方法2:---------------------------------定义类的成员变量CDC dcMemory1;CBitmap bmp1;CBitmap *pOldBitmap1;void CViewImage::initBackgroundImage(CDC* pDC){ if (!bFirst) { return; } CRect rect; GetClientRect(&rect); dcMemory1.CreateCompatibleDC(pDC); bmp1.CreateCompatibleBitmap(pDC, rect.right, rect.bottom); pOldBitmap1 = dcMemory1.SelectObject(&bmp1); dcMemory1.SetBkMode(TRANSPARENT); //dcMemory1.FillSolidRect(0, 0, rect.right, rect.bottom, RGB(255, 255, 255));//默认是黑色 DrawBackgroundImage(&dcMemory1);//绘制自定义背景图}void CViewImage::destroyBackgroundImage(){ dcMemory1.SelectObject(pOldBitmap1); dcMemory1.DeleteDC(); bmp1.DeleteObject();}void CViewImage::OnDraw(CDC* pDC){ initBackgroundImage(CDC* pDC);//使用布尔量控制只绘制一次,但是遇到OnSize消息时,背景图需要重绘 //方法2:一个CDC拷贝到另一个CDC CDC dcMemory2; dcMemory2.CreateCompatibleDC(&dcMemory1); CBitmap bmp2; bmp2.CreateCompatibleBitmap(&dcMemory1, rect.right, rect.bottom); CBitmap *pOldBitmap2 = dcMemory2.SelectObject(&bmp2); dcMemory2.SetBkMode(TRANSPARENT); dcMemory2.BitBlt(rect.left, rect.top, rect.right, rect.bottom, &dcMemory1, 0, 0, SRCCOPY);//CDC拷贝 DrawBall(&dcMemory2);//绘制小球 pDC->BitBlt(rect.left, rect.top, rect.right, rect.bottom, &dcMemory2, 0, 0, SRCCOPY);//绘制在客户区 dcMemory2.SelectObject(pOldBitmap2); dcMemory2.DeleteDC(); bmp2.DeleteObject();}
推荐使用方法2!!
---题外话---
//---------------------------------题外话:外部加载位图并显示---------------------------------void CViewImage::OnDraw(CDC* pDC){ CBitmap bitmap; bitmap.LoadBitmap(IDB_BITMAP_DEBUG_TEACH); BITMAP bmp1; bitmap.GetBitmap(&bmp1); CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); pOldBitmap = dcCompatible.SelectObject(&bitmap); dcCompatible.MoveTo(1, 1); dcCompatible.LineTo(40, 50); pDC->BitBlt(rect.left, rect.top, rect.right, rect.bottom, &dcCompatible, 0, 0, SRCCOPY); dcCompatible.SelectObject(pOldBitmap); dcCompatible.DeleteDC(); bitmap.DeleteObject();}
---
参考文献:
转载地址:https://libaineu2004.blog.csdn.net/article/details/90344612 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
第一次来,支持一个
[***.219.124.196]2024年04月14日 17时06分44秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
JAVA学习笔记10 - 继承
2019-04-30
JAVA学习笔记11 - 接口interface
2019-04-30
JAVA学习笔记12 - 包package
2019-04-30
Android 开发学习笔记 00 - Getting Started
2019-04-30
【学习笔记】Android Activity
2019-04-30
【学习笔记】Android Fragments
2019-04-30
Android使用Retrofit_00_Getting Started
2019-04-30
Android使用Retrofit_01_OAuth2 + GitHub
2019-04-30
Django + REST学习笔记
2019-04-30
【转载】将Ubuntu16.04 中gedit在仅显示一个文件时显示文件名tab
2019-04-30
fstream 对象多次使用时注意clear
2019-04-30
调试 LenaCV 3D Camera (Linux)
2019-04-30
OpenCV杂记 - Mat in C++
2019-04-30
lnmp部署
2019-04-30
location区段
2019-04-30
nginx访问控制、基于用户认证、https配置
2019-04-30