前言:
閃爍問題,之前的經驗是使用雙緩沖,借此機會,把雙緩沖的研究心得總結下。
雙緩沖的含義:
緩沖這個詞,相信大家都不陌生,Cache。主要是為了解決上下遊(或者子產品、或者系統)等性能不比對問題。如果把上遊看成“生産者”,下遊看成“消費者”,當“生産者”與“消費者”的處理速度不同時,為了避免幹等,中間會加一些緩沖區。

無緩沖,雙方都容易阻塞。
最常用的生産者-消費者模型,增加彈性緩沖,缺點是每生産+每消費都需要加鎖
雙緩沖,等消費者隊列為空時加鎖,執行exchange(如swap方法),大大減少了加鎖次數。
雙緩沖在界面的使用:
在圖形圖象處理程式設計過程中,雙緩沖是一種基本的技術。我們知道,如果窗體在響應WM_PAINT消息的時候要進行複雜的圖形處理,那麼窗體在重繪時由于過頻的重新整理而引起閃爍現象。解決這一問題的有效方法就是雙緩沖技術。因為窗體在重新整理時,總要有一個擦除原來圖象的過程OnEraseBkgnd,它利用背景色填充窗體繪圖區,然後在調用新的繪圖代碼進行重繪,這樣一擦一寫造成了圖象顔色的反差。當WM_PAINT的響應很頻繁的時候,這種反差也就越發明顯。于是我們就看到了閃爍現象。
雙緩沖我們會很自然的想到,避免背景色的填充是最直接的辦法。但是那樣的話,窗體上會變的一團糟。因為每次繪制圖象的時候都沒有将原來的圖象清除,造 成了圖象的殘留,于是窗體重繪時,畫面往往會變的亂七八糟。是以單純的禁止背景重繪是不夠的。我們還要進行重新繪圖,但要求速度很快,于是我們想到了使用 BitBlt函數。它可以支援圖形塊的複制,速度很快。我們可以先在記憶體中作圖,然後用此函數将做好的圖複制到前台,同時禁止背景重新整理,這樣就消除了閃 爍。以上也就是雙緩沖繪圖的基本的思路。
GDI雙緩沖:
WM_ERASEBKGND : 傳回1,禁止背景重繪
HDC hDc = ::GetDC( m_hWnd) ;
RECT rcDest;
HDC hMemDc = CreateCompatibleDC(hDc);
HBITMAP hMemBitmap = CreateCompatibleBitmap(hDc); //記憶體畫布,放到記憶體DC中就可以在記憶體中畫圖了
HBITMAP hOldBitmap = ::SelectObject (hDC, hBitmap);
//....hMemDc作畫
::BitBlt (m_hDestDC, rc.left, rc.top, rc.Width(), rc.Height(), hDC, rc.left, rc.top, SRCCOPY); //拷貝到顯示器緩沖
//資源釋放與還原
::SelectObject (hDC, hOldBitmap);
DeleteObject(hMemBitmap);
DeleteDC(hMemDc);
ReleaseDC(m_hWnd,hDc);
WTL雙緩沖:
CDoubleBufferImpl 在AtlFrame.h中。
1.首先繼承自CDoubleBufferImpl
class TCtrl:
public CWindowImpl< TCtrl>,
public WTL::CDoubleBufferImpl<TCtrl> // 繼承雙緩沖類
2.由于雙緩沖類中已經處理了WM_ERASEBKGND 和WM_PAINT消息,是以需要從你的代碼中删除對這些消息的處理。然後加上雙緩沖的消息處理即可。
BEGIN_MSG_MAP(TCtrl)
// MESSAGE_HANDLER(WM_PAINT, OnPaint)
CHAIN_MSG_MAP( WTL::CDoubleBufferImpl<TCtrl>)
END_MSG_MAP()
3.增加一個DoPaint函數,函數聲明如下:
void DoPaint(CDCHandle dc);
4.将原來OnPaint函數中的代碼移到DoPaint中,注意原來的CPaintDC需要改用參數中的CDCHandler
void TCtrl::DoPaint( CDCHandle dc )
{
//CPaintDC dc(m_hWnd);
dc.MoveTo( xx… )
}
Duilib雙緩沖:
Duilib使用的GDI+引擎,也已經支援雙緩沖,這裡簡單介紹下它的實作過程
1、Duilib也預設處理了WM_REASEBKGND 并傳回1;
2、IRenderContext 類負責渲染。
目前由RenderContext_GdiPlus來實作。
1)構造函數中,建立記憶體DC
2) 擷取繪圖區域後,建立記憶體Bitmap
然後作畫,細節暫時略,後續源碼剖析會補充,敬請期待。
最後,拷貝到顯示器緩沖。