Duilib的雙緩衝實現,附帶GDI、WTL的雙緩衝實現

釣徒發表於2021-06-29

前言:

 閃爍問題,之前的經驗是使用雙緩衝,藉此機會,把雙緩衝的研究心得總結下。

 

雙緩衝的含義:

           緩衝這個詞,相信大家都不陌生,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

        

 

     

 

    

 

 

         然後作畫,細節暫時略,後續原始碼剖析會補充,敬請期待。

        

 

 

 

          最後,拷貝到顯示器緩衝。

        

 

相關文章