关于这两个函数在网上查了几篇文章,说法不完全一样,经过一些代码实验现将自己的理解记录如下:
首先引入概念:页面空间到设备空间的转换所用的是两个矩形的宽与高的比率(转换因子),其中页面空间中的矩形被称为窗口(使用逻辑坐标),设备空间中的矩形被称为视口(使用设备坐标),Windows把窗口原点映射到视口原点,把窗口范围映射到视口范围,就完成了这种转换[1]。
在下认为上面红色字体所说的映射转换是Windows自动实现的,程序员需要做的工作就是设定好原点坐标、窗口尺寸和映射模式。
几乎在所有GDI函数中使用的坐标值都是逻辑坐标,Windows对所有的消息、所有的非GDI函数和个别GDI函数(例如GetGeviceCaps函数)采用设备坐标[1]。
逻辑坐标是系统用作记录的坐标[2],是一个抽象的平面坐标系,它的x轴和y轴方向以及坐标单位由映射模式和相关参数设置决定。
设备坐标是指输出设备上的坐标(如屏幕),以像素为单位,x轴向右为正,y轴向下为正[2]。
注:关于设备坐标的方向,孙鑫的《VC++深入详解》中也是说x轴向右为正,y轴向下为正。但是在下通过程序实验发觉有不同的解释,具体见下面的例子。
1. 关于逻辑原点和设备原点
网易博主embeded-在文章《SetWindowOrg》里写道对这两类原点的理解:设备原点永远在左上角,也就是最初的(0,0)的位置,逻辑原点是可以移动的,Y轴的正向是向上还是向下是可以改变的[3],不过在下更认同另一篇文章的看法:原点与(0,0)并没有必然联系[2]。不过前者所说的有一点与在下理解相同,那就是设备点的(0,0)坐标点始终位于客户区的左上角,只是这个(0,0)点未必就是设备原点。同样对于逻辑原点也是这个道理。
在这个理解的基础上,SetWindowOrg和SetViewportOrg这两个函数的功能就比较明了了。SetWindowOrg是指定窗口(逻辑坐标)中的某个点作为逻辑坐标的原点,SetViewportOrg是指定视口(设备坐标)中的某个点作为设备坐标的原点。
2. SetWindowOrg的一个小例子
pDC->SetMapMode(MM_TEXT);
pDC->SetWindowOrg(100,50); // 逻辑单位
pDC->Rectangle(0,0,200,200); // 参数是逻辑单位,但绘制出来要转换为设备单位

图1 程序执行结果图

图2 原理图
第一行代码指明映射方式为MM_TEXT模式(这也是缺省模式),即一个逻辑单位对应1个设备像素,且坐标轴方向一致,均为x轴向右为正,y轴向下为正。
第二行代码将窗口的逻辑坐标原点设置为(100,50),或者说将逻辑坐标点(100,50)设置成坐标原点(注意此时不要理解成将(100,50)变成了(0,0),逻辑坐标点(0,0)和(100,50)的位置没有发生改变,只是缺省时原点为(0,0),但这里已经靠代码人为地将原点改变成了(100,50),(0,0)已不再是原点了)。
第三行绘制一个200*200的正方形,第二行和第三行的数值都是逻辑单位,方框与原点的关系如图2中窗口部分所示。然后这是程序为了将图像通过显示器显示出来,需要Windows进行页面空间到设备空间的转换,即窗口到视口的映射。映射模式为MM_TEXT,一个逻辑单位对应1个像素,Windows把窗口原点映射到视口原点,把窗口范围映射到视口范围,原点和方框被映射的效果如图2所示,通过观察图2可以发现,这种映射并不能保证图形坐标值的不变,而是保持了图形相对于原点位置的不变(这里采用了简单的MM_TEXT模式,如果改用其他映射模式,由于坐标轴方向的改变和转换因子的不为1,图形还可能发生镜像、旋转、水平或竖直方向缩放等变化)。图2中表示视口的方框就是图1中程序界面的客户区(白色部分),方框位于客户区外部的地方无法显示出来,因此只能看到右下角100*150范围的部分。
3. SetWindowOrg和SetViewportOrg同时使用
pDC->SetMapMode(MM_LOMETRIC); // 一个逻辑单位对应0.1mm,x向右为正,y向上为正
pDC->SetWindowOrg(100,50); // 将(100,50)设为逻辑原点,逻辑坐标
pDC->SetViewportOrg(50,100); // 将(50,100)设为设备原点,设备坐标,单位为像素
pDC->Rectangle(50,50,200,200); // 参数是逻辑单位,但绘制出来要转换为设备单位

图3 程序执行结果图
在下根据之前在网上和书上看到的一些说法来解释这个结果时遇到了困难。
首先关于设备坐标方向的固定问题,映射模式MM_LOMETRIC是x向右为正,y向上为正,那么这个是说的逻辑坐标还是设备坐标呢?如果说逻辑坐标,逻辑坐标其实是个虚拟坐标系,不会作为可视化效果出现,这样一来正向朝哪里其实结果都一样。更重要的是,通过测量图3中的像素(利用简单的图像处理软件就可以做到),在下发现其实设备原点是位于客户区左上角的(0,0)点往右50个像素往下100个像素,也就是说设备坐标的方向依旧是x向右为正,y向下为正,像素为单位。但是绘制出来的方框却是往上画的,虽然给的参数是正的数值(50,50,200,200),也就是说一个边长为150个逻辑单位的正方形,以设备坐标(50,100)为原点(这里是像素单位),再根据MM_LOMETRIC的规定的方向和大小画出一个正方形,确定原点的时候还是以设备坐标为准的,可是画正方形的时候已经变成了逻辑坐标了(这里正方形的尺寸已经转化成逻辑单位了,此处边长为150个逻辑单位,对应15mm),只是方框相对于原点和坐标轴的位置没有变(比如这里,方框整体位于y轴正半轴部分,而x轴正半轴部分面积为负半轴面积的两倍)。具体见图4。

图4 原理图
这么一来是可以解释为何代码执行后会出现这样的结果。但是这样理解起来似乎又比较复杂,如果真的是这么个道理的话(之前的描述属于在下的推断,还有待进一步验证),也就意味着在视口(设备坐标)中存在着两个坐标系,一个是其自己的设备坐标,以像素为单位,用来确定原点在哪里;另一个是从窗口映射到视口的逻辑坐标,映射后的坐标方向和单位由映射方式决定,映射图形时在先前已经确定的原点的基础上,根据窗口中图形与原点的方位关系进行映射。
结束语:暂时先写到这里,想法不是很成熟,但是目前可以解释一些情况,关于这方面在书上和网上一直没能找到满意的解答,欢迎有看法的朋友一起讨论。
参考文献:
1. 孙鑫《VC++深入详解》第11章 p404 - 411
2. http://wenku.baidu.com/view/da0e8692daef5ef7ba0d3cfb.html
3. http://embeded-life.blog.163.com/blog/static/105932519200942511331799/
本文探讨了Windows图形编程中的SetWindowOrg和SetViewportOrg函数,解释了逻辑原点和设备原点的概念。通过实例分析,阐述了这两个函数如何影响窗口和视口的坐标转换,以及在不同映射模式下的图形显示效果,旨在澄清关于设备坐标方向和逻辑坐标移动的误解。
1549

被折叠的 条评论
为什么被折叠?



