1. 程式人生 > >Windows繪圖中的對映模式

Windows繪圖中的對映模式

轉載地址:http://www.cnblogs.com/xi52qian/archive/2011/08/29/2158341.html

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都用下面兩個公式將視窗座標轉換成視口座標:

  1. xViewport = (xWindow - xWinOrg) * (xViewExt / xWinExt) + xViewOrg
  2. yViewport = (yWindow - yWinOrg) * (yViewExt / yWinExt) + yViewOrg

其中,(xWindow,yWindows)是待轉換的邏輯點,(xViewport,yViewport)是轉換後的裝置點。如果裝置座標是客戶區域座標或全視窗座標,則Windows在畫一個物件前,還必須將這些座標轉換成螢幕座標。

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

Windows還能將視口(裝置)座標轉換為視窗(邏輯)座標:

  1. xWindow=(xViewport-xViewOrg)*(xWinExt/xViewExt)+xWinOrg
  2. yWindow=(yViewport-yViewOrg)*(yWinExt/yViewExt)+yWinOrg

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

4.對映模式的種類

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

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

注:MM_TWIPS經常在印表機上,單位是1/20磅(1磅=1/72英寸)。

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

  1. MM_TEXT對映模式這種對映模式被稱為"文字"對映方式,不是因為它對 於文字最合適,而是軸的方向與讀文字的方向一致。Windows提供了函式SetViewportOrg和SetWindowOrg 用來設定視口和視窗的原點。預設的視窗原點和視口原點均為(0,0),可以改變;預設的窗 口範圍和視口範圍均為(1,1),不可改變。
  2. 度量對映方式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中水平畫素的點數。
  3. 自定義對映模式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的倍數,轉換後就沒有精度的丟失了。

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