MFC双缓冲绘图

    在VC/MFCCDC绘图时,频繁的刷新,屏幕会出现闪烁的现象,CPU时间占用率相当高,绘图效率极低,很容易出现程序崩溃。

 

      所谓双缓冲技术,百度百科的解释:双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。

 

     当我们看电视时,看到的屏幕称为OSD层,也就是说,只有在OSD层上显示图像我们才能看到。现在,我需要创建一个虚拟的、看不见但是可以在上面画图(比如说画点、线)的OSD层,我称之为offscreen(后台缓冲区)。这个offscreen存在于内存中,我们在上面画图,这个offscreen上面的东西可以显示在OSD层上,需要一个创建这个offscreen的函数,返回这个offscreen的句柄(整型指针)、宽度、高度、指向新建offscreen数据缓冲区的指针,该缓冲区是一个在函数外创建的offscreen的数据缓冲区,大小是offscreen的高度*宽度*每个像素点数据的大小。闪烁是图形编程的一个常见问题。需要多重复杂绘制操作的图形操作会导致呈现的图像闪烁或具有其他不可接受的外观。双缓冲的使用解决这些问题。双缓冲使用内存缓冲区来解决由多重绘制操作造成的闪烁问题。当启用双缓冲时,所有绘制操作首先呈现到内存缓冲区,而不是屏幕上的绘图图面。所有绘制操作完成后,内存缓冲区直接复制到与其关联的绘图图面。因为在屏幕上只执行一个图形操作,所以消除了由复杂绘制操作造成的图像闪烁。

 

      在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲技术。

 

  因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是我们就看到了闪烁现象。

 

我们会很自然的想到,避免背景色的填充是最直接的办法。但是那样的话,窗体上会变的一团糟。因为每次绘制图象的时候都没有将原来的图象清除,造成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。所以单纯的禁止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快,于是我们想到了使用BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。以上也就是双缓冲绘图的基本的思路,即:

双缓冲图形刷新的实现步骤
      1、创建与窗口设备描述表(前端缓冲区)兼容的内存设备描述表(后端缓冲区)
      2、创建与内存设备描述表相兼容的位图并将该位图选入内存设备描述表中(没有位图的设备描述表是不能绘图的)
      3、将图形绘制在内存设备描述表中
      4、将内存设备描述表中的内容拷贝到窗口设备描述表
      5、释放设备描述表句柄、位图等资源

mfc实现双缓冲图形刷新技术主要代码如下,

1、首先在OnDraw()或者OnPaint()中添加下列代码
void OnDraw(CDC *pDC)
{
//定义一个内存设备描述表对象(即后备缓冲区)
CDC MemDC; 
//定义一个位图对象
CBitmap MemBitmap;
//建立与屏幕设备描述表(前端缓冲区)兼容的内存设备描述表句柄(后备缓冲区)
MemDC.CreateCompatibleDC(NULL);
//这时还不能绘图,因为没有位图的设备描述表是不能绘图的
//下面建立一个与屏幕设备描述表(或者内存设备描述表)兼容的位图
MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);
//将位图选入到内存设备描述表
//只有选入了位图的设备描述表才有地方绘图,画到指定的位图上
CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);
//先用背景色将位图清除干净,暂红色作为背景
MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,0,0));
//绘图 画空心圆8行8列半径为r;
Int r=4;
for(int i=0; i<8; ++i)
	{
	for(int j=0; j<8; ++j)
	{
		MemDC.Ellipse(i*r, j*r, (i+1)*(r+1), (j+1)*(r+1));
	}
}

//将后备缓冲区中的图形拷贝到前端缓冲区
pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);
//绘图完成后的清理
MemBitmap.DeleteObject();
MemDC.DeleteDC();
}
2、添加WM_ERASEBKGND响应函数,并清除响应函数的生成代码在其中添加如下代码
BOOL OnEraseBkgnd(CDC* pDC)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//return CDialog::OnEraseBkgnd(pDC);
return FALSE;
}

    当然,实际开发中根据具体业务,在程序初始化时候就应该申请匹配的设备描述表和位图对象,中间图像的切换进行绘画,程序结束时候进行资源释放。


猜你喜欢

转载自blog.csdn.net/u010855021/article/details/77801225