首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

MFC 繪圖閃爍問題徹底解決:雙緩衝、局部刷新與 Direct2D 三種方案實戰

     編輯:程式設計綜合問答

MFC 開發中,InvalidateRect(&rc, FALSE) 觸發全客戶區重繪,若在 OnPaint 直接對 CPaintDC 繪製複雜圖形,必然產生嚴重閃爍。網路上常見的「雙緩衝」程式碼讓初學者望而卻步。本文提供三種由易到難、各有適用場景的解決方案,附完整可複製貼上的核心程式碼。

方案一:記憶體 DC 雙緩衝(最經典、相容性最好)

原理:在記憶體位圖上繪圖完成後,一次性 BitBlt 到螢幕 DC,避免使用者看到中間過程。

void CMyView::OnPaint() {
    CPaintDC dc(this); // 螢幕 DC
    CRect rect;
    GetClientRect(&rect);

    // 1. 建立相容記憶體 DC 與位圖
    CDC memDC;
    memDC.CreateCompatibleDC(&dc);
    CBitmap bitmap;
    bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
    CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);

    // 2. 擦除背景(關鍵:填滿背景色,否則透明殘影)
    memDC.FillSolidRect(rect, ::GetSysColor(COLOR_WINDOW));

    // 3. 在 memDC 上進行所有繪圖操作
    DrawAllGraphics(&memDC); // 你的繪圖邏輯

    // 4. 一次性複製到螢幕
    dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);

    // 5. 清理
    memDC.SelectObject(pOldBitmap);
}

關鍵細節:FillSolidRect 必須在繪圖前呼叫,否則未繪製區域會顯示上一幀殘影(因位圖預設未初始化)。

方案二:局部刷新 + WM_ERASEBKGND 抑制(簡單場景有效)

若圖形更新區域固定(如只有某個矩形變動),可只刷新該區域並禁止系統擦除背景。

// 1. 僅刷新變動區域
CRect updateRect(100, 100, 200, 200); // 計算出的髒矩形
InvalidateRect(&updateRect, FALSE); // FALSE = 不擦除背景

// 2. 在視圖類覆寫 OnEraseBkgnd 返回 TRUE(告訴系統「我自己處理背景」) 
BOOL CMyView::OnEraseBkgnd(CDC* pDC) {
    return TRUE; // 禁止預設擦除,減少閃爍來源
}

// 3. OnPaint 中只繪製 updateRect 內容
void CMyView::OnPaint() {
    CPaintDC dc(this);
    CRect clipBox;
    dc.GetClipBox(&clipBox); // 取得實際需重繪區域
    DrawGraphicsInRect(&dc, clipBox);
}

適用場景:簡單動畫、滑鼠拖曳回饋、局部資料更新。**不適用**於全畫面複雜向量圖形重繪。

方案三:Direct2D 硬體加速渲染(現代、高效能、Win7+)

MFC 從 VS2010 (MFC 10) 起內建 CD2DRenderTarget 支援,利用 GPU 渲染徹底消除閃爍,且支援抗鋸齒、幾何變換。

// 1. 標頭檔宣告
#include 
CD2DRenderTarget* m_pD2DRenderTarget;

// 2. OnInitialUpdate 或 OnCreate 建立
HRESULT CMyView::CreateD2DResources() {
    if (m_pD2DRenderTarget) return S_OK;
    CRect rect; GetClientRect(&rect);
    m_pD2DRenderTarget = new CD2DRenderTarget();
    HRESULT hr = m_pD2DRenderTarget->Create(this, rect.Size());
    if (SUCCEEDED(hr)) {
        // 建立筆刷、幾何體等資源
        m_pD2DRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Blue), &m_pBrush);
    }
    return hr;
}

// 3. OnRender 繪圖(MFC 自動呼叫,雙緩衝內建)
void CMyView::OnRender(CD2DRenderTarget* pRenderTarget, const CRect& rect) {
    pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
    pRenderTarget->DrawRectangle(D2D1::RectF(10,10,100,100), m_pBrush, 2.0f);
    // 繪製文字、圖片、幾何路徑...
}

// 4. 視窗大小改變時重建
void CMyView::OnSize(UINT nType, int cx, int cy) {
    CView::OnSize(nType, cx, cy);
    if (m_pD2DRenderTarget) {
        m_pD2DRenderTarget->Resize(CD2DSizeF(cx, cy));
    }
}

優勢:零閃爍、高效能、現代圖形 API、**自動雙緩衝**。
要求:目標機需安裝 Platform Update for Windows 7 或更新版本(Win8+ 內建)。

方案選擇決策樹

| 專案現況 | 推薦方案 | 理由 | |----------|----------|------| | 老專案、需支援 XP/Win7 無平台更新 | 方案一 (GDI 雙緩衝) | 相容性最好、零依賴 | | 只有局部區域頻繁更新 | 方案二 (局部刷新) | 程式碼最少、效能足夠 | | 新專案、目標 Win7 SP1+/Win10/11、追求高效能/抗鋸齒 | 方案三 (Direct2D) | 最現代、長期維護成本最低 |

常見錯誤避坑

  • InvalidateRect(..., TRUE) + OnEraseBkgnd 返回 TRUE → 背景被擦兩次,閃爍加劇。
  • 雙緩衝位圖尺寸與客戶區不一致 → 邊緣殘影或拉伸變形。
  • Direct2D 資源(筆刷、幾何體)未在 CreateD2DResources 中建立,反覆在 OnRender new → 嚴重洩漏與效能下降。

根據專案約束選擇合適方案,MFC 繪圖閃爍問題皆可徹底解決,提升應用專業觀感。

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