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

VC++ 大數據量繪圖無閃爍技術:雙緩衝/內存 DC 完整實現指南

     編輯:VC 教學

閃爍根源:WM_PAINT 與無效區機制

MFC 默認繪圖流程:數據變化 → InvalidateRect 標記無效區 → 系統發送 WM_PAINT → OnDraw 重繪所有圖元。數據量大(萬級點位)時,頻繁全量重繪導致劇烈閃爍,實時監控、電子地圖類應用尤為嚴重。

解決方案:內存兼容 DC + 位圖雙緩衝

核心思路:**所有繪圖操作在內存 DC 完成,最後一次性 BitBlt 到屏幕 DC**,避免用戶可見的中間過程。

標準實現步驟

  1. OnPaint / OnDraw 獲取屏幕 CDC* pDC。
  2. 創建兼容內存 DC:CDC memDC; memDC.CreateCompatibleDC(pDC);。
  3. 創建兼容位圖:CBitmap bmp; bmp.CreateCompatibleBitmap(pDC, width, height);。
  4. 選入位圖:CBitmap* pOldBmp = memDC.SelectObject(&bmp);。
  5. 設置映射模式一致:memDC.SetMapMode(pDC->GetMapMode()); 等。
  6. **在 memDC 上進行所有繪圖操作**(線、點、文本、圖像)。
  7. 一次性拷貝:pDC->BitBlt(0, 0, width, height, &memDC, 0, 0, SRCCOPY);。
  8. 恢復選入: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);
}

雙緩衡版 OnDraw(關鍵)

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 硬件加速。

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