1. Windows座標系統
座標系主要分爲兩種:
- 邏輯座標系:主要是在“虛擬界面”中繪圖,是繪製到設備座標系的準備階段。
- 設備座標系:由客戶區座標、窗口座標、屏幕座標組成,主要和顯示設備有關。
在不同的空間中,各個座標系及其座標表示方法稍有不同。世界空間和頁面空間的座標系爲平時熟悉的方式,x軸向右爲正,y軸向上爲正,設備空間中,y軸改爲向下爲正(要問爲什麼這麼定,emm,自己問MS去)。
MS其實對繪製的流程進行了明確的定義,且在不同空間中進行操作時的職能進行了分離,使不同層級下的操作內容更明確。先說邏輯座標系中的繪製,windows定義的繪圖流程如下:
- 窗口上繪製大小和原點配置(可使用默認值);
- 世界空間上繪製(允許對圖形的縮放、平移、旋轉、裁剪、鏡像);
- 頁面空間繪製(設置映射模式SetMapMode,確定怎麼畫到設備空間上);
接下來是設備座標系的繪製,流程如下:
- 視口上繪製大小和原點配置(可使用默認值);
- 設備空間繪製(僅允許進行平移操作);
- 物理設備顯示(最終顯示在對應的區域)。
注意點:viewPort 和 windowsPort,這兩者的翻譯是視口,窗口。視口是基於設備座標(像素的),窗口是基於邏輯座標的,默認情況下兩者重合,但可設置視口座標系的原點以及窗口座標系的原點。
- 要注意區分視口座標系原點跟視口原點的區別,窗口座標系原點跟窗口原點的區別。
- 視口座標系其實就是設備座標系,它是固定不變的,因此設備原點永遠是(0,0)。
- 視口原點是可變的,它默認處於設備原點,但是可以更改。
- 窗口座標系指定了在窗口繪圖時的參考點,參考點就是窗口座標系的原點。
- 窗口原點默認等於窗口座標系原點,但是窗口原點可以改變。
- 窗口原點永遠跟視口原點匹配映射。//這個是最主要的
2. 常見函數及參數
世界空間繪製的常用函數:
函數 | 主要參數 | 作用 |
SetWorldTransform | CONST XFORM *lpXform | 對圖形處理,包括縮放、平移、旋轉、裁剪、鏡像 |
SetGraphicsMode |
int iMode: GM_COMPATIBLE(默認) GM_ADVANCED(可使用SetWorldTransform) |
設置特定設備上下文的圖形模式 |
頁面空間繪製常用參數:
函數 | 主要參數 | 作用 |
SetMapMode |
int fnMapMode: MM_ANISORTOPIC:自定義縮放比例 MM_HIENGLISH:0.001inch-->1像素;x正軸向右,y正軸向上 MM_HIMETRIC:0.01毫米-->1像素;x正軸向右,y正軸向上 MM_ISOTROPIC:x和y方向的縮放比例相同 MM_LOENGLISH:0.01inch;x正軸向右,y正軸向上 MM_LOMETRIC:0.1毫米-->1像素;x正軸向右,y正軸向上 MM_TEXT:1:1拷貝;x正軸向右,y正軸向下 MM_TWIPS:1/1440inch-->1像素;x正軸向右,y正軸向上 |
設置映射模式SetMapMode,確定怎麼畫到設備空間上 |
SetWindowExtEx | int nXExtent, int nYExtent; | 設置繪製區域大小 |
SetWindowOrgEx | int X, int Y; | 設置繪製的窗口原點 |
設備空間常用參數:
函數 | 主要參數 | 作用 |
SetViewportExtEx | int nXExtent, int nYExtent; | 設置繪製區域大小 |
SetViewportOrgEx | int X, int Y; | 設置顯示的視口原點 |
3. Examples
- 修改窗口原點
-
SetWindowOrgEx(dc.m_hDC, 200, 200, NULL); TextOut(dc.m_hDC, 200, 200, "123456", 6);
先在窗口中繪製, 最終轉換到視口中顯示。
-
- 修改視口原點
-
// viewpoint SetViewportOrgEx(dc.m_hDC, 100, 100, NULL); TextOut(dc.m_hDC, 200, 200, "123456", 6);
在窗口中的座標點(200,200)繪製文字,而視口中原點座標變爲(100,100),經一定量的偏移,最終繪製在界面座標(300,300) 處。
-
- 窗口和視口同時配置
-
// hybird SetWindowOrgEx(dc.m_hDC, 200, 200, NULL); SetViewportOrgEx(dc.m_hDC, 100, 100, NULL); TextOut(dc.m_hDC, 200, 200, "123456", 6);
繪製在座標(100,100)的位置。這裏爲什麼是這樣呢?首先在窗口中繪製圖形,之後以(200,200)爲起點位置,將窗口中的內容複製到視口以(100,100)爲起點的位置。
-
這裏,假設本文的繪製位置更改爲(250,300),在窗口和視口中繪製的位置又是多少呢?——窗口爲(250,300),視口爲(150,200),視口中的計算方法:150=(250-200)+100; 200=(300-200)+100;(這裏SetMapMode爲默認參數MM_TEXT,未配置縮放係數或修改座標軸方向)。
-
- 配置縮放係數爲0.5,即從窗口中2個單位的位置,在視口中表示爲一個1單位。
-
CRect rect; GetClientRect(&rect); SetMapMode(dc.m_hDC, MM_ANISOTROPIC); SetWindowExtEx(dc.m_hDC, rect.Width()*2, rect.Height()*2, NULL); SetViewportExtEx(dc.m_hDC, rect.Width(), rect.Height(), NULL); TextOut(dc.m_hDC, 200, 200, "123456", 6);
- 從窗口的頁面空間轉換到設備空間的公式,原文請見此:
-
Dx = ((Lx - WOx) * VEx / WEx) + VOx 其中: Dx x value in device units Lx x value in logical units (also known as page space units) WOx window x origin VOx viewport x origin WEx window x-extent VEx viewport x-extent
-
- 使用世界空間進行轉換
-
// world transform int nGraphicsMode = SetGraphicsMode(dc.GetSafeHdc(), GM_ADVANCED); XFORM xForm; xForm.eM11 = (FLOAT) 0.5; xForm.eM12 = (FLOAT) 0.0; xForm.eM21 = (FLOAT) 0.0; xForm.eM22 = (FLOAT) 0.5; xForm.eDx = (FLOAT) 0.0; xForm.eDy = (FLOAT) 0.0; SetWorldTransform(dc.GetSafeHdc(), &xForm); //Rectangle(dc.GetSafeHdc(), 0, 0, 200, 300); TextOut(dc.m_hDC, 200, 200, "123456", 6);
另一種把圖繪製在(100,100)處的方法就是使用世界空間,因爲說來說去,圖形最開始畫的地方就在這裏,而且在世界空間中能進行多種的圖形操作。這裏用到了SetWorldTransform、XFORM和SetGraphicsMode,這是必須使用的函數或者參數,具體使用方法各請看MSDN,那裏有非常詳細的介紹。
-
- 綜合使用WindowOrg和ViewPortOrg
-
// using windowpoint & viewpoint SetMapMode(dc.m_hDC, MM_TEXT); SetViewportOrgEx(dc.GetSafeHdc(), 0, rect.bottom/10, NULL); SetWindowOrgEx(dc.GetSafeHdc(), -100, 0, NULL); dc.Rectangle(0, 0, 200, 300); TextOut(dc.m_hDC, 200, 200, "123456", 6);
主要明白繪圖現在窗口上繪,再是在視口上顯示,這個過程即可,轉換過程中可能會有一定比例的縮放。
-
4. 引用
1. Using SetWorldTransform() to Rotate Basic Shapes by Any Angle
2. MSDN(官方文檔,最最權威!)
3. 其他文檔