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

MFC 視圖類即時顯示滑鼠座標:無閃爍、不覆蓋內容的 BitBlt 雙緩衝技術

     編輯:VC 教學

需求與挑戰

在繪圖/編輯類應用中,滑鼠移動時於游標旁即時顯示座標 (如 [123, 456]) 是基本需求。簡單做法在 OnDraw 繪製,但會導致:

  1. 需呼叫 Invalidate 觸發重繪,整個視圖閃爍。
  2. 座標文字覆蓋原有繪圖內容,移動後留下殘影。

本文實作「離屏備份 + BitBlt 還原」技術,達到零閃爍、零殘影、不觸發視圖重繪的完美效果。

核心原理:三步驟循環

  1. 備份:滑鼠移動前,將「即將繪製文字的矩形區域」從視圖 DC 複製到記憶體位圖 (CBitmap + CDC)。
  2. 繪製:在視圖 DC 上直接繪製座標文字 (透明背景)。
  3. 還原:下一次滑鼠移動時,先將上一幀備份的位圖 BitBlt 回視圖 DC,抹除舊文字,再進入下一輪備份-繪製。

關鍵常數與成員變數


// 常數定義 (依系統字體微調)
#define CHAR_HEIGHT 18
#define CHAR_WIDTH   7
#define OFFSET_X    15  // 文字相對滑鼠 X 偏移
#define OFFSET_Y    10  // 文字相對滑鼠 Y 偏移
#define TEXT_COLOR  RGB(255, 0, 0) // 紅色文字

// CDrawMouseView 成員變數
CBitmap m_bmpBackup;      // 備份位圖
CDC     m_dcBackup;       // 備份 DC
int     m_nTextWidth;     // 文字像素寬度
CPoint  m_ptLastMouse;    // 上一次滑鼠位置
BOOL    m_bFirstMove = TRUE; // 是否為第一次移動

核心函式實作

1. 備份目標區域畫面到記憶體位圖


void CDrawMouseView::BackupRect(CDC* pDC, CPoint ptMouse)
{
    // 計算文字寬度
    CString str;
    str.Format(_T("[%d,%d]"), ptMouse.x, ptMouse.y);
    m_nTextWidth = str.GetLength() * CHAR_WIDTH;

    // 目標矩形 (螢幕座標 -> 用戶區座標)
    CRect rcTarget(ptMouse.x + OFFSET_X, ptMouse.y + OFFSET_Y,
                   ptMouse.x + OFFSET_X + m_nTextWidth, ptMouse.y + OFFSET_Y + CHAR_HEIGHT);

    // 清理舊備份資源
    if (m_bmpBackup.GetSafeHandle())
    {
        m_dcBackup.SelectObject(m_bmpBackup); // 確保位圖已選入
        m_bmpBackup.DeleteObject();
        m_dcBackup.DeleteDC();
    }

    // 建立相容 DC 與位圖
    if (m_dcBackup.CreateCompatibleDC(pDC))
    {
        if (m_bmpBackup.CreateCompatibleBitmap(pDC, m_nTextWidth, CHAR_HEIGHT))
        {
            CBitmap* pOldBmp = m_dcBackup.SelectObject(&m_bmpBackup);
            // 關鍵:從視圖 DC 複製目標區域到備份 DC
            m_dcBackup.BitBlt(0, 0, m_nTextWidth, CHAR_HEIGHT,
                              pDC, rcTarget.left, rcTarget.top, SRCCOPY);
            m_dcBackup.SelectObject(pOldBmp);
        }
        else
        {
            m_dcBackup.DeleteDC();
        }
    }
}

2. 繪製座標文字


void CDrawMouseView::DrawCoordText(CDC* pDC, CPoint ptMouse)
{
    if (!m_bmpBackup.GetSafeHandle()) return; // 無備份不繪製

    CString str;
    str.Format(_T("[%d,%d]"), ptMouse.x, ptMouse.y);

    CRect rcText(ptMouse.x + OFFSET_X, ptMouse.y + OFFSET_Y,
                 ptMouse.x + OFFSET_X + m_nTextWidth, ptMouse.y + OFFSET_Y + CHAR_HEIGHT);

    pDC->SetBkMode(TRANSPARENT);
    COLORREF oldColor = pDC->SetTextColor(TEXT_COLOR);
    pDC->DrawText(str, &rcText, DT_LEFT | DT_TOP | DT_SINGLELINE);
    pDC->SetTextColor(oldColor);
}

3. 還原上一幀畫面 (抹除舊文字)


void CDrawMouseView::RestorePrev(CDC* pDC)
{
    if (!m_bmpBackup.GetSafeHandle()) return;

    CRect rcPrev(m_ptLastMouse.x + OFFSET_X, m_ptLastMouse.y + OFFSET_Y,
                 m_ptLastMouse.x + OFFSET_X + m_nTextWidth, m_ptLastMouse.y + OFFSET_Y + CHAR_HEIGHT);

    // 將備份位圖複製回視圖 DC
    CBitmap* pOldBmp = m_dcBackup.SelectObject(&m_bmpBackup);
    pDC->BitBlt(rcPrev.left, rcPrev.top, m_nTextWidth, CHAR_HEIGHT,
                &m_dcBackup, 0, 0, SRCCOPY);
    m_dcBackup.SelectObject(pOldBmp);

    // 釋放備份資源 (下一輪會重建)
    m_dcBackup.DeleteDC();
    m_bmpBackup.DeleteObject();
}

4. 滑鼠移動事件整合


void CDrawMouseView::OnMouseMove(UINT nFlags, CPoint point)
{
    CDC* pDC = GetDC();
    if (!pDC) return;

    if (!m_bFirstMove)
    {
        RestorePrev(pDC); // 非第一次:先還原上一幀
    }
    else
    {
        m_bFirstMove = FALSE;
    }

    // 備份當前位置畫面
    BackupRect(pDC, point);

    // 繪製新座標
    DrawCoordText(pDC, point);

    m_ptLastMouse = point; // 記錄供下一次還原用
    ReleaseDC(pDC);

    CView::OnMouseMove(nFlags, point);
}

視圖銷毀時的資源清理


void CDrawMouseView::OnDestroy()
{
    if (m_bmpBackup.GetSafeHandle())
    {
        m_bmpBackup.DeleteObject();
        m_dcBackup.DeleteDC();
    }
    CView::OnDestroy();
}

技術優勢總結

指標傳統 Invalidate 重繪本文 BitBlt 雙緩衝
閃爍明顯 (整視圖重繪)零閃爍 (局部像素操作)
殘影無 (背景重繪覆蓋)無 (精確還原備份像素)
效能低 (觸發完整 OnDraw)極高 (僅 2 次 BitBlt + 1 次 DrawText)
適用場景靜態內容、低頻更新高頻滑鼠追蹤、即時標註、游標十字線

延伸應用

  • 游標十字線:備份整行/整列像素,繪製十字線,移動時還原。
  • 即時工具提示:顯示像素顏色、圖層資訊、測量長度等。
  • 橡皮筋選取框:同樣原理實現拖曳選取框無殘影。

結語

掌握 CDC::BitBlt、CBitmap、CDC::CreateCompatibleDC 的組合運用,是 MFC/GDI 繪圖進階的關鍵技能。本技術不僅解決座標顯示,更是所有「非破壞性疊加繪製」場景的通用解法。下次遇到需在現有畫面上疊加動態資訊時,請優先考慮此方案。

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