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

《VC++技術內幕》學習筆記(六):映射模式完全指南 — 從 MM_TEXT 到 MM_ANISOTROPIC、座標轉換、滾動視圖實戰

     編輯:VC++

Day 6 核心主題:映射模式

為什麼需要映射模式?

預設 MM_TEXT:1 邏輯單位 = 1 像素 (設備座標)。
**痛點**:相同邏輯座標在不同解析度、不同 DPI、印表機上對應的物理尺寸完全不同。
**目標**:用**邏輯座標**描述圖形幾何屬性 (如「畫一個 4cm x 4cm 的圓」),由映射模式自動轉換為設備座標,實現**「所見即所得」跨設備一致性**。

八大標準映射模式

模式X 方向Y 方向單位/比例可改原點典型用途
MM_TEXT右增下增1 像素是像素級精確控制、遊戲繪圖
MM_LOENGLISH右增上增0.01 英吋否英制工程圖
MM_HIENGLISH右增上增0.001 英吋否高精度英制
MM_LOMETRIC右增上增0.1 mm否公制工程圖
MM_HIMETRIC右增上增0.01 mm (10 微米)否**高精度公制、列印預覽**
MM_TWIPS右增上增1/1440 英吋 (1 twip = 1/20 pt)否文字排版、RichEdit 內部單位
MM_ISOTROPIC右增上增**可變,但 X/Y 比例固定 1:1**是保持圖形不變形的縮放 (正圓永遠是圓)
MM_ANISOTROPIC右增上增**可變,X/Y 比例獨立**是**任意縮放、填滿客戶區、非等比變形**

**關鍵差異**:
- 固定比例模式 (LO/HI ENGLISH/METRIC/TWIPS):**Y 軸向上增**,不可改。
- 可變比例模式 (ISO/ANISO):**Y 軸向上增**,但可透過 SetViewportExt 設定負值翻轉 Y 軸。

核心 API:設定映射模式與比例

// 1. 選擇模式
pDC->SetMapMode(MM_ANISOTROPIC);

// 2. 設定視窗範圍 (邏輯單位) — 代表「整個文檔邏輯大小」
pDC->SetWindowExt(1000, 1000); // 邏輯寬高各 1000 單位

// 3. 設定視口範圍 (設備單位/像素) — 代表「客戶區像素大小」
CRect rectClient;
GetClientRect(&rectClient);
pDC->SetViewportExt(rectClient.Width(), -rectClient.Height()); // 負號翻轉 Y 軸

// 4. 設定視口原點 (設備座標) — 邏輯 (0,0) 映射到視口中心
pDC->SetViewportOrg(rectClient.Width()/2, rectClient.Height()/2);

// 5. 繪製:邏輯座標 (-500,-500) 到 (500,500) 自動填滿整個客戶區
pDC->Ellipse(CRect(-500, -500, 500, 500)); // 完美橢圓隨視窗縮放

座標轉換函數 (CDC 成員)

  • LPtoDP(LPPOINT/ LPRECT/ LPSIZE):邏輯 → 設備 (如滑鼠點擊座標轉為邏輯座標做碰撞檢測)。
  • DPtoLP(...):設備 → 邏輯 (如將邏輯圖形邊界轉為設備座標做 PtInRect 命中測試)。

座標系使用鐵律 (必背)

實戰專案:滾動視圖 CScrollView 完整實現

需求

  • 文檔邏輯大小 20000 x 30000 (單位 0.01mm, 即 2m x 3m 超大圖紙)。
  • 視窗可滾動瀏覽,滾動頁面/行步長自定義。
  • 畫一個固定邏輯位置的 4cm 圓,滑鼠點擊圓內變色,鍵盤方向鍵控制滾動。

關鍵代碼節錄

// 1. 專案類型:Single Document + Base Class = CScrollView

// 2. 視圖類成員
private:
    int m_nColor;          // GRAY_BRUSH / WHITE_BRUSH
    CRect m_rectEllipse;   // 邏輯座標: (0,0,4000,-4000) = 4cm x 4cm

// 3. OnInitialUpdate — 設定滾動參數 (最佳時機)
void CMyApp6View::OnInitialUpdate() {
    CScrollView::OnInitialUpdate();
    CSize sizeTotal(20000, 30000);           // 文檔總邏輯大小
    CSize sizePage(sizeTotal.cx/2, sizeTotal.cy/2); // PageUp/Down 步長
    CSize sizeLine(sizeTotal.cx/50, sizeTotal.cy/50); // 方向鍵步長
    SetScrollSizes(MM_HIMETRIC, sizeTotal, sizePage, sizeLine);
}

