在 MFC 開發中,圖形閃爍是許多開發者頭疼的問題。許多人認為 MFC 繪圖效率低下,紛紛尋求其他方案。事實上,MFC 繪圖效率並不差,關鍵在於掌握正確的技巧。本文將深入剖析閃爍成因,並介紹雙緩衝與裁剪區優化技術,助你編寫高效、無閃爍的繪圖程式。
MFC 的繪圖邏輯通常放在 OnDraw 或 OnPaint 中。當視窗需要重繪時,系統會先以背景色填充顯示區域,隨後才呼叫 OnPaint 進行實際繪圖。背景色與繪圖內容反差大,導致短時間內「背景→圖形」交替出現,肉眼便感知到閃爍。
關鍵在於:閃爍本質是「反差」與「時間差」。即使繼圖極簡單(如畫一條線),只要背景擦除動作存在,閃爍就會發生。繪圖越複雜、耗時越長,反而因背景擦除佔比降低,閃爍感覺越不明顯。
首先必須阻止系統自動擦除背景,常用方法有三種:
NULLSetClassLong(..., GCL_HBRBACKGROUND, NULL)OnEraseBkgnd 直接回傳 TRUE(最簡單)但僅禁用背景會導致舊圖形殘留、疊加混亂,必須配合雙緩衝。
在記憶體中建立一個與螢幕相容的裝置環境(Memory DC)與位圖,先在記憶體中完成所有繪圖動作(含背景填充),最後一次性用 BitBlt 將記憶體位圖拷貝到螢幕。因為記憶體操作不可見,螢幕只呈現最終結果,自然無閃爍。
void CMyView::OnDraw(CDC* pDC)
{
// 1. 建立記憶體 DC 與位圖
CDC memDC;
CBitmap memBitmap;
memDC.CreateCompatibleDC(pDC);
memBitmap.CreateCompatibleBitmap(pDC, nWidth, nHeight);
CBitmap* pOldBmp = memDC.SelectObject(&memBitmap);
// 2. 在記憶體中繪圖(含背景填充)
memDC.FillSolidRect(0, 0, nWidth, nHeight, RGB(255,255,255)); // 白底
memDC.MoveTo(...);
memDC.LineTo(...);
// ... 其他繪圖指令 ...
// 3. 一次性輸出到螢幕
pDC->BitBlt(0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);
// 4. 清理資源
memDC.SelectObject(pOldBmp);
memBitmap.DeleteObject();
memDC.DeleteDC();
}
當場景包含成千上萬個元件(如電力系統 CAD),全量重繪極其緩慢。MFC 已內建裁剪區機制:OnDraw 只會實際繪製落在裁剪區內的圖形。我們可以進一步在程式碼中判斷元件是否在裁剪區內,跳過區外繪圖運算:
CRect clipRect;
pDC->GetClipBox(&clipRect); // 取得裁剪區
foreach (元件 in 場景) {
if (元件.BoundingRect().IntersectRect(clipRect)) {
元件.Draw(pDC); // 僅繪製可見元件
}
}
此法對簡單場景提升有限,但對大型複雜場景效果顯著。
MFC 繪圖不閃爍、高效能的關鍵在於:禁用背景擦除 + 雙緩衝(BitBlt)+ 裁剪區剔除。掌握這三招,即可輕鬆打造專業級繪圖應用。