首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 雙重緩衝實現無閃爍繪圖:原理、實作與效能優化完整教學

     編輯:VC 教學

前言:為什麼會閃爍?

MFC 繪圖流程:OnPaint → 擦除背景 (WM_ERASEBKGND) → OnDraw 繪圖。背景色與繪圖內容反差大,短時間內交替出現造成閃爍。繪圖速度快慢非根本原因——簡單畫線也會閃,複雜圖形反而因繪圖時間長、閃爍頻率低而感覺不嚴重。動畫不閃是因連續幀差異小,非繪圖快。

解決之道:雙重緩衝

核心概念:記憶體中完整繪製好一幀 → 一次性 BitBlt 貼到螢幕。記憶體繪圖過程不可見,貼圖過程極快且無反差,徹底消除閃爍。

標準雙重緩衝實作模板 (OnDraw / OnPaint)

void CMyView::OnDraw(CDC* pDC)
{
    // 1. 取得繪圖區域大小
    CRect rect;
    GetClientRect(&rect);
    int nWidth = rect.Width();
    int nHeight = rect.Height();

    // 2. 建立記憶體 DC 與相容位圖
    CDC memDC;
    CBitmap memBitmap;
    memDC.CreateCompatibleDC(pDC);
    memBitmap.CreateCompatibleBitmap(pDC, nWidth, nHeight);
    CBitmap* pOldBitmap = memDC.SelectObject(&memBitmap);

    // 3. 記憶體 DC 清背景
    memDC.FillSolidRect(0, 0, nWidth, nHeight, RGB(255,255,255));

    // 4. 在記憶體 DC 上繪圖 (所有繪圖操作改用 memDC)
    memDC.MoveTo(0, 0);
    memDC.LineTo(100, 100);
    // ... 複雜繪圖邏輯 ...

    // 5. 一次性貼到螢幕
    pDC->BitBlt(0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);

    // 6. 清理
    memDC.SelectObject(pOldBitmap);
    memBitmap.DeleteObject();
    memDC.DeleteDC();
}

進階:禁止預設背景擦除

雙重緩衝已包含清背景步驟,必須禁止 MFC 預設 WM_ERASEBKGND:

BOOL CMyView::OnEraseBkgnd(CDC* pDC)
{
    return TRUE;  // 告訴系統「我已處理背景」,不再執行預設擦除
}

完整範例:無閃爍背景圖繪製 (OnEraseBkgnd)

BOOL CStrucView::OnEraseBkgnd(CDC* pDC)
{
    CRect rect;
    GetClientRect(&rect);
    int nWidth = rect.Width(), nHeight = rect.Height();

    CDC memDC;
    CBitmap memBitmap;
    memDC.CreateCompatibleDC(pDC);
    memBitmap.CreateCompatibleBitmap(pDC, nWidth, nHeight);
    CBitmap* pOldBit = memDC.SelectObject(&memBitmap);

    // 1. 背景色
    memDC.FillSolidRect(0, 0, nWidth, nHeight, RGB(255,255,255));

    // 2. 置中繪製背景圖片 (m_bmp 為 CBitmap 成員,建構函數 LoadBitmap)
    BITMAP bm;
    m_bmp.GetObject(sizeof(bm), &bm);
    CDC dcMem;
    dcMem.CreateCompatibleDC(pDC);
    CBitmap* pOldBmp = dcMem.SelectObject(&m_bmp);
    memDC.BitBlt((nWidth - bm.bmWidth)/2, (nHeight - bm.bmHeight)/2,
                 bm.bmWidth, bm.bmHeight, &dcMem, 0, 0, SRCCOPY);
    dcMem.SelectObject(pOldBmp);

    // 3. 貼到螢幕
    pDC->BitBlt(0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);

    memBitmap.DeleteObject();
    memDC.DeleteDC();
    return TRUE;
}

效能優化:裁剪區

MFC 自動設定 pDC 的裁剪區,僅重繪受損區域。複雜繪圖時可主動利用:

CRect clipRect;
pDC->GetClipBox(&clipRect);  // 取得裁剪矩形

// 繪圖前判斷圖元是否在裁剪區內
if (CRect(objLeft, objTop, objRight, objBottom).IntersectRect(&clipRect, ...)) {
    // 僅繪製可見部分
    memDC.DrawSomething(...);
}

簡單繪圖判斷開銷可能大於繪圖本身,視複雜度決定是否啟用。

關鍵點總結

技巧作用
OnEraseBkgnd return TRUE禁止預設背景擦除,避免雙重清背景
CreateCompatibleDC + CreateCompatibleBitmap建立螢幕相容的記憶體繪圖表面
BitBlt 一次性貼圖極快的記憶體複製,無閃爍
GetClipBox 裁剪區優化跳過不可見區域繪圖,提升複雜場景效能

結語

雙重緩衝是解決 MFC 繪圖閃爍的標準答案。掌握「記憶體 DC → 完整繪製 → BitBlt → 禁止預設擦除」標準流程,並善用裁剪區優化複雜場景,即可實現專業級無閃爍繪圖體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved