MFC双缓冲+提升绘图效率方法(号称三缓冲):自定义静态背景不频繁擦除(★firecat推荐★)

应用场景是:绘制一个运动小球,在大屏幕滚动,屏幕背景图也是一个自定义绘制的(填充色,线条和文字等组成)。如图所示。红色是小球,在大背景里动态移动,而背景是静态的,静止不动。

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://www.cnblogs.com/renyuan/p/3474802.html

https://bbs.csdn.net/topics/390929456

https://blog.csdn.net/ooyyee11/article/details/7600625

https://www.cnblogs.com/lujin49/p/4704795.html

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值