// 4. 構造函數 — 成員初始化列表 (效率+正確性)
CMyApp6View::CMyApp6View() : m_rectEllipse(0, 0, 4000, -4000) {
    m_nColor = GRAY_BRUSH;
}

// 5. OnDraw — 純邏輯座標繪製
void CMyApp6View::OnDraw(CDC* pDC) {
    pDC->SelectStockObject(m_nColor);
    pDC->Ellipse(m_rectEllipse);
}

// 6. OnLButtonDown — 設備座標命中測試
void CMyApp6View::OnLButtonDown(UINT nFlags, CPoint point) {
    CClientDC dc(this);
    OnPrepareDC(&dc); // 套用映射模式
    CRect rectDev = m_rectEllipse;
    dc.LPtoDP(rectDev); // 邏輯矩形 → 設備矩形
    if (rectDev.PtInRect(point)) {
        m_nColor = (m_nColor == GRAY_BRUSH) ? WHITE_BRUSH : GRAY_BRUSH;
        InvalidateRect(rectDev); // 只重繪該矩形區域
    }
}

// 7. OnKeyDown — 鍵盤控制滾動
void CMyApp6View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
    switch (nChar) {
        case VK_HOME:  OnVScroll(SB_TOP, 0, NULL); OnHScroll(SB_LEFT, 0, NULL); break;
        case VK_END:   OnVScroll(SB_BOTTOM, 0, NULL); OnHScroll(SB_RIGHT, 0, NULL); break;
        case VK_UP:    OnVScroll(SB_LINEUP, 0, NULL); break;
        case VK_DOWN:  OnVScroll(SB_LINEDOWN, 0, NULL); break;
        case VK_PRIOR: OnVScroll(SB_PAGEUP, 0, NULL); break;
        case VK_NEXT:  OnVScroll(SB_PAGEDOWN, 0, NULL); break;
        case VK_LEFT:  OnHScroll(SB_LINELEFT, 0, NULL); break;
        case VK_RIGHT: OnHScroll(SB_LINERIGHT, 0, NULL); break;
    }
}

構造函數成員初始化列表深度解析

CMyApp6View::CMyApp6View() : m_rectEllipse(0,0,4000,-4000) { m_nColor = GRAY_BRUSH; }
**為何不用函數體賦值?**
1. **必要性**:成員為類類型且無默認構造函數 (如 CMember(int)) 時,**只能**用初始化列表。
2. **效率**:避免「默認構造 → 再賦值」兩次函數調用。對 CString, std::vector 等大型類節省記憶體分配/拷貝。
3. **順序陷阱**:初始化順序**按聲明順序**,而非列表順序。聲明順序與初始化列表順序保持一致可避免未定義行為。

雷神心法:映射模式選擇決策樹

  1. 需要**物理尺寸精確** (列印、CAD、報表) → MM_HIMETRIC / MM_LOMETRIC / MM_TWIPS。
  2. 需要**填滿客戶區、保持比例** (圖表、預覽) → MM_ISOTROPIC。
  3. 需要**任意拉伸、非等比** (遊戲地圖、圖片瀏覽) → MM_ANISOTROPIC。
  4. 像素級遊戲/特效/自定義渲染管線 → MM_TEXT (或直接用 Direct2D/OpenGL)。

下一篇預告

Day 7:GDI 核心 — CDC 設備環境類體系、CPen/CBrush/CFont/CBitmap/CPalette/CRgn 六大 GDI 物件、庫存物件、SelectObject 保存/恢復模式、句柄持久化技巧。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景必用座標系原因
CDC 所有繪圖函數參數邏輯座標映射模式自動轉換
CWnd 所有函數參數 (MoveWindow, SetWindowPos, GetClientRect...)設備座標直接對應螢幕/客戶區像素
區域 (CRgn) 定義設備座標區域操作在設備空間進行
命中測試設備座標滑鼠座標本身是設備座標
長期儲存幾何數據 (文檔序列化)邏輯座標 或 物理座標 (mm/inch)設備座標會隨滾動、縮放、DPI 改變