設備座標和邏輯座標

 

Windows應用程序繪製圖形時使用的是一種邏輯單位,每個邏輯單位的大小由映射模式決定,這個邏輯單位既可以與設備單位(屏幕或打印機上的一個像素點)相同,也可以是一種物理單位(如毫米),還可以是用戶自定義的一種單位。在Windows應用程序中,只要與輸出有關係,都要使用映射模式。本文的目的是幫助讀者瞭解映射模式的一些基本知識,並對在使用中經常出現的一些問題提出解決方案。

一、映射模式基本知識
當Windows應用程序在其客戶區繪製圖形時,必須給出在客戶區的位置,其位置用x和y 兩個座標表示,x表示橫座標,y表示縱座標。在所有的GDI繪製函數中,這些座標使用的是一種"邏輯單位"。當GDI函數將輸出送到某個物理設備上時,Windows將邏輯座標轉換成設備座標(如屏幕或打印機的像素點)。邏輯座標和設備座標的轉換是由映射模式決定的。映射模式被儲存在設備環境中。GetMapMode函數用於從設備環境得到當前的映射模式,SetMapMode函數用於設置設備環境的映射模式。

1.邏輯座標

邏輯座標是獨立於設備的,它與設備點的大小無關。使用邏輯單位,是實現"所見即所得"的基礎。當程序員在調用一個畫線的GDI函數LineTo,畫出25.4mm(1英寸) 長的線時,他並不需要考慮輸出的是何種設備。若設備是VGA顯示器,Windows自動將其轉化爲96個像素點;若設備是一個300dpi的激光打印機,Windows自動將其轉化爲300個像素點。

2.設備座標

Windows將GDI函數中指定的邏輯座標映射爲設備座標,在所有的設備座標系統中,單位以像素點爲準,水平值從左到右增大,垂直值從上到下增大。

Windows中包括以下3種設備座標,以滿足各種不同需要:

(1)客戶區域座標,包括應用程序的客戶區域,客戶區域的左上角爲(0,0)。

(2)屏幕座標,包括整個屏幕,屏幕的左上角爲(0,0)。屏幕座標用在WM_MOVE消息中(對於非子窗口)以及下面的Windows函數中:CreateWindow和MoveWindow(都對於非子窗口)、GetMessage、GetCursorPos、GetWindowRect、WindowFromPoint和SetBrushOrg中。用函數ClientToScreen和ScreenToClient可以將客戶區域座標轉換成屏幕區域座標,或反之。

(3)全窗口座標,包括一個程序的整個窗口,包括標題條、菜單、滾動條和窗口框,窗口的左上角爲(0,0)。使用GetWindowDC得到的窗口設備環境,可以將邏輯單位轉換成窗口座標。

3.邏輯座標與設備座標的轉換方式

映射方式定義了Windows如何將GDI函數中指定的邏輯座標映射爲設備座標。要繼續討論映射方式我們要介紹Windows有關映射模式的一些術語:我們將邏輯座標所在的座標系稱爲"窗口",將設備座標所在的座標系稱爲"視口"。

"窗口"依賴於邏輯座標,可以是像素點、毫米或程序員想要的其他尺度。

"視口"依賴於設備座標(像素點)。通常,視口和客戶區域等同。但是,如果程序員用GetWindowDC或CreateDC獲取了一個設備環境,則視口也可以指全窗口座標或屏幕座標。點(0,0)是客戶區域的左上角。x的值向右增加,y的值向上增加。

對於所有映射模式,Windows都用下面兩個公式將窗口座標轉換成視口座標:

xViewport=(xWindow-xWinOrg)*(xViewExt/xWinExt)+xViewOrg

yViewport=(yWindow-yWinOrg)*(yViewExt/yWinExt)+yViewOrg

其中,(xWindow,yWindows)是待轉換的邏輯點,(xViewport,yViewport)是轉換後的設備點。如果設備座標是客戶區域座標或全窗口座標,則Windows在畫一個對象前,還必須將這些座標轉換成屏幕座標。

