Windows 映射模式

                                             I_Love_CPP

一、 映射模式

这是一个初级话题,但好像很多人都没有弄明白,因为几乎每过几天就用人发帖问这个。半年前有人问我这个问题时就想写这篇文章了,但一直觉得没有时间(因为除了学习、工作和玩耍,几乎就没有多少空时间了)。本文现在其实只能算着《 Programming Windows 》中关于“映射模式”的加工,以咱们中国读者更易懂的方式写出;我将逐步往其中添加一些相关内容。

首先,从一个十分简单但又很常用的函数开始:
BOOL TextOut( HDC hdc , // handle to DC
int nXStart , // x-coordinate of starting position
int nYStart , // y-coordinate of starting position
LPCTSTR lpString , // character string
int cbString // number of characters );
这里的( nXstart , nYStart )是字符串显示的起始坐标点,这里的坐标就是“逻辑坐标”。为了最终将文字显示到设备(屏幕)上, Windows 必须将这些逻辑坐标“翻译”成“设备坐标” —— 其实就是“像素 (pixels) ”。 Windows 一共定义了 8 种映射模式,如下表所示:

-----------------------------------------------------------------------------------
                                                                      Increasing Value
Mapping Mode            Logical Unit           x-axis            y-axis

MM_TEXT                       Pixel                      Right             Down
MM_LOMETRIC            0.1 mm                  Right             Up
MM_HIMETRIC              0.01 mm               Right             Up
MM_LOENGLISH          0.01 in.                  Right             Up
MM_HIENGLISH            0.001 in.               Right             Up
MM_TWIPS                     1/1440 in.             Right             Up
MM_ISOTROPIC           Arbitrary (x = y)     Selectable   Selectable
MM_ANISOTROPIC      Arbitrary (x !=y)     Selectable   Selectable
--------------------------------------------------------------------------------------
表 1

注: in. 表 英寸 ; Windows 默认的映射模式是 MM_TEXT ,其实就是 设备坐标 !!!

趁热打铁,看一下例子:

例: 如果我们没有用 SetMapMode 设定 Windows 的映射模式,那么 Windows 将使用默认的 MM_TEXT 映射模式,

TextOut (hdc, 8, 16, TEXT ("Hello"), 5);

的结果是文字将从离客户区左边界 8 个像素、离上边界 16 个像素的位置从左向右输出文字。

SetMapMode (hdc, MM_LOENGLISH);

TextOut (hdc, 50, -100, TEXT ("Hello"), 5);

的结果将是 从离客户区左边界 50* 0.01 in .= 0.5 in . 、离上边界 100*0.01 = 1 in . 的位置从左向右输出文字。

但 TextOut 的第 3 个参数为什么是 -100 呢?因为客户区左上角的坐标的默认值始终是 (0,0) 的(后面将详细讲到),而上面的列表不是写了吗 ? 在 MM_LOENGLISH 下, Y 轴值递增的方向是向上,也就是说向下递减,当然此时第 3 个参数为负值了。

Windows 设定这些映射模式是为了让用户使用起来更方便,因此你选定合适的映射模式来简化你的工作;或者选用 MM_ISOTROPIC 、 MM_ANISOTROPIC 自己设定逻辑坐标的单位,数值递增方向;或者干脆就用 MM_TEXT (相当于直接使用设备坐标),需要缩放时用 GetDeviceCap 获得必要的信息后 自己去计算得了。

二、坐标原点

常看到网友问 CDC::SetWindowOrg 和 CDC:: SetViewportOrg 的用法,它们实质是封装了 GDI 函数 SetWindowOrgEx 和 SetViewportOrgEx :

而解释是:

The SetWindowOrgEx function specifies which window point maps to the viewport origin (0,0). The SetViewportOrgEx function specifies which device point maps to the window origin (0,0).

初学者往往一头雾水。

其实“映射模式”中的“映射”就是数学中的“映射”,一种对应关系或者说是一个数学表达式而已。这个表达式就是:

公式一:
xViewport = (xWindow - xWinOrg) × Kx + xViewOrg
yViewport = (yWindow - yWinOrg)  × Ky + yViewOrg

等价公式二:

xWindow = (xViewport - xViewOrg) /Kx + xWinOrg
yWindow = (yViewport - yViewOrg) /Ky + yWinOrg

其中( xWinOrg,yWinOrg ),( xViewOrg,yViewOrg )分别表示逻辑坐标和设备坐标的原点,默认值均为( 0,0 );

( xWindow,yWindow )表示逻辑坐标点,( xViewport,yViewport )表示与之对应的设备坐标点(也就是说,在当前的映射模式下, Windows 将( xWindow,yWindow )影射为( xViewport,yViewport )。);

常数 Kx,Ky 是缩放系数(后面将详细介绍),不同的映射模式下缩放系数不同。

因此,在默认的情况下,( xWinOrg,yWinOrg ) = ( xViewOrg,yViewOrg ) = ( 0,0 ),公式一其实就是:

xViewport = xWindow / Kx          yViewport = yWindow / Ky 。

因此,可以看出,映射模式其实就是设定了一个比例而已,没有什么了不起的!

例:

SetMapMode(hdc,MM_TEXT); // 在 MM_TEXT 下缩放比例 Kx = Ky = 1;

SetWindowOrgEx (hdc,100,100,NULL); // 此时( xWinOrg,yWinOrg )被赋值为( 100 , 100 );

                                                              // 而 ( xViewOrg,yViewOrg )保持不变为( 0 , 0 )。

那么公式一即为:

xViewport = xWindow -100       yViewport = yWindow -100

因此

TextOut (hdc, 520, 530, TEXT ("ILoveYou,MyGirl"), 15);
                                                                            // ( xWindow,yWindow ) = ( 520 , 530 )

将会从( 520 , 530 ) - ( 100 , 100 ) = ( 220 , 230 )点开始在设备上显示。

而 SetViewportOrgEx 是用来设定( xViewOrg,yViewOrg )的值的,使用方法与 SetWindowOrgEx 的使用类似。从公式一可以看出,在 MM_TEXT 中,

SetWindowOrgEx (hdc, x, y , NULL) ;

SetViewportOrgEx (hdc, - x, - y, NULL);

等价的。

三、缩放系数

缩放系数
Kx = xViewExt / xWinExt
Ky = yViewExt / yWinExt

(xViewExt,yViewExt)和(xWinExt,yWinExt)可分别由 GetViewportExt 和 GetWindowExtEx 得到。

在 MM_TEXT 下, Kx = Ky = 1 ;其它映射模式下Kx、Ky均为常数,它们可以由下表来计算(Window NT,显示设备:1024x768 pixels):
--------------------------------------------------------------------------------------
Mapping Mode     Viewport Extents (x, y)      Window Extents (x, y)

MM_LOMETRIC      (1024, -768)                    (3200, 2400)
MM_HIMETRIC       (1024, -768)                    (32000, 24000)
MM_LOENGLISH   (1024, -768)                    (1260, 945)
MM_HIENGLISH     (1024, -768)                   (12598, 9449)
MM_TWIPS              (1024, -768)                    (18142, 13606)
------------------------------------------------------------------------------------

表 2 Windows NT Extents (1024 x 768)

参考文献:

   MSDN by Microsoft

《 Visual C++ 技术内幕》 by David J. Kruglinski et al

《 Programming Windows 》 5 th Edition ,by Petzoldi

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~The End

本人才疏学浅,本文若有什么不当之处,请告知,谢谢。