在信號處理與實時監控系統中,將採集到的信號波形即時顯示在界面上是常見需求。然而,若直接在螢幕裝置上繪圖,畫面會因頻繁重繪產生明顯閃爍,嚴重影響觀測體驗。本文介紹一種經典的「雙緩衝繪圖」技術:先在記憶體裝置上下文(Memory DC)完成所有繪圖操作,再一次性將位圖拷貝至螢幕裝置上下文(Screen DC),從而實現流暢無閃爍的動態波形顯示。
程式執行時的波形動畫效果如下:
Windows GDI 繪圖本質上是對裝置上下文(DC)的操作。螢幕 DC 直接對應顯示器輸出,每次繪圖指令都會立即觸發畫面更新,導致閃爍。解決方案是引入記憶體 DC 作為中介:
BitBlt 將記憶體位圖一次性複製到螢幕 DC由於螢幕只經歷一次完整畫面更新,人眼無法察覺中間過程,即消除了閃爍。
private:
CDC *pDC; // 螢幕繪圖裝置指標
CDC memDC; // 記憶體繪圖裝置
int m_High; // 繪圖區域上邊界
int m_Low; // 繪圖區域下邊界
int m_lCount[1024]; // 波形資料緩衝區
int m_now; // 當前寫入索引
BOOL CDrawTest::OnInitDialog()
{
CDialog::OnInitDialog();
m_Low = 0;
m_High = 1024;
m_now = 0;
SetTimer(1, 100, NULL); // 100ms 觸發一次重繪
return TRUE;
}
void CDrawTest::OnTimer(UINT nIDEvent)
{
CRect rect;
CWnd* pWnd = GetDlgItem(IDC_COORD); // 取得 Picture 控制項
pWnd->GetClientRect(&rect); // 取得繪圖區域大小
pDC = pWnd->GetDC(); // 取得螢幕 DC
pWnd->Invalidate();
pWnd->UpdateWindow();
// --- 建立記憶體 DC 與位圖 ---
CBitmap memBitmap;
CBitmap* pOldBmp = NULL;
memDC.CreateCompatibleDC(pDC);
memBitmap.CreateCompatibleBitmap(pDC, rect.right, rect.bottom);
pOldBmp = memDC.SelectObject(&memBitmap);
// 先將螢幕當前內容拷貝到記憶體(保留靜態背景)
memDC.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
// --- 自定義波形繪圖函數 ---
DrawWave(&memDC); // 具體實現見原始碼:繪製網格、坐標軸、波形曲線等
// --- 將記憶體位圖一次性輸出到螢幕 ---
pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
// --- 清理資源 ---
memDC.SelectObject(pOldBmp);
memDC.DeleteDC();
memBitmap.DeleteObject();
CDialog::OnTimer(nIDEvent);
}
BitBlt 高速傳輸無格式轉換開銷。SRCCOPY 直接複製像素,速度最快。Polyline」或「分段繪製」優化 DrawWave 內部邏輯。DrawWave 中引入座標轉換矩陣。本文配套完整原始碼可供下載參考。