這兩個公式使用了分別指定窗口和視口原點的點:(xWinOrg,yWinOrg)是邏輯座標的窗口原點;(xViewOrg,yViewOrg)是設備座標的視口原點。在缺省的設備環境中,這兩個點均設置爲(0,0),但它們可以改變。此公式意味着,邏輯點(xWinOrg,yWinOrg)總被映射爲設備點(xViewOrg,yViewOrg)。

Windows還能將視口(設備)座標轉換爲窗口(邏輯)座標:

xWindow=(xViewport-xViewOrg)*(xWinExt/xViewExt)+xWinOrg

yWindow=(yViewport-yViewOrg)*(yWinExt/yViewExt)+yWinOrg

可以使用Windows提供的兩個函數DPtoLP和LPtoDP在設備座標及邏輯座標之間互相轉換。

4.映射模式的種類

Windows定義了表1所列出的8種映射方式。

上述映射模式中又可分成以下3類:

映 射 方 式 邏 輯 單 位 X 軸 增 加 Y 軸 增 加 毫 米
MM_TEXT 像 素 點 與 設 備 有 關
MM_LOMETRIC 0. 1mm 0.1
MM_HIMETRIC 0. 01mm 0.01
MM_LOENGLISH 0. 254mm 0.254
MM_HIENGLISH 0. 0254mm 0.0254
MM_TWIPS 0.0176mm 0.0176
MM_ISOTROPIC 任 意(x=y) 可 選 可 選 可 設
MM_ANISOTROPIC 任 意(x!=y) 可 選 可 選 可 設


MM_TEXT映射模式這種映射模式被稱爲"文本"映射方式,不是因爲它對於文本最合適,而是軸的方向與讀文本的方向一致。Windows提供了函數SetViewportOrg和SetWindowOrg 用來設置視口和窗口的原點。缺省的窗口原點和視口原點均爲(0,0),可以改變;缺省的窗口範圍和視口範圍均爲(1,1),不可改變。

度量映射方式MM_LOMETRIC、MM_HIMETRIC、MM_LOENGLISH、MM_HIENGLISH和MM_TWIPS 將1個邏輯單位映射爲固定的實際單位,其中1twip等於0.0176mm(1/1440英寸)。其他映射模式對應的物理單位參見表1。設置了映射模式以後,Windows自動設置了窗口及視口的範圍,範圍本身的值並不重要,但範圍比是一個固定的值,對於MM_LOMETRIC,Windows計算範圍比xViewExt/xWinExt=0.1mm中水平像素的點數。

自定義映射模式MM_ISOTROPIC和MM_ANISOTROPIC兩種映射模式允許程序員設置自己的窗口和視口範圍。MM_ISOTROPIC和MM_ANISOTROPIC的區別是所設置的x軸和y軸的的範圍必須相同,而MM_ANISOTROPIC所設置的x軸和y軸的的範圍可以不同。isotropi的意思是" 在所有方向相同",anisotropic的意思正相反。自定義映射模式中窗口和視口的原點和範圍都可以改變,程序員可以設置自己需要的映射模式。函數SetWindowExt和SetViewportExt 用於改變窗口和視口的範圍。下面的代碼將1個邏輯單位映射成0.396mm(1/64英寸)。

SetMapMode(hDC,MM_ISOTROPIC);

SetWindowExt(64,64);

SetViewportExt(hdc,GetDeviceCaps(hdc,LOGPIXELSX),GetDeviceCaps(hdc, LOGPIXELSY));

二、與映射模式有關的問題的解決

實際應用中,程序員會遇到一些與顯示模式有關的問題。例如OLEServer中映射模式的設置、如何減少邏輯座標與設備座標間相互轉換的誤差等。下面,筆者就討論一下這兩個問題的解決方法。

1.OLEServer中映射模式的設置方法

開發OLEServer應用程序時,如果程序員直接調用SetMapMode函數將映射模式設置成度量映射方式中的一種後,在Windows95/98上程序會正常運行,但在WindowsNT上對象顯示的大小比邊框小。經過筆者研究後,發現WindowsNT上OLEServer應使用基於邏輯英寸的映射方式。在討論如何設置基於邏輯英寸的映射方式前,我們先介紹一下邏輯英寸的概念。

