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

VC++ 雙緩衝繪圖徹底解決界面閃爍:原理、實現與優化

     編輯:VC++

閃爍根因分析

MFC 默認繪圖流程:OnPaint → OnEraseBkgnd (填充背景刷) → OnDraw (業務繪圖)。背景色與前景圖形反差大、頻繁重繪 (調整大小、遮擋恢復) 導致視覺閃爍。單純設置背景刷為 NULL 雖消除閃爍,但會造成舊圖形殘留疊加混亂。

雙緩衝核心原理

在內存創建兼容 DC 與位圖,所有繪圖操作導向內存 DC,最後一次性 BitBlt 到屏幕 DC。內存繪圖過程不可見,屏幕僅呈現最終幀,無閃爍。

標準實現模板 (OnDraw)

void CMyView::OnDraw(CDC* pDC) {
    CRect rect;
    GetClientRect(&rect);
    int nWidth = rect.Width(), nHeight = rect.Height();

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

    // 1. 填充背景 (內存中無閃爍)
    memDC.FillSolidRect(0, 0, nWidth, nHeight, RGB(255,255,255));

    // 2. 業務繪圖 (全部針對 memDC)
    memDC.MoveTo(0,0); memDC.LineTo(100,100);
    // ... 復雜繪圖邏輯 ...

    // 3. 一次性傳輸到屏幕
    pDC->BitBlt(0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);

    // 4. 清理 (恢復舊位圖,刪除對象)
    memDC.SelectObject(pOldBmp);
    memBmp.DeleteObject();
    memDC.DeleteDC();
}

優化:裁剪區剪枝

pDC->GetClipBox(&rectClip) 獲取無效區矩形。繪圖前判斷圖形是否與 rectClip 相交,不相交則跳過,大幅減少復雜場景繪圖開銷。

完整案例:無閃爍背景圖繪制 (OnEraseBkgnd)

BOOL CMyView::OnEraseBkgnd(CDC* pDC) {
    CRect rect; GetClientRect(&rect);
    int w = rect.Width(), h = rect.Height();

    CDC memDC; CBitmap memBmp;
    memDC.CreateCompatibleDC(pDC);
    memBmp.CreateCompatibleBitmap(pDC, w, h);
    CBitmap* pOld = memDC.SelectObject(&memBmp);

    // 背景色
    memDC.FillSolidRect(0, 0, w, h, RGB(255,255,255));

    // 居中繪制背景圖片 m_bmp (CBitmap 成員,構造函數 LoadBitmap)
    BITMAP bm; m_bmp.GetObject(sizeof(bm), &bm);
    CDC imgDC; imgDC.CreateCompatibleDC(pDC);
    CBitmap* pOldImg = imgDC.SelectObject(&m_bmp);
    memDC.BitBlt((w - bm.bmWidth)/2, (h - bm.bmHeight)/2,
                 bm.bmWidth, bm.bmHeight, &imgDC, 0, 0, SRCCOPY);
    imgDC.SelectObject(pOldImg);

    // 輸出到屏幕
    pDC->BitBlt(0, 0, w, h, &memDC, 0, 0, SRCCOPY);

    memBmp.DeleteObject(); memDC.DeleteDC();
    return TRUE; // 關鍵:返回 TRUE 告訴系統背景已處理,阻止默認 OnEraseBkgnd
}

關鍵點總結

  • OnEraseBkgnd 返回 TRUE:阻止系統默認背景擦除,由自己全權繪制。
  • 兼容 DC/位圖尺寸:必須與目標 DC 尺寸一致,否則 BitBlt 會拉伸或截斷。
  • 對象生命週期:SelectObject 返回舊對象指針,繪制結束前必須 SelectObject(pOld) 恢復,然後 DeleteObject 刪除新建對象,防止 GDI 泄漏。
  • 高頻重繪場景:建議將內存 DC/位圖作為成員變量創建一次,OnSize 變化時重建,避免每幀分銷毀開銷。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved