MFCはダブルバッファを利用して画面のフラッシュ無しリフレッシュを実現します.

4132 ワード

回転:http://hi.baidu.com/doriahe/blog/item/869e3dc603ee04c638db4950.html
1、表示された図形がなぜ点滅したかは、私たちの描画過程の多くがOnDrawまたはOnPaint関数において、OnDrawは画面表示時にOnPaintによって呼び出されます.ウィンドウが何らかの理由で再描画が必要な場合は、常に背景色で表示エリアをクリアしてからOnPaintを呼び出しますが、背景色は描画内容とは大きく異なる場合があります.このように、短い時間で背景色と表示パターンが交互に現れて、表示ウィンドウがフラッシュしているように見えます.背景ブラシをNULLにセットすれば、いくら塗り替えても点滅しません.もちろん、このようにすると、ウィンドウの表示を混乱させてしまいます.背景色がないので、元の図形をクリアしたり、新しい図形を重ねたりします.点滅は図形描画の速度が遅すぎたり、表示のパターンが複雑すぎたりするためという人もいますが、実はそうではなく、点滅に対する描画の表示速度の影響は根本的ではありません.例えばOnDraw(CDC*pDC)には、次のように書く.
 pDC->MoveTo(0,0);
 pDC->LineTo(100,100);
この製図過程はとても簡単で、とても速いでしょう.でも、窓を動かすと点滅が見えます.実際には、絵を描く過程が複雑になるほど、点滅が少なくなるはずです.絵を描く時間と背景で画面を消す時間の割合が大人になるほど、点滅に対する感覚がはっきりしないからです.例えば、画面時間が1 sであることを確認しても1 sです.このように10 s以内の連続した再描画で5回点滅します.スクリーン時間が1 sであることが明らかであれば、描画時間は9 sであり、このように10 s以内の連続再描画は一回だけ点滅します.これもテストできます.OnDraw(CDC*pDC)にはこう書きます.
for(int i=0;i<100000;i++)
{
 pDC->MoveTo(0,i);
 pDC->LineTo(1000,i);
}
プログラムは極端ですが、問題を説明することができます.ここでまた言いたい人がいるかもしれませんが、なぜ簡単な図形は複雑な図形ほどキラキラしないですか?これは複雑な図形が占める面積が大きいため、書き直した時のギャップが大きいので、強いフラッシュを感じますが、フリッカの周波数は低いです.なぜアニメの描き直しの頻度が高いのですか?ここでもう一度強調します.点滅は何ですか?点滅はコントラストで、コントラストが大きいほど、きらめきが強くなります.アニメーションの連続二フレームの差が小さいので、フラッシュしないように見えます.信じないなら、動画の各フレームに純白のフレームを加えてもいいです.
2、解決方法:グラフィック処理プログラムの過程で、ダブルバッファは基本的な技術です.フォームがWM_に応答している場合は知っています.PAINTメッセージの場合は複雑なグラフィックス処理を行いますが、フォームは再描画時にオーバーコンバートによって点滅します.この問題を解決する効果的な方法は二重バッファ技術です.
フォームが更新されると、元のイメージを消去するプロセスが常にありますので、フォームの描画エリアに背景色を塗りつぶして、新しいグラフィックコードを呼び出して再描画します.このように書くと画像の色の差が出ます.WMになるPAINTの応答が頻繁な時には、このギャップがますます顕著になります.そこで私たちは点滅現象を見ました.
背景色の塗りつぶしを避けるのが一番直接的な方法だと自然に思います.しかし、フォームはめちゃくちゃになります.画像を描くたびに元のイメージをクリアしないので、画像が残ります.フォームを塗り替えると、画面がめちゃくちゃになります.だから単純に背景の塗り替えを禁止するのは足りません.再描画もしますが、速度が速いことが要求されますので、BitBlt関数の使用を考えました.図のブロックのコピーをサポートします.速度はとても速いです.私達は先にメモリの中で図を作ることができて、それからこの関数でしっかりと行った図をフロントにコピーして、同時に背景の更新を禁止して、このようにフラッシュを取り除きました.以上、つまりダブルバッファの基本的な考え方です.
3、具体的な手順:仮に我々がDrawの工事を立てたとしたら、我々はDrawViewの中で製図作業を行います.ダブルバッファ法では、まずシールドバックグラウンドのリフレッシュが必要です.背景の更新は実はWM_に応答しています.ERASEBKGNDメッセージ.このメッセージに対する応答をビュー(CDrawView)に追加します.デフォルトのコードは以下の通りです.
BOOL CDrawView::OnEraseBkgnd(CDC* pDC) 
 {
       //return CDrawView::OnEraseBkgnd(pDC);
  return TRUE;
 }

次に、ダブルバッファの実現ステップです.(1)メンバー変数を追加します.(DrawView.hファイルで)
//    
  CBitmap* m_pOldBitmap;
   CBitmap* m_pMemBitmap; //              
  CDC* m_pMemDC;   //           DC

(2)初期化変数(DrawViewの構築関数で)
 m_pMemDC=new CDC();
 m_pMemBitmap=new CBitmap();

(3)メッセージ応答関数WM_を追加する.CREATE(DrawView.cpp中)
int CDrawView::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
   if (CView::OnCreate(lpCreateStruct) == -1)
    return -1;

   int x=GetSystemMetrics(SM_CXSCREEN);
   int y=GetSystemMetrics(SM_CYSCREEN);

  CDC* pDC=GetDC();
  m_pMemDC->CreateCompatibleDC(pDC); //    DC      DC
  m_pMemBitmap->CreateCompatibleBitmap(pDC,x,y); //      
   m_pOldBitmap=m_pMemDC->SelectObject(m_pMemBitmap); //       DC,      m_pOldBitmap

   CBrush brush(RGB(255,255,255));
  m_pMemDC->FillRect(CRect(0,0,x,y),&brush);    //          

  ReleaseDC(pDC);

  return 0;
 }

(4)OnDraw()関数の修正(DrawView.cpp中)
 void CDrawView::OnDraw(CDC* pDC)
  {
   CDocument* pDoc = GetDocument();
   CRect rc;
   GetClientRect(&rc);
   DrawSomething();   //                
   pDC->BitBlt(0,0,rc.Width(),rc.Height(),m_pMemDC,0,0,SRCCOPY);
    //                    buffer 
  }

(5)自分の図形描画関数(DrawView.cpp中)
void DrawSomething()
  {
      m_pMemDC->Rectangle(0,0,100,100);        //       
       Invalidate();
  }

(6)delete掉new的东西(DrawView的析构関数中)
delete m_pBitmap;
delete m_pMemDC;