【MFC】VC界面绘制双缓存

VC界面绘制双缓存

转载请注明原文网址: html

http://www.cnblogs.com/xianyunhe/archive/2011/11/20/2255811.html缓存

一、闪屏的问题
在GDI的绘图系统中,每调用一次区域绘图操做,如FillRect、BitBlt等,图形显示系统就会在屏幕中对指定的区域进行一次刷新操做。若是频繁的进行区域绘制操做的操做的话,咱们就会发现,屏幕会出现闪屏。
使用下面的代码对闪屏的问题进行测试,在XP系统闪屏尤为严重,在Win7系统,闪屏问题有所改善。Win7系统在绘制效率上有所提高。多线程

void  CDoubleBufferView::DrawDirect(CDC* pDC)
{
     ASSERT_VALID(pDC);
 
     /*用渐变色粉刷背景*/
     CRect rect(0,0,0,0);
     CSize szTotal = GetTotalSize();
     for  ( int  i = 0; i <= szTotal.cy; i++)
     {
         rect.left = 0;
         rect.right = szTotal.cx;
         rect.top = i*10;
         rect.bottom = (i+1)*10;
         pDC->FillSolidRect(&rect, RGB((i*10)%255,0,0));
     }
 
     /*绘制图片*/
     CDC dcPic;
     dcPic.CreateCompatibleDC(pDC);
     CBitmap bmpImport;
     bmpImport.LoadBitmap(IDB_BITMAP1);
     CBitmap* pOldBmp = dcPic.SelectObject(&bmpImport);
     BITMAP bitmap;
     bmpImport.GetBitmap(&bitmap);
     int  iWidth = bitmap.bmWidth;
     int  iHeight = bitmap.bmHeight;
     pDC->BitBlt(0, 0, iWidth, iHeight,
         &dcPic, 0, 0, SRCCOPY);
     dcPic.SelectObject(pOldBmp);
 
     /*释放资源*/
     bmpImport.DeleteObject();
     dcPic.DeleteDC();
}

 

二、双缓存
产生闪屏的缘由是相似于多进程之间的通讯问题,每次DC的绘图操做,都要把相关的显示数据发送到显卡,显卡处理后,在显示器上显示。借鉴提高多线程之间的通讯效率的解决方法,可经过减小与显卡之间的交互次数来提高绘制的效率。这也就是双缓存的思路。双缓存的原理是先把更新操做中全部绘制数据先写入内存,而后再调用BitBlt或StretchBlt一次性的把全部数据发送到显卡中。
用一个比喻来讲,没有用双缓存就像老师讲课时在黑板上使用粉笔写板书,学生能看到老师写板书的整个过程,若是把黑板看作是一个屏幕的话,老师在写板书的过程,就是一个闪屏的过程。
使用了双缓存,就像老师采用了多媒体教学,老师就能够提早在家把板书用PPT作好,上课时只要一页一页的翻PPT就能够了,学生们是看不到PPT制做的过程,也就会有闪屏的问题了。
可采用了以下代码来实现双缓存。函数

void  CDoubleBufferView::DrawWithBufferInefficient(CDC* pDC)
{
     ASSERT_VALID(pDC);
 
     /*建立内存DC*/
     CDC dcMemory;
     dcMemory.CreateCompatibleDC(pDC);
     dcMemory.SetBkColor(pDC->GetBkColor());
     
     /*设置内存DC的画板,大小整个窗口同样*/
     CSize szTotal = GetTotalSize();
     CBitmap bmpMemory;
     bmpMemory.CreateCompatibleBitmap(pDC,
         szTotal.cx, szTotal.cy);
     dcMemory.SelectObject(&bmpMemory);
     
     /*设置内存DC的起始点*/
     dcMemory.SetViewportOrg(0, 0);
     
     /*粉刷背景*/
     dcMemory.FillSolidRect(0, 0, szTotal.cx, szTotal.cy, pDC->GetBkColor());
     
     DrawDirect(&dcMemory);
     
     /*把内存DC复制到输入DC中*/
     pDC->BitBlt(0, 0, szTotal.cx, szTotal.cy,
         &dcMemory, 0, 0, SRCCOPY);
     
     /*释放资源*/
     bmpMemory.DeleteObject();
     dcMemory.DeleteDC();
}

 

三、取消擦除背景
当咱们使用了上面的双缓存技术后,咱们看到闪屏问题有所缓解,可是有些操做仍然会致使闪屏,好比在有滚动条的视图窗口拖动滚动条时。为何会产生这样的缘由呢?先来分析一下界面的绘制原理。
当咱们须要对窗口绘制时,可调用UpdateWindow、RedrawWindow、Invalidate或InvalidateRect等函数。经过查看这些函数的MSDN中得知,有些窗口绘制函数中,有一个是否擦除背景的选项。若是要擦除背景,那一次绘制就要进行两部操做,也就是要跟显卡交互两次,一是擦除背景,一是从新绘制图形,那双缓存的做用就失去了。
若是咱们再绘制图形的时候,本身对背景进行一次粉刷,也就没有必要再使用擦除背景,同时也能保证双缓存的效果。
取消擦除北京的方法主要有两种:
(1)取消重绘时的擦除选项。
如使用Invalidate(FALSE)。
这种方法虽然有效,可是须要对全部的重绘函数进行处理,难以彻底取消擦除背景。
(2)截断擦除消息。
背景的擦除是经过WM_ERASEBKGND消息来完成。因而,咱们只要截获了该消息,就能完全取消擦除背景。
可在窗口类中为WM_ERASEBKGND提供消息响应函数,而后直接返回TRUE。post

BOOL  CDoubleBufferView::OnEraseBkgnd(CDC* pDC)
{
     // TODO: Add your message handler code here and/or call default
     
     return  TRUE;
     //return CScrollView::OnEraseBkgnd(pDC);
}

 

四、绘制效率的提高
在刷新界面的时候,刷新的区域越小,刷新效率更高,所以,在刷新界面的时候,咱们应该尽可能较少没必要要的刷新。操做系统也会对界面的刷新操做进行优化,如拉动滚动条的时候,并非对整个界面进行刷新,而只是对已经无效的区域中换上新的图形,而后再在屏幕调整图形区域在界面上的位置。所以,就有一个裁剪区域的概念,在重绘的过程当中,只有裁剪区域须要重绘。所以,咱们在双缓存中,也只需对裁剪区域重绘。可经过CDC::GetClipBox来得到裁剪区域的大小。
所以,对双缓存的优化代码以下所示:测试

void  CDoubleBufferView::DrawWithBufferEfficient(CDC* pDC)
{
     ASSERT_VALID(pDC);
 
     /*建立内存DC*/
     CDC dcMemory;
     dcMemory.CreateCompatibleDC(pDC);
     dcMemory.SetBkColor(pDC->GetBkColor());
     
     /*设置内存DC的画板,大小与输入DC的裁剪区域同样*/
     /*只对裁剪区域进行从新绘制*/
     CRect rectClip(0,0,0,0);
     pDC->GetClipBox(&rectClip);
     CBitmap bmpMemory;
     bmpMemory.CreateCompatibleBitmap(pDC,
         rectClip.Width(), rectClip.Height());
     dcMemory.SelectObject(&bmpMemory);
     
     /*设置内存DC的起始点*/
     dcMemory.SetViewportOrg(-1*rectClip.left, -1*rectClip.top);
 
     /*粉刷背景*/
     dcMemory.FillSolidRect(&rectClip, pDC->GetBkColor());
 
     DrawDirect(&dcMemory);
 
     /*把内存DC复制到输入DC中*/
     pDC->BitBlt(rectClip.left, rectClip.top, rectClip.Width(), rectClip.Height(),
         &dcMemory, rectClip.left, rectClip.top, SRCCOPY);
     
     /*释放资源*/
     bmpMemory.DeleteObject();
     dcMemory.DeleteDC();
}

 五、工程源代码下载优化

http://files.cnblogs.com/xianyunhe/DoubleBuffer.rarspa

相关文章
相关标签/搜索