视口和窗口这一部分也确实有点难懂,我看了好几个博主的博文。
现加上自己的一些看法,总结如下:
先来看一个小例子:
我们对 windows 说在 (5,6) 处画个点 (调用GDI函数)。windows 认为是在我们的思维的 (5,6) 处画了个点。
(也就是说 (5,6) 是逻辑坐标,GDI函数中的大部分都是逻辑坐标)
那么,要把它映射到屏幕上,必须作一些转换。
那么问题来了:
1、原点在哪里,坐标轴的方向如何?(即坐标系的选定)。
2、逻辑坐标 (5,6) 该如何转换到屏幕上的坐标 ?
SetViewportOrgEx() 和 SetWindowOrgEx() 是和第一个问题相关的的。设置原点和设置X轴Y轴方向 SetMapMode()。
SetViewportOrgEx 与 SetWindowOrgEx 解析
这两个函数,是用来改变视口和窗口的原点,(默认情况下,视口和窗口原点都和设备点 (0,0) (即客户区左上角) 相同)。
窗口和视口:
我们将逻辑坐标所在的坐标系称为"窗口",将设备坐标所在的坐标系称为"视口"。
"窗口"依赖于逻辑坐标,可以是像素点、毫米、英寸或程序员想要的其他尺度。
"视口"依赖于设备坐标(像素点)。
对于视口和窗口的概念可以这样理解:
幻想显示器大小可以随便改变,那么显示器每次都变成view「视口」的太小就可以了,window「窗口」就没存在的必要了。
view「视口」就是实际所需的大小;
window「窗口」就是显示器给你的限制。
设备坐标系:
设备坐标系统取决于你获取设备环境所用的函数。设备坐标系有三种:
客户区坐标系、窗口坐标系、屏幕坐标系。(以默认的 MM_TEXT 映射模式介绍)
设备坐标系中的屏幕坐标系:就是以整个显示器为坐标系,以屏幕的左上角为坐标系原点。
设备坐标系中的窗口坐标系:就是以你程序的整个窗口为坐标系,以程序窗口的左上角为原点。
设备坐标系中的客户区坐标系:就是以程序窗口里面的客户区为坐标系,以客户区的左上角为原点。
逻辑坐标系:
映射方式只是针对逻辑坐标系的。所有的 GDI 绘图操作的坐标都是在逻辑坐标系下坐标。
Windows必须将逻辑单位转换为“设备单位”---像素。
这种转换是由映射方式、窗口和视口的原点以及窗口和视口的范围所控制的。
理解窗口与视口的坐标转换公式:
Xviewport= (Xwindow-Xwinorg) * Xviewext / Xwinext + Xvieworg;
Yviewport= (Ywindow-Xwinorg) * Yviewext / Ywinext + Yvieworg;
此公式初看上去不好理解,变形如下:
(Xviewport-Xvieworg) / (Xwindow-Xwinorg) = Xviewext / Xwinext;
(Yviewport-Yvieworg) / (Ywindow-Xwinorg) = Yviewext / Ywinext;
(Xwindow,Ywindow) 是一个待转换的逻辑点坐标;(Xvieport,Yviewport) 是转换后的设备坐标点坐标;
(Xwinorg,Ywinorg) 是在逻辑坐标系下的窗口原点;(Xvieworg,Yvieworg) 是在设备坐标系下视口的原点;
(Xwinext,Ywinext) 是在逻辑坐标系下的窗口范围;(Xviewext,Yviewext) 是在设备坐标系下的视口范围;
如此就很好理解了:逻辑坐标单位与设备坐标单位的比,即比例因子。
GDI 映射模式:
映射方式定义了Windows如何将 GDI函数 中指定的逻辑坐标映射为设备坐标。
我们使用逻辑坐标系绘图,然后要在设备坐标系下显示。所以就有一个逻辑坐标和设备坐标之间的转换。
SetViewportOrgEx的参数总是使用设备坐标系单位(像素)。
假设显示区域为 cxClient 像素宽和 cyClient 像素高。映像方式为MM_TEXT。
如果想将逻辑点(0,0)定义为显示区域的中心,可进行如下呼叫:
SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;
逻辑点(0,0)将映像为设备点(cxClient/2,cyClient/2)。
SetWindowOrgEx的参数总是使用逻辑单位。
要想获得与上面使用SetViewportOrgEx同样的效果则可以进行如下呼叫:
SetWindowOrgEx (hdc, -cxClient / 2, -cyClient / 2, NULL) ;
逻辑点(-cxClient / 2,-cyClient / 2)映像为设备点(0,0),即显示区域的左上角。
很多人对于SetWindowOrgEx使用负数坐标参数表示很困惑,这里做一下解释。
首先要知道 不管对窗口和视口原点作什么改变,设备坐标点(0,0)始终是显示区域的左上角。
初始情况下画笔从世界坐标系的原点开始画,画笔的原点就是画布的原点就是世界坐标系的原点。
移动视口原点好比移动画笔,如果将视口原点设置为 (xViewOrg,yViewOrg),相当于对画笔的移动,
则逻辑点 (0,0) 就会被映射为设备点 (xViewOrg,yViewOrg)。
初始时绘图在逻辑点(0,0)下笔,现在图形将以客户区 (设备点) 的(xViewOrg,yViewOrg)为中心进行绘制。
在 MM_TEXT 映射模式下 SetViewportOrg(hdc,cxclient / 2, cyClient / 2);
绘图坐标原点从 (0,0) 向右下方移动到(cxClient / 2, cyClient / 2)。
可以看出 SetViewportOrg() 函数可以更改设备上下文的坐标原点。
移动窗口原点好比画布的移动,如果将窗口原点改变为 (xWinOrg,yWinOrg),
则逻辑点(xWinOrg,yWinOrg)将会被映射为设备点(0,0)。
SetWindowOrg(hdc,-cxclient / 2, -cyClient / 2);逻辑点(-cxclient / 2, -cyClient / 2)对应于设备点(0,0);
可以这样理解:将画布分别向右,向下移动 cxClient / 2 ,cyClient / 2 个单位。
总结:
移动视口原点就是移动画笔,移动窗口原点就是移动画布。设备点 (0,0) 始终在左上角。
SetViewportExtEx和SetWindowExtEx是管第二个问题的。 设置比率。
其实想象一下也可以知道,5和6应该是乘上一个比率来变成像素单位。而后根据原点并按照X轴Y轴方向去画就可以了。
SetViewportExtEx和SetWindowExtEx就是决定比率的函数。(他们的第二个和第三个参数分别相除来代表横纵坐标的比率。)
SetWindowExtEx用于设置逻辑坐标范围。
SetViewportExtEx用于设置设备坐标范围。
必须在呼叫SetViewportExtEx之前呼叫SetWindowExtEx,以便最有效地使用显示区域中的空间。
举个例子:
SetMapMode(hdc,MM_ANISOTROPIC);
SetWindowExtEx(hdc,1,1,NULL);
SetViewportExtEx(hdc,cxChar,cyChar,NULL);
TextOut(hdc,3,2,TEXT( "Hello "),5);
由于TextOut的坐标值是 3和2,在Windows98允许的范围32767之内,所以还是可以显示出 "Hello "字符串。
又因为程序把窗口的范围(逻辑坐标范围)定义为1,而视口的范围(设备坐标范围)定义为 cxChar和cyChar。
因此在显示时,Windows会在(3*(cxChar/1),2*(cyChar/1))的地方(也就是第二行的第三个字符处)显示字符串 "Hello "。
我们通过改一改数值来证明一下:
SetMapMode(hdc,MM_ANISOTROPIC);
SetWindowExtEx(hdc,4,4,NULL); //设窗口范围(逻辑坐标范围)为4个逻辑单位
SetViewportExtEx(hdc,8,8,NULL); //设视口范围(设备坐标范围)为8个物理单位(也就是8个像素)
TextOut(hdc,1,1,TEXT( "Hello "),5); //在逻辑坐标(1,1)处显示 "Hello "。
从上面的代码我们得出X轴的逻辑与物理比值为2,Y轴的逻辑与物理比值也为2,1*(8/4)=2,
因此,Windows事实上会在客户区的左上角往右2个像素、往下2个像素的地方显示 "Hello "。
功能:获取设备环境的映射模式。
功能:函数功能:该函数把屏幕上指定点的屏幕坐标转换成用户坐标。
设备点与逻辑点的转换函数:
DPtoLP() 介绍:
功能:将设备坐标转变为逻辑坐标。
函数原型:BOOL DPtoLP(HDC hdc, POINT * lpPoint,int nCount);
参数:
LPtoDP() 介绍:
功能:该函数把逻辑坐标转换为设备坐标。
函数原型:BOOL LPtoDP(HDC hdc, POINT * lpPoint,int nCount);
设置视口与窗口原点的函数:
SetViewportOrgEX() 介绍:
功能:说明哪个设备点映射到窗口原点(0,0)。
函数原型:BOOL SetViewportOrgEx( HDC hdc, int x, int y, LPPOINT lpPoint ) ;
参数:
SetWindowOrgEx() 介绍:
功能:说明哪个窗口点映射到视口原点(0,0)。
函数原型:BOOL SetViewportOrgEx( HDC hdc, int x, int y, LPPOINT lpPoint ) ;
参数:
功能:该函数为指定设备环境获取视口原点的X坐标和Y坐标。
SetWindowExtEx() 介绍:
功能:以指定的值为设备环境设置窗口的水平的和垂直的范围。
函数原型:BOOL SetWindowExtEx(HDC hdc, int nXExtent, int nYExtent, LPSIZE lpSize);
参数:
SetViewportExtEx() 介绍:
功能:以指定的值为设备环境设置视口的水平的和垂直的范围。
函数原型:BOOL SetViewportExtEx(HDC hdc, int nXExtent, int nYExtent, LPSIZE lpSize);
参数:
GetWindowExtEx() 介绍:
功能:获取指定的设置环境的窗口的X和Y的范围。
函数原型:BOOL GetViewportExtEx(HDC hdc, LPSIZE lpSize);
参数:
GetViewportExtEx() 介绍:
功能:获取指定的设置环境的视口的X和Y的范围。
函数原型:BOOL GetViewportExtEx(HDC hdc, LPSIZE lpSize);
参数: