1. 程式人生 > >MFC雙緩衝繪圖

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;
}

    當然,實際開發中根據具體業務,在程式初始化時候就應該申請匹配的裝置描述表和點陣圖物件,中間影象的切換進行繪畫,程式結束時候進行資源釋放。