【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

相關文章
相關標籤/搜索