程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> Windows映射模式及相關問題的解決

Windows映射模式及相關問題的解決

編輯:關於VC++

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時,可得到水平和垂直方向每邏輯英寸所含像素數。---www.bianceng.cn。

在介紹了邏輯英寸的知識以後,很容易將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的倍數,轉換後就沒有精度的丟失了。

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved