视口与窗口内容摘要

  映射模式被定义为从“窗口”(window)(逻辑坐标)到“视口”(viewport)(设备坐标)的映射。

  Windows对所有消息(例如WM_MOVE、WM_SIZE和WM_MOUSEMOVE),所有的非GDI函数,甚至一些GDI函数,都继续使用设备坐标。可以按这种方式考虑:映射模式是设备环境的一种属性,因此,只有当使用以设备环境句柄作为参数的GDI函数时,映射模式才会生效。

  Windows会把在GDI函数中指定的逻辑坐标转换为设备坐标。

  Windows通常使用三种坐标系统:“屏幕坐标”、“全窗口坐标”、“客户区坐标”,在所有的设备坐标系统中,单位都是一像素的形式表示的。

  视口是以设备坐标(像素)的形式指定的。大多数情况下,视口与客户区相同,但是如果从GetWindowDC或者CreateDC函数获取了设备环境,视口也可以是指全窗口坐标或片;屏幕坐标。电(0,0)是客户区(或者全窗口,或者屏幕)的左上角。x值向右增加,y值向下增加。

  《逻辑坐标设备坐标转换》这是一篇关于逻辑坐标和设备坐标的文章,其实这篇文章最亮眼的地方在于头两句对这两个坐标概念的说明:“关于逻辑坐标(LP - Logical Point)和设备坐标(DP – Device Point) 对于屏幕,逻辑坐标的原点在最左下角,让整个屏幕在坐标轴的第一象限。设备坐标是在输出设备上定点绘制图形对象是用的,它采用笛卡尔坐标系,原点在屏幕的最左上角,x轴的值自左向右增加,y轴自右向左增加,单位为像素(设备单位)。”。其实看完《Windows程序设计》第158页的5.5.4节会发现,这篇文章的说明并不准确,因为根据映射模式和其它因素的不同,逻辑坐标原点的位置可以改变,不过他这种简单的理解可以借鉴。

  Windows提供SetViewportOrgEx函数和SetWindowOrgEx函数来改变视口原点和窗口原点。需要知道这两个函数的“运行方式”:如果想将视口原点改为(xViewOrg,yViewOrg),那么逻辑点(0,0)将会被映射到设备点(xViewOrg,yViewOrg)。如果想将窗口原点改为(xViewOrg,yViewOrg),逻辑点(xViewOrg,yViewOrg)将会被映射到设备点(0,0),即左上角。

  SetViewportOrgEx函数的参数总是以设备单位的形式给出,下边第一行代码表示逻辑点(0,0)将被映射到设备点(cxClient/ 2,cyClient / 2);SetWindowOrgEx的参数总是以逻辑单位的形式给出的,调用下面第二行的函数后,逻辑点(-cxClient / 2, -cyClient / 2)被映射到设备点(0,0).

  根据这段话在TextOut函数前“分别单独”运行下面两行代码观察效果(假定客户区宽为xClient像素,高为cyClient像素):

SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;
SetWindowOrgEx (hdc, -cxClient / 2, -cyClient / 2, NULL) ;

TextOut函数如下:

TextOut(hdc,0,0,TEXT ("Hello, Windows 98!"),lstrlen(TEXT ("Hello, Windows 98!")));

发现,在TextOut函数前添加上面两个函数中的任意一个后,TextOut函数的输出结果在屏幕中间,也就是说“这两行代码任意一行都可以定义逻辑点(0,0)为客户区的中心”。查看MSDN中关于TextOut的说明:The x-coordinate, in logical coordinates, of the reference point that the system uses to align the string.,TextOut的输出是依据逻辑点,这对理解上段中描述的两个函数的运行方式有帮助。查看MSDN的类似需要坐标的函数都有“ in logical coordinates”、“ in logical units”的解释,例如MoveToEx、Rectangle。

  相比之下,SetViewportOrgEx较易理解,SetWindowOrgEx是一个逆向思维(而且“使用SetWindowOrgEx函数来改变逻辑点(0,0),这个任务有点困难,因为SetWindowOrgEx的参数必须是逻辑坐标的形式。所以需要转换函数”),假设调用如下:

SetWindowOrgEx (hdc, 1, 1, NULL) ;

这是想将窗口原点改为(1,1),按照运行方式中的理解,此时逻辑点(1,1)被映射到设备点(0,0),但是TextOut函数中是从逻辑点(0,0)开始输出的,也就是说在视口即设备点(0,0)的左上方向,输出的内容是看不到的,所以,通常使用SetWindowOrgEx函数时,其中设置的坐标通常是负值。 

   函数中的逻辑坐标以0.01英寸的形式给出的,例如下面的代码:

SetMapMode (hdc, MM_LOENGLISH) ;

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

它是将文本显示在距离客户区左上角的右边和下边各一英寸的地方。

  其实使用SetViewportOrgEx和SetWindowOrgEx函数的主要目的也是应为映射面试改变后y轴数值增长的方向改变,所以设置一下原点,避免像上面TextOut函数中使用-100这样的负值。

  调用SetWindowExtEx函数时,要把参数设定为期望得到的逻辑窗口的逻辑大小,而在调用SetViewportExtEx函数时,则把参数设定为客户区的实际高度和宽度。SetWindowExtEx函数的参数表示每英寸里逻辑单位的个数,SetViewportExtEx函数的参数表示每英寸里物理单位(像素)的个数。所以根据这段话可以理解下面这句代码的逻辑坐标的单位为什么是1/16英寸:

SetWindowExtEx (hdc, 16, 16, NULL) ;

因为SetWindowExtEx函数参数表示每英寸里逻辑单位的个数,也就是说在这个函数调用后,每英寸里有16个逻辑单位,自然,每个逻辑单位是1/16英寸。

  当Windows调整这些范围时,它必须让逻辑窗口可以容纳在对应的物理视口之内,这就有可能导致一部分的客户区落在逻辑窗口之外。应当在掉用SetViewExtEx之前先调用SetWindowExtEx来有效地使用客户区的空间。

  其实调用完下面两行代码,不但设置了范围,而且确定了方向:

SetWindowExtEx (hdc, 32767, 32767, NULL) ;

SetViewportExtEx (hdc, cxClient, -cyClient, NULL) ;

因为两个函数中有坐标对应,在调用完

SetViewportOrgEx (hdc, 0, cyClient, NULL) ;

函数后,原点确定,于是整个坐标系完成。

__________________________________________________________________________________

  《Windows程序设计》第274页的程序使用了一次SetWindowOrgEx和SetViewportOrgEx的组合使用方式,

SetWindowOrgEx (hdc, 138, 36, NULL) ;

SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;

针对这个组合使用,给了一段解释:程序将窗口原点设置在电(138,36),这是“窗口区域”的中心(此时根据前面的知识:逻辑点(138,36)被映射到设备点(0,0),也就是客户区的左上角,也就是说,此时输出的内容将从客户区左上角开始,不过,只有窗口区域的右下象限的内容会显示出来,所以才调用SetViewportOrgEx函数来调整),并设置视口原点为(cxClient/2,,cyClient/2),至此,整时钟的四个象限出现在客户区。

  另外这两行代码前还有两行代码:

SetWindowExtEx (hdc, 276, 72, NULL) ;

SetViewportExtEx (hdc, cxClient, cyClient, NULL) ;

它们将窗口范围设置为276和72,然后对应到整个客户区。

  在【精讲】中有如下相关的几句:"一般来说,MFC的CDC绘图函数都使用逻辑坐标为参数;CWnd的成员函数一般使用设备坐标为参数,如返回鼠标位置作为屏幕位置测试时,一般也是使用设备坐标"

 

     ——理解相关概念,应阅读《Windows程序设计(第5版珍藏版)》第144页到160页,原本为了辅助理解想摘要一部分内容,写完才发现,这些页几乎句句是重点。

 2013-07-20

《WINDOWS程序设计》第280页,对这个问题有一个描述,各向同性映射模式再次成为(CLOCK)这样的应用程序的最佳选择,调用SetMapMode函数后,窗口大小被设置为1000,视口范围被设定为窗口是客户区宽度的一半,高度是客户区高度一半的负数。视口原点设置为客户区的中心点。

 

 

你可能感兴趣的:(窗口)