M-Anonymous

视口和窗口这一部分也确实有点难懂,我看了好几个博主的博文。

现加上自己的一些看法,总结如下:

先来看一个小例子:

我们对 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函数 中指定的逻辑坐标映射为设备坐标。

默认情况下,所有关于坐标位置的都是相对于客户区左上角坐标并以像素为单位来绘制的。
 有一个称为“映射模式”的设备环境属性,它能影响几乎所有客户区绘制的图形。
 紧密相关的还有 4 个其他的设备环境属性:窗口原点,视口原点,窗口范围,视口范围。
设置和获取映射模式的函数:SetMapMode(), GetMapMode()。
 

我们使用逻辑坐标系绘图,然后要在设备坐标系下显示。所以就有一个逻辑坐标和设备坐标之间的转换。

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 个单位。

 

总结:

直观的说:
SetViewportOrgEx()函数是客户区不动,图像的原点坐标移动。
SetWindowsOrgEx()函数是图像的原点坐标不动,客户区移动。

移动视口原点就是移动画笔,移动窗口原点就是移动画布。设备点 (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 "。

 
 
SetMapMode() 介绍:
            功能:该函数设置指定设备环境的映射方式。(默认映射模式是 MM_TEXT )
                       映射方式定义了将逻辑单位转换为设备单位的度量单位,并定义了设备的X、Y轴的方向。
            函数原型:int SetMapMode(HDC hdc, int fnMapMode);
            参数:nMapMode     指定新映射的模式。它可以是以下任一值:
            MM_ANISOTROPIC:逻辑单位转换成具有任意比例轴的任意单位。x, y 的正方向可变。
            MM_HIENGLISH:每个逻辑单位转换为0.001英寸,X的正方向向右,Y的正方向向上。
            MM_HIMETRIC:每个逻辑单位转换为0.01毫米,X正方向向右,Y的正方向向上。
            MM_ISOTROPIC:逻辑单位转换成具有均等比例轴的任意单位,即沿X轴的一个单位等于沿Y轴的一个单位。
                                            x, y 的正方向可变。              
            MM_LOENGLISH:每个逻辑单位转换为0.01英寸,X正方向向右,Y正方向向上。
            MM_LOMETRIC:每个逻辑单位转换为0.1毫米,X正方向向右,Y正方向向上。
            MM_TEXT:每个逻辑单位转换为一个像素,X正方向向右,Y正方向向下。
            MM_TWIPS;每个逻辑单位转换为打印点的1/20(即1/1440英寸),X正方向向右,Y方向向上。
            返回值:如果函数调用成功,返回值指定先前的映射方式,否则,返回值为零。
            (提示:1 英寸 = 25.4 mm, 像素数 / DPI = 英寸数,DPI(每英寸多少个像素,这和分辨率有关))
 
设备环境:设备环境就是显示设备(显示器,打印机等等)。窗口和视口中的场景必须通过映射模式正确的显示到设备环境中。
 
GetMapMode() 介绍:
            功能:获取设备环境的映射模式。
            原型:int GetMapMode(HDC hdc) ;
            hdc:设备环境句柄。
            返回值:如果函数调用成功,返回值规定映射方式;否则,返回值为零。
 
设备坐标系统转换:
   将客户区坐标转换到屏幕坐标:ClientToScreen()。
   将屏幕坐标转换到客户区坐标:ScreenToClient()。
 
ClientToScreen() 介绍:
         功能:该函数将指定点,或者矩形的用户坐标转换成屏幕坐标。
         函数原型:BOOL ClientToScreen(HWND hWnd,LPPOINT lpPoint);
         参数:
                     hWnd :用户区域用于转换的窗口句柄。
                     lpPoint:指向一个含有要转换的用户坐标的结构的指针,如果函数调用成功,新屏幕坐标复制到此结构。
         返回值:如果函数调用成功,返回值为非零值,否则为零。
         注释:函数用屏幕坐标取代POINT结构中的用户坐标,屏幕坐标与屏幕左上角相关联。
 
ScreenToClient() 介绍:
         功能:函数功能:该函数把屏幕上指定点的屏幕坐标转换成用户坐标。
         函数原型:BOOL ScreenToClient(HWND hWnd, LPPOINT lpPoint);
         参数:
                 hWnd:指向窗口的句柄,此窗口的用户空间将被用来转换。
                 lpPoint:指向POINT结构指针,该结构含有要转换的屏幕坐标。
                 返回值:如果函数调用成功,返回值为非零值,否则为零。
 
GetWindowRect() 介绍:
          功能:该函数返回指定窗口的边框矩形的尺寸。该尺寸以相对于屏幕坐标左上角的屏幕坐标给出。
          函数原型:BOOL GetWindowRect(HWND hWnd, LPRECT lpRect);
          参数:
                    hWnd:窗口句柄。
                    lpRect:指向一个RECT结构的指针,该结构接收窗口的左上角和右下角的屏幕坐标。
           返回值:如果函数成功,返回值为非零:如果函数失败,返回值为零。
                         若想获得更多错误信息,请调用GetLastError函数。
 
GetClientRect() 介绍:
           功能:取窗口客户区的大小。总的来说就是把客户区的大小写进第二个参数所指的Rect结构当中。
           函数原型:BOOL GetClientRect(HWND hWnd, LPRECT lpRect);
           参数:
                   hWnd:是程序窗口的句柄。
                   lpRect:是一个指针,指向一个RECT类型的rectangle结构。
                                该结构有四个LONG字段,分别为left、top、right和bottom。
                                GetClientRect将这四个字段设定为窗口显示区域的尺寸。left和top字段通常设定为0。
                                right和bottom字段设定为显示区域的宽度和高度(像素点数)。 也可以是一个CRect对象指针。
                                CRect对象有多个参数,与RECT用法相同。
           返回值:如果函数成功,返回一个非零值。如果函数失败,返回零。 

 

设备点与逻辑点的转换函数:

DPtoLP() 介绍:

           功能:将设备坐标转变为逻辑坐标。

           函数原型:BOOL DPtoLP(HDC hdc, POINT * lpPoint,int nCount);

           参数:

                hdc:指向设备环境的句柄。
                lpPoints:指向POINT结构数组的指针,每一个POINT结构中的X坐标和Y坐标将被转换。
                nCount:指定数组中点的数目。
            返回值:如果函数调用成功,返回值为非零值,否则为零。
 

LPtoDP() 介绍:

           功能:该函数把逻辑坐标转换为设备坐标。

           函数原型:BOOL LPtoDP(HDC hdc, POINT * lpPoint,int nCount);

 

设置视口与窗口原点的函数:

 SetViewportOrgEX() 介绍:

             功能:说明哪个设备点映射到窗口原点(0,0)。      

             函数原型:BOOL SetViewportOrgEx( HDC  hdc, int  x, int  y, LPPOINT  lpPoint ) ;

            参数:

                     hdc:[输入]设备内容的HANDLE
                     X:[输入]新的视口原点的设备单位的x坐标
                     Y:[输入]新的视口原点的设备单位的y坐标
                     lpPointt:[输出]指向一个POINT结构用来接收原先的视口原点坐标,坐标是设备单位的。
                                  如果lpPoint是NULL,这个参数不使用。
            返回值:如果返回成功返回非0,如果失败,返回0。
 

SetWindowOrgEx() 介绍:

            功能:说明哪个窗口点映射到视口原点(0,0)。

            函数原型:BOOL SetViewportOrgEx( HDC  hdc, int  x, int  y, LPPOINT  lpPoint ) ;

            参数:

                     hdc:[输入]设备内容的HANDLE
                     X:[输入]新的窗口原点的设备单位的x坐标
                     Y:[输入]新的窗口原点的设备单位的y坐标
                     lpPointt:[输出]指向一个POINT结构用来接收原先的窗口原点坐标,坐标是逻辑单位的。
                                  如果lpPoint是NULL,这个参数不使用。
            返回值:如果返回成功返回非0,如果失败,返回0。
 
GetViewportOrgEx() 介绍:
             功能:该函数为指定设备环境获取视口原点的X坐标和Y坐标。
             函数原型:BOOL GetViewportOrgEx(HDC hdc, LPPOINT lpPoint);
             参数:
                     hdc:指向设备环境的句柄。
                     lpPoint:指向POINT结构的指针,该结构存放视口原点坐标。
             返回值:如果函数调用成功,返回值为非零值,否则为零。
 
GetWindowsOrgEx() 介绍:
             功能:该函数为指定的设备环境获取窗口原点的X坐标和Y坐标。
             函数原型:BOOL GetWindowOrgEx(HDC hdc, LPPOINT lpPoint);
             参数:
                    hdc:指向设备环境的句柄。
                    lpPoint:指向POINT结构的指针,该结构以页面为单位存放窗口原点的坐标。
             返回值:如果函数调用成功,返回值为非零值,否则为零。
 

SetWindowExtEx() 介绍:
       功能:以指定的值为设备环境设置窗口的水平的和垂直的范围。

       函数原型:BOOL SetWindowExtEx(HDC hdc, int  nXExtent, int  nYExtent, LPSIZE  lpSize);

       参数:

              hdc:设备环境句柄。
              nXExtent:以逻辑单位给出的窗口的水平范围。
              nYExtent:以逻辑单位给出的窗口的垂直范围。
              lpSize[输出]:一个指向SIZE结构的指针,该结构保存了先前的窗口的范围,如果lpSize为NULL,那么不返回值。
        返回值:如果函数调用成功,返回值为非零。如果函数调用失败,返回值为零。
 

SetViewportExtEx() 介绍:

         功能:以指定的值为设备环境设置视口的水平的和垂直的范围。

         函数原型:BOOL SetViewportExtEx(HDC hdc, int nXExtent, int nYExtent, LPSIZE lpSize);

         参数:

               hdc:设备环境的句柄。
               nXExtent:指定观察口以设备单位为单位的水平轴的范围。
               nYExent:指定观察口以设备单位为单位的垂直轴的范围。
               lpSize:指向Size结构的指针,先前的设备单位为单位的视口范围存放在此结构中,如lpSize值为NULL,则什么也没返回。
          返回值:如果函数调用成功,返回值为非零。如果函数调用失败,返回值为零。
 

GetWindowExtEx() 介绍:
           功能:获取指定的设置环境的窗口的X和Y的范围。

           函数原型:BOOL GetViewportExtEx(HDC hdc, LPSIZE lpSize);

           参数:

                   hdc:指向设备环境的句柄。
                   lpSize:指向结构的指针,X和Y的范围以设备单位放在此结构中。
           返回值:如果函数调用成功,返回值为非零值,否则为零。
 

GetViewportExtEx() 介绍:

           功能:获取指定的设置环境的视口的X和Y的范围。

           函数原型:BOOL GetViewportExtEx(HDC hdc, LPSIZE lpSize);

           参数:

                   hdc:指向设备环境的句柄。
                   lpSize:指向结构的指针,X和Y的范围以设备单位放在此结构中。
           返回值:如果函数调用成功,返回值为非零值,否则为零。

            

分类:

技术点:

相关文章: