MFC 绘制界面 控件以及屏幕闪烁的问题

来源:互联网 发布:网络机房的重要性 编辑:程序博客网 时间:2024/05/23 18:32

绘图时屏幕闪烁的原因分析

很长一段时间我一直认为绘图过程中出现的屏幕闪烁是图形刷新速度过快而造成的(相信有很多朋友也跟我一样有这样的想法),但是通过编写一些绘图程序,我发现事情并非如此,至少刷新速度快不会是造成屏幕闪烁的根本原因。这个问题可以通过下面的两个试验进行说明。
1、编写一个刷新速度很慢的应用程序,可以设计为通过点击鼠标来进行屏幕刷新。通过该试验可以发现即使屏幕的刷新速度很慢,但是在每次刷新的时候仍然存在屏幕的问题,只是闪烁不是很明显。
2、编写一个刷新速度很快的应用程序,并在程序中应用双缓冲图形刷新技术。通过该试验可以发现虽然屏幕刷新速度很快,但是采用了双缓冲图新刷新技术以后,屏幕不存在闪烁。
屏幕闪烁的根本原因是相邻两帧图像之间存在的巨大差异造成的,而windows的图形刷新方式使得任何两帧图像之间都存在着巨大的差异,因为windows在进行刷新之前都会首先将整个屏幕刷成白色,就相当于在电影胶片的相邻两帧之间都插入了一个白色的帧,这也就是为什么屏幕闪烁时总是看到一个隐约的白色窗口在闪烁而不是一个红色的窗口在闪烁。双缓冲图形刷新避免了windows刷新的问题,其没有在连续的两帧之间插入白色的帧,从而解决了屏幕闪烁的问题。


双缓冲图形刷新的原理

双缓冲图形刷新顾名思义是采用双缓存实现的。传统的绘图方式实际上是一种单缓冲。在windows中每一种设备都在内存中有一个设备描述表与其对应,这个设备描述表实际上就是一个内存缓冲区。传统的绘图中我们是将图形绘制在设备描述表缓冲区中,然后由gdi自动的将设备描述表中的图像拷贝到显存中进行显示。这样一个自动的拷贝过程屏蔽了传统的绘图方式是单缓冲的实质,使我们感觉到我们是在直接操纵显存一样。双缓冲图形刷新技术在内存中有两片缓存,除了设备描述表以外还有一个需要手动建立的与设备描述表缓冲区(前端缓冲区)相兼容的后备缓冲区。绘图过程中,首先将图形绘制在后备缓冲区中,然后在手动的将后备缓冲区中的图像拷贝到前端缓冲区中,再由gdi自动将前端缓冲区中的图像拷贝到显存完成图形的显示过程。
双缓冲图形刷新的实现步骤
1、创建与窗口设备描述表(前端缓冲区)兼容的内存设备描述表(后端缓冲区)
2、创建与内存设备描述表相兼容的位图并将该位图选入内存设备描述表中(没有位图的设备描述表是不能绘图的)
3、将图形绘制在内存设备描述表中
4、将内存设备描述表中的内容拷贝到窗口设备描述表

5、释放设备描述表句柄、位图等资源


下面是双缓冲的代码例子

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,255,255));  //绘图  MemDC.MoveTo(……);  MemDC.LineTo(……);  //将后备缓冲区中的图形拷贝到前端缓冲区  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 TRUE;  }  

Dialog上有自己添加的控件,这个时候在重绘窗口的时候,控件会闪烁有以下解决办法

设置Dialog的属性  Clip Children 设置为True即可

代码实现

LONG style = GetWindowLong(m_hWnd, GWL_STYLE);
style = style | WS_CLIPCHILDREN;
SetWindowLong(m_hWnd, GWL_STYLE, style);

原创粉丝点击