2010年9月28日 星期二

利用 memory dc 解決畫面閃爍問題

重繪時要避免畫面閃爍可以使用 Invalidate(FALSE);
使重繪時不做清除底圖的動作,減少閃爍的情況,

但並不是所有的情況都可以不清除底圖直接繪上新圖,
例如若要在花色背景上打字,為了不讓字產生底色,我們會將DC的屬性設為透明,


dc.SetBkMode(TRANSPARENT);


例如先前已經印上 99%,不清除背景的狀況上更新為 100%,
在透明字體的情況下就會發生字體重疊的囧況,
為免這種情況發生我們只好重新再把花色背景畫上去一次,
如此就可以在不清除背景的情況下讓字不會重疊,
但是重畫背景是粍時的工作,顯示卡在螢幕上畫出每一條線也可能造成閃爍,
如此我們就要想辦法不要一直在螢幕上畫圖,使用 Memory DC 是個好方法。

原本使用 CWnd 的 dc.DrawText(); dc.LineTo(); 等等這些動作,
其實每條指令都會佔用顯示卡請它將資訊輸出至螢幕,相當粍時,
現在我們改成在記憶體中的一塊 dc 上作畫,
將佔用顯示卡的頻率降至將 memory dc 貼到 CWnd dc 上這麼一次就好,
貼的動作是使用記憶體複製,遠比一條一條指令請顯示卡畫快多了,

使用Memory DC的範例如下


// Dlg.h

class CDlg : CDialog
{
private:
  CDC m_dcMem; // 宣告一塊畫布
  CBitmap m_bmp; // 宣告一張點陣圖
  CFont m_fontSet; // 宣告一個字型
};




// Dlg.cpp

BOOL CDlg::OnInitDialog()
{
  CDialog::OnInitDialog();
  SetIcon(m_hIcon, TRUE); // 設定大圖示
  SetIcon(m_hIcon, FALSE); // 設定小圖示

  CDC* pDC = GetDC(); // 取得目前視窗的DC
  m_dcMem.CreateCompatibleDC(NULL); // 初始化MemDC
  // 也可以指定和 CWnd DC 一樣的格式 m_dcMem.CreateCompatibleDC(pDc);
  // 將m_bmp初始化為C_MAXWIDTH、C_MAXHEIGHT大小,並指定和pDC相同格式
  m_bmp.CreateCompatibleBitmap(pDC, C_MAXWIDTH, C_MAXHEIGHT);
  m_dcMem.SelectObject(&m_bmp); // Mem DC選擇一張點陣圖
  m_dcMem.SetBkMode(TRANSPARENT); // 視需求設為透明底圖
  ReleaseDC(pDC); // 釋放CWnd DC

  // 當然 Memory DC 也可以設定字型、筆刷等條件
  m_dcMem.SelectObject(m_fontSet);

  return TRUE;
}


這時我們可以在任意時間利用 dcMem.DrawText() 等方式畫上資訊,
當然畫面都不會有任何改變,因為我們畫的是記憶體中的畫布,
在 OnPaint() 時我們再將 dcMem 貼到 CWnd 的 DC 上,


void ChundredSingerDlg::OnPaint()
{
  CPaintDC dc(this);
  dc.BitBlt(0, 0,
            m_rcClient.Width(),
            m_rcClient.Height(), /* 以上4個參數指定貼到CWnd DC的哪裡 */
            &m_dcMem, /* 來源 */
            0, 0, /* 從來源的哪個座標開始貼 */
            SRCCOPY /* 使用複製的方式貼上 */);
}


如此不論我們對 Memory DC 做多複雜的指令,畫面也不會閃爍,
要更新資訊時,只需要使用非常快速的記憶體複製貼上 Memory DC 即可。

Memory DC 不一定要宣告為類別成員物件,
做為 OnPaint() 裡面的 local variable 再一次畫好貼上也可以,
但如此就必須每次 OnPaint 時都做一次 Mem DC 的初始化動作,
所以我習慣將 Memory DC 與 Bitmap 宣告為類別成員省去常常初始化的時間。

沒有留言:

張貼留言