在影像處理應用中,常需在有限視窗區域瀏覽大尺寸圖片。使用者按住滑鼠左鍵拖曳即可平移圖像,核心挑戰在於拖曳過程的重繪效能與閃爍抑制。本文詳細分析從基本 Invalidate 到直接裝置內容繪製的演進過程,達成流暢無閃爍的拖曳體驗。
WM_LBUTTONDOWN 儲存滑鼠按下座標 m_lPicOldLeft、m_lPicOldTop。WM_MOUSEMOVE 中若偵測到 MK_LBUTTON,計算新舊座標差值 dwLRShift、dwTBShift,更新圖像顯示原點 m_lPicLeft、m_lPicTop。void CWingImgDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
m_lPicOldLeft = point.x;
m_lPicOldTop = point.y;
CDialog::OnLButtonDown(nFlags, point);
}
void CWingImgDlg::OnMouseMove(UINT nFlags, CPoint point)
{
if ((nFlags & MK_LBUTTON) == MK_LBUTTON)
{
m_lPicNewLeft = point.x;
m_lPicNewTop = point.y;
DWORD dwLRShift = m_lPicNewLeft - m_lPicOldLeft;
DWORD dwTBShift = m_lPicNewTop - m_lPicOldTop;
m_lPicLeft -= dwLRShift;
m_lPicTop -= dwTBShift;
// 邊界檢查略...
m_lPicOldLeft = m_lPicNewLeft;
m_lPicOldTop = m_lPicNewTop;
// 高效重繪:僅更新圖像矩形區域
InvalidateRect(&m_rtPic, TRUE);
// 進階寫法:直接繪製避免 WM_PAINT 流程
CDC* pDC = GetDC();
if (m_pImgInfo != NULL)
pDC->BitBlt(m_wShowLeft, m_wShowTop, m_lWidth, m_lHeight,
&m_AdjDC, m_lPicLeft, m_lPicTop, SRCCOPY);
ReleaseDC(pDC);
}
CDialog::OnMouseMove(nFlags, point);
}
void CWingImgDlg::OnPaint()
{
CPaintDC dc(this);
if (m_pImgInfo != NULL)
dc.BitBlt(m_wShowAdjLeft, m_wShowAdjTop, m_lWidth, m_lHeight,
&m_AdjDC, m_lPicLeft, m_lPicTop, SRCCOPY);
CDialog::OnPaint();
}
| 方法 | 程式碼 | 效果 |
|---|---|---|
| 全視窗重繪 | Invalidate(TRUE); UpdateWindow(); | 嚴重閃爍 |
| 區域重繪 | InvalidateRect(&m_rtPic, TRUE); UpdateWindow(); | 閃爍緩解 |
| RedrawWindow 旗標組合 | RedrawWindow(&m_rtPic, NULL, RDW_INTERNALPAINT|RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE); | 進一步減少 |
| 直接 GetDC 繪製 (推薦) | 見上方 OnMouseMove 程式碼 | 幾乎無閃爍、最流暢 |
關鍵在於限制重繪區域與繞過預設 WM_PAINT 流程直接在裝置內容繪製。結合記憶體裝置內容 (m_AdjDC) 雙緩衝機制,可在一般硬體上實現 60fps 以上的平滑拖曳體驗。