Windows在顯示時以"邏輯英寸"爲單位,邏輯英寸比實際的英寸要大。如果Windows程序使用實際英寸,則普通的10磅文本在顯示器上就會小到幾乎難以辨認,因此Windows使用放大了的"邏輯英寸"來表示文本。邏輯英寸隻影響顯示,而不影響打印。

使用GetDeviceCaps函數可得到當前設備的各種能力,其第一個參數nIndex指示要獲取信息的類型。當nIndex爲HORZSIZE和VERTSIZE時,可得到顯示區域的寬度和高度;當nIndex 爲HORZRES和VERTRES時,可得到每個水平和垂直方向的像素數即分辨率;當nIndex的值爲LOGPIXELSX 和LOGPIXELSY時,可得到水平和垂直方向每邏輯英寸所含像素數。

在介紹了邏輯英寸的知識以後,很容易將OLEServer設置爲基於邏輯英寸的映射模式。如果程序員僅僅調用SetMapMode(hdc,MM_LOENGLISH)來設置映射模式,當前的映射模式爲物理英寸,而不是邏輯英寸。設置邏輯英寸必須自定義窗口和視口的範圍,使xViewExt/xWinExt =0.01邏輯英寸中水平像素的點數,當xViewExt=LOGPIXELSX,xWinExt=100時,其比值正好滿足上述要求。

以下是設置映射模式的代碼。

intxLogPixPerInch=GetDeviceCaps(hdc,LOGPIXELSX);
intyLogPixPerInch=GetDeviceCaps(hdc,LOGPIXELSY);
SetMapMode(MM_ANISOTROPIC);
SetWindowExt(100,100);
SetViewportExt(xLogPixPerInch,yLogPixPerInch);

上述代碼中調用SetMapMode函數將映射模式設置爲自定義的,該調用必須位於SetWindowExt 和SetViewportExt調用之前,否則設置將會無效。
上述代碼實際上將映射模式設置成邏輯MM_LOENGLISH,若程序員需要設置邏輯MM_LOMETRIC、MM_HIMETRIC、MM_HIENGLISH 或MM_TWIPS,只需修改上述代碼中的SetWindowExt的參數,該參數實際上是每英寸所包含的各種映射模式下的單位數。根據表1中各映射模式的參數,可得到表2中每英寸所對應的各邏輯單位的個數。

例如,要設置邏輯MM_TWIPS,函數SetWindowExt中的參數爲應1440。

2.邏輯座標與設備座標轉換時誤差的處理

表2

映 射 模 式 每 英 寸 所 對 應 的 邏 輯 單 位 數
MM_LOENGLISH 100
MM_HIENGLISH 1000
MM_LOMETRIC 254
MM_HIMETRIC 2540
MM_TWIPS 1440


當我們將映射模式設置成基於邏輯英寸的MM_LOMETRIC時,窗口的範圍設爲256,視口的範圍設爲96(在VGA顯示器下LOGPIXELSX的值),約2.6個邏輯單位對應1個像素,這顯然會造成不小的誤差,它會表現在應用程序的各個方面:客戶區的一個部分沒有被刷新;對象之間本來沒有間距,卻顯示出有間距;對象在屏幕的不同位置上會縮小或增大一個像素等問題。

可以採取以下兩個步驟避免轉換誤差。(1)儘量選擇窗口範圍和視口範圍比可以整除的映射方式,例如基於邏輯英寸的MM_TWIPS其窗口範圍和視口範圍比1440/96,可簡化爲15/1,從設備座標轉化爲邏輯座標時沒有誤差,從消除誤差角度看,MM_TWIPS比其他幾個映射模式都要好。(2)窗口範圍和視口範圍比不能整除時,也儘量將其簡化,例如,當採用0.3900mm 中的將1個邏輯單位映射成1/64英寸的映射方式時,其窗口範圍和視口範圍比值爲64/96,可簡化爲2/3。如果我們將邏輯單位的值都取爲2的倍數,設備單位的值都取爲3的倍數,轉換後就沒有精度的丟失了。

綜上所述,如果我們能夠根據映射模式值的特點,邏輯座標和設備座標都取經簡化的窗口和視口範圍值的倍數,則邏輯座標和設備座標間的轉化將沒有誤差

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