Windows GDI 繪圖涉及四個座標空間,繪圖指令經由層層轉換最終映射至實體裝置:

預設 GDI 模式下 無世界座標到頁面座標轉換,僅當呼叫 SetGraphicsMode(hdc, GM_ADVANCED) 並設定 SetWorldTransform 時才啟用。
typedef struct _XFORM {
FLOAT eM11, eM12; // 縮放、旋轉、剪切分量
FLOAT eM21, eM22;
FLOAT eDx, eDy; // 平移分量
} XFORM, *PXFORM;
| API | 用途 |
|---|---|
SetGraphicsMode(hdc, GM_ADVANCED) | 啟用進階圖形模式,允許世界轉換 |
SetWorldTransform(hdc, &xform) | 設定世界轉換矩陣 |
CombineTransform(&result, &x1, &x2) | 合併兩個轉換(矩陣相乘) |
GetWorldTransform(hdc, &xform) | 取得目前世界轉換矩陣 |
世界座標 (x, y) 經矩陣 XFORM 轉換為頁面座標 (x'', y''):
| x'' y'' 1 | | x y 1 | | eM11 eM12 0 |
| | = | | * | eM21 eM22 0 |
| | | | | eDx eDy 1 |
展開公式:
x'' = x * eM11 + y * eM21 + eDx
y'' = x * eM12 + y * eM22 + eDy
| 變換類型 | 矩陣參數 (eM11, eM12, eM21, eM22, eDx, eDy) |
|---|---|
| 縮放 | (sx, 0, 0, sy, 0, 0) |
| 平移 | (1, 0, 0, 1, tx, ty) |
| 旋轉 (角度 θ) | (cosθ, sinθ, -sinθ, cosθ, 0, 0) |
| X 軸剪切 | (1, k, 0, 1, 0, 0) |
| Y 軸剪切 | (1, 0, k, 1, 0, 0) |
| X 軸鏡像 | (-1, 0, 0, 1, 0, 0) |
| Y 軸鏡像 | (1, 0, 0, -1, 0, 0) |

void TransformAndDraw(int iTransform, HWND hWnd)
{
HDC hDC = ::GetDC(hWnd);
RECT rect; ::GetClientRect(hWnd, &rect);
// 畫座標軸參考線
::MoveToEx(hDC, rect.right/2, 0, NULL); ::LineTo(hDC, rect.right/2, rect.bottom);
::MoveToEx(hDC, 0, rect.bottom/2, NULL); ::LineTo(hDC, rect.right, rect.bottom/2);
::SetGraphicsMode(hDC, GM_ADVANCED);
XFORM xForm = {0};
switch (iTransform)
{
case 0: // 原始
break;
case 1: // 縮放 1/2
xForm.eM11 = 0.5f; xForm.eM22 = 0.5f; break;
case 2: // 平移
xForm.eM11 = 1.0f; xForm.eM22 = 1.0f; xForm.eDx = 75.0f; break;
case 3: // 逆時針旋轉 30°
xForm.eM11 = 0.8660f; xForm.eM12 = 0.5000f;
xForm.eM21 = -0.5000f; xForm.eM22 = 0.8660f; break;
case 4: // X 剪切
xForm.eM11 = 1.0f; xForm.eM12 = 1.0f; xForm.eM22 = 1.0f; break;
case 5: // 水平鏡像
xForm.eM11 = 1.0f; xForm.eM22 = -1.0f; break;
case 6: // 垂直鏡像
xForm.eM11 = -1.0f; xForm.eM22 = 1.0f; break;
}
::SetWorldTransform(hDC, &xForm);
// 將邏輯座標轉裝置座標以便 BitBlt 定位
::DPtoLP(hDC, (LPPOINT)&rect, 2);
// 繪製位圖於中心
HBITMAP hBMP = ::LoadBitmap(AfxGetResourceHandle(), MAKEINTRESOURCE(IDB_BITMAP1));
HDC hMemDC = ::CreateCompatibleDC(hDC);
HBITMAP hOld = (HBITMAP)::SelectObject(hMemDC, hBMP);
::BitBlt(hDC, rect.right/2 - 40, rect.bottom/2 - 40, 80, 80, hMemDC, 0, 0, SRCCOPY);
::SelectObject(hMemDC, hOld);
::DeleteObject(hBMP); ::DeleteDC(hMemDC); ::ReleaseDC(hWnd, hDC);
}
CombineTransform 疊加多個效果(如先旋轉再平移),注意矩陣乘法順序影響結果。World-to-Page 轉換雖較少直接使用,但掌握矩陣變換原理能靈活實現縮放、旋轉、鏡像等效果,且為理解完整 GDI 座標管線奠定基礎。詳細資訊請參閱 MSDN Coordinate Spaces and Transformations 章節。
本文配套原始碼可供下載。