理解Windows座標系概念及其轉換

1. Windows座標系統

座標系主要分爲兩種:

  1. 邏輯座標系:主要是在“虛擬界面”中繪圖,是繪製到設備座標系的準備階段。
  2. 設備座標系:由客戶區座標、窗口座標、屏幕座標組成,主要和顯示設備有關。

在不同的空間中,各個座標系及其座標表示方法稍有不同。世界空間和頁面空間的座標系爲平時熟悉的方式,x軸向右爲正,y軸向上爲正,設備空間中,y軸改爲向下爲正(要問爲什麼這麼定,emm,自己問MS去)。

illustration showing a rectangle that changes size and position as it appears in the world space, page space, device space, and the device

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);
      可以看到,繪製的位置還是(100,100)。因爲現在窗口中的(200,200)位置繪製了文本,但轉換到視口(即顯示設備)時,位置信息縮小了1倍。
    • 從窗口的頁面空間轉換到設備空間的公式,原文請見此
    • 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. 其他文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章