MFC 默認繪圖流程:數據變化 → InvalidateRect 標記無效區 → 系統發送 WM_PAINT → OnDraw 重繪所有圖元。數據量大(萬級點位)時,頻繁全量重繪導致劇烈閃爍,實時監控、電子地圖類應用尤為嚴重。
核心思路:**所有繪圖操作在內存 DC 完成,最後一次性 BitBlt 到屏幕 DC**,避免用戶可見的中間過程。
OnPaint / OnDraw 獲取屏幕 CDC* pDC。CDC memDC; memDC.CreateCompatibleDC(pDC);。CBitmap bmp; bmp.CreateCompatibleBitmap(pDC, width, height);。CBitmap* pOldBmp = memDC.SelectObject(&bmp);。memDC.SetMapMode(pDC->GetMapMode()); 等。pDC->BitBlt(0, 0, width, height, &memDC, 0, 0, SRCCOPY);。memDC.SelectObject(pOldBmp);,釋放資源。若僅局部數據更新,可結合 GetClipBox 獲取無效區矩形,內存位圖尺寸僅覆蓋該區域,或僅在無效區內繪圖,最後 BitBlt 對應區域,極大降低內存佔用與 CPU 開銷。
基於 CFormView + Picture 控件(IDC_SCREEN),讀取文件 ASCII 碼作為波形數據。
int m_BufLen; unsigned char* buffer; // 數據緩衝
int m_dx, m_DY, m_DX, m_Y0; // 顯示區幾何參數
CPoint* value; // 轉換後的屏幕坐標點陣列
CRect rect; // 繪圖區矩形
void CWaveShowerView::GetScreenRect() {
CWnd* pStatic = GetDlgItem(IDC_SCREEN);
pStatic->GetWindowRect(&rect); ScreenToClient(&rect);
rect.DeflateRect(4,4); // 留邊距
m_Y0 = (rect.Height())/2 + rect.top; // 中線Y
m_DX = rect.Width(); m_DY = rect.Height()/2;
value = new CPoint[m_DX];
}
void CWaveShowerView::CleanScreen() {
CDC* pDC = GetDC();
CPen penBlack(PS_SOLID, 1, RGB(0,0,0)), *old = pDC->SelectObject(&penBlack);
for (int y=rect.top; yMoveTo(rect.left,y); pDC->LineTo(rect.right,y); }
pDC->SelectObject(old);
CPen penBlue(PS_SOLID, 1, RGB(0,0,255)); pDC->SelectObject(&penBlue);
pDC->MoveTo(rect.left, m_Y0); pDC->LineTo(rect.right, m_Y0); // 中線
pDC->SelectObject(old); ReleaseDC(pDC);
}
void CWaveShowerView::DrawPoint(CPoint pt, COLORREF clr) {
CDC* pDC = GetDC();
pDC->SetPixel(rect.left + pt.x, m_Y0 - pt.y, clr);
ReleaseDC(pDC);
}
void CWaveShowerView::OnDraw(CDC* pDC) {
// 1. 獲取無效區
CRect clipBox; pDC->GetClipBox(&clipBox);
// 2. 創建內存 DC 與位圖
CDC memDC; memDC.CreateCompatibleDC(pDC);
CBitmap bmp; bmp.CreateCompatibleBitmap(pDC, clipBox.Width(), clipBox.Height());
CBitmap* pOldBmp = memDC.SelectObject(&bmp);
// 3. 映射模式同步
memDC.SetMapMode(pDC->GetMapMode());
memDC.SetWindowOrg(clipBox.left, clipBox.top);
// 4. 背景填充(可選)
memDC.FillSolidRect(clipBox, RGB(0,0,0));
// 5. 繪製波形點/線
CPen pen(PS_SOLID, 1, RGB(0,255,0)); CPen* pOldPen = memDC.SelectObject(&pen);
for (int i=0; iBitBlt(clipBox.left, clipBox.top, clipBox.Width(), clipBox.Height(), &memDC, 0, 0, SRCCOPY);
// 7. 清理
memDC.SelectObject(pOldBmp);
}
雙緩衝是解決閃爍的標準答案。關鍵點:兼容 DC/位圖創建、映射模式同步、無效區裁剪、資源嚴格釋放。對於極高幂率場景,可進一步引入 Direct2D/OpenGL 硬件加速。