Windows GDI中的座標系(一)
By leezy_2000
2003-10-21 16:13
如果你用GDI輸出過文本、位元影像、或者繪製過如直線、曲線的圖形,那麼你必然使用過座標系。螢幕解析度為96dpi(dot per inch),印表機的解析度通常為600dpi,而你使用如
MoveTo(hDC,100,100);
LineTo(hDC,1300,1300);
在兩者之上卻可能產生完全相同的兩條線(此即所見即所得 (WYSIWYG)WYSIWYG)。就實現原理而言,座標系在其中扮演著決定性的角色。
一、什麼是GDI座標系?
首先從數學的角度看,GDI座標系是二維笛卡爾座標系,通過兩條軸和原點就可以確定平面上任何一點的位置。
從使用的角度看,GDI座標系是一種轉換規則,把你所制定的邏輯資料轉換成最終裝置驅動所能使用的資料。比如(100,100)這一點,經過實際的變換,在96dpi的螢幕上就可能是(9.6,9.6),在600dpi的印表機上則可能是(60,60)。(注一)
GDI座標系由四層座標空間組成(注二),按層次的高低分別為:
全局座標空間(World-Space):支援affine變換,應用於下面所說的頁面座標空間之上,只在NT類作業系統中有支援。
頁面座標空間(Page-Space):支援大量預定義的映射模式,是必然會被使用的座標空間。原點和相應的縮放比率的設定適合在頁面座標空間中進行設定。
全局座標空間和頁面座標空間統稱為邏輯座標空間,是GDI使用者所能夠直接使用的座標空間。也就是說進行GDI輸出時,你所指定的位置、大小等資訊只能是相對於邏輯座標空間的資料。
裝置座標空間(Device-Space):同裝置上下文相關聯的裝置空間。可以表示物理裝置的一小塊或者整個物理裝置。由於各種GDI輸出是面向裝置上下文的,邏輯座標空間中的相關資料自然也就必須要轉化為裝置座標空間中的資料。
物理裝置座標空間(Physical-Device Space):圖形裝置的物理表面的部分或全部。也就是圖形驅動程式所使用的座標空間。任何GDI輸出最終想在顯示器或印表機上成形都要經過相關的驅動程式,進行裝置座標空間向物理裝置座標空間的轉換就成為一種必然。這個過程完全由系統完成。所以DC的原點和大小資訊是唯讀。(為表述方便以下將用DC表示裝置上下文)
二、輸出位置到底在那兒?
由上述說明可見最終輸出位置的確定至少要經2次變換(如果你啟用了全局座標空間那是3次),有沒有什麼辦法能夠直接確定任意一個邏輯點最終會對應到那個物理位置呢?現成的沒有,我們來自己實現一個,其實也並不複雜。首先要把邏輯座標轉化為裝置座標,這個過程要根據affine矩陣和當前映射模式做很多運算,但我們現在先不自己進行這種運算,而是使用
BOOL LPtoDP(
HDC hdc, // handle to device context
LPPOINT lpPoints, // array of points
int nCount // count of points in array
);
這個函數負責把lpPoints中的邏輯座標轉換為同hdc相關聯的裝置座標。(稍後我們來自己完成這個函數)。
因為裝置座標和物理裝置座標的單位是一致的,都是物理裝置點。所以從裝置座標到物理裝置座標的轉換沒那麼麻煩,只要知道了DC的原點就可以了(注意這裡的原點是指裝置座標空間的原點在物理裝置座標空間中的位置)。這個任務的本質是去讀取OS的DC結構,我們使用
BOOL GetDCOrgEx(
HDC hdc, // handle to a DC
LPPOINT lpPoint // translation origin
);
來完成這一任務。
這樣的話,對於任意邏輯點logicalPoint,其DC原點為dcorgPoint有:
physicaldevicePoint就是logicalPoint理論上在物理裝置座標空間中的位置。此處需要說明的是由於物理裝置座標空間並不一定涵蓋圖形裝置的整個物理表面,所以從physicaldevicePoint計算出的位置並不一定就是物理表面上的位置。比如說列印的時候就還要加上四周的不可列印範圍才是其實際位置。同時顯示器一類的裝置還要考慮物理裝置本身有沒有進行縮放,如果沒有(也就是說沒用顯示器下邊的那些鈕)那麼你就會發現我們計算出的尺寸同相應點在螢幕上的位置符合的挺好。
void GetPhysicalPosition(HDC hDC,LPPOINT lpPoint ,int nCount)用於完成上述功能。具體實現見源碼1。