Win32映射模式和原点移动+坐标转换

前言:

在开始之前,需要了解两个概念:窗口和视口

视口是基于设备坐标系的,对于显示器,就是像素的,也就是你看到的。
窗口是基于逻辑坐标的,虚拟的,也是你写程序时使用的数字什么的。

在窗口(逻辑坐标)下编程,在视口(设备坐标)下显示。
我们在窗口 下编程完成后,要通过视口显示,这样我们才能看见,那么问题来了,怎么从窗口到视口的呢?那就是映射模式决定的了,映射模式能够决定按什么比例缩放(也就是下面的逻辑单位了),还有映射的方向,其实个人理解就是:它就像投影仪那种,从窗口投影到视口,投影的性质由映射模式控制。

一般通过beginPaint拿到的都是客户区;而使用getDC拿到的则是通常意义下的窗口:客户区+菜单栏+工具栏+标题栏等等。

映射模式决定逻辑 坐标的单位,设备坐标的单位是像素。

这个逻辑单位是由下面的数学公式计算出。
而窗口到视口的坐标映射,就是映射模式。
用数学公式表述为:
窗口到视口:
xViewport = (xWindow-xWinOrg)*xViewExt/xWinExt+xViewOrg
yViewport = (yWindow-yWinOrg)*yViewExt/yWinExt+yViewOrg
其中 xViewExt表示的是视口的横坐标范围,xWinExt表示的是窗口的横坐标范围通常我们关心的不是它们的具体值,而是二者的比例


1.逻辑单位取决xViewExt/xWinExt的比值

不同xViewExt/xWinExt的比值决定了一个逻辑单位代表多少个像素点长,不同映射模式决定了一个逻辑单位代表多少个像素点长。

举个例子来说:
我们将视口的横坐标范围xViewExt设置为100,窗口的横坐标范围xWinExt也为100,显然两者比值xViewExt/xWinExt=1,这说明了,从窗口到视口的映射是等比的(即逻辑单位为像素),1个逻辑单位表示1像素。当然我们将视口的横坐标范围xViewExt设置为50,窗口的横坐标范围xWinExt也为50,因为两者比值也为1,所有逻辑单位为像素。
我们可以通过设置窗口范围和视口范围,改变xViewExt/xWinExt的比值,这样就能改变逻辑单位在设备上的量度。映射模式决定逻辑单位这句话是没错的,映射模式一共有8种,其中前面6种系统已经设置好了,我们不能改变其逻辑单位和坐标轴方向,然最后两种:MM_ISOTROPIC模式和MM_ANISOTROPIC模式,我们是能自由发挥的,它们可以改变视口到窗口的转换因子。


一、8种映射模式

在这里插入图片描述
前面6种映射模式比较简单,这里只讲解一下MM_ISOTROPIC和MM_ANISOTROPIC,这两种的映射比例可以自定义,而且x轴和y轴的方向也可以自定义

MM_ISOTROPIC:各向同性,x轴和y轴的映射比例必须相同MM_ANISOTROPIC:各向异性,x轴和y轴的映射比例可以不同

扫描二维码关注公众号,回复: 9237529 查看本文章

刚开始将映像方式设定为MM_ISOTROPIC时,Windows使用与MM_LOMETRIC同样的窗口和视端口范围。

设置映射比例和坐标轴方向,就需要SetWindowExtEx函数和SetViewportExtEx函数,(特别提醒:所以必须在使用SetViewportExtEx之前使用SetWindowExtEx)。


1.设置映射模式

系统默认值是MM_TEXT。
SetMapMode函数:
该函数设置指定设备环境的映射方式,映射方式定义了将逻辑单位转换为设备单位的度量单位,并定义了设备的X、Y轴的方向

int SetMapMode(HDC hdc,
               int fnMapMode);  // 映射模式

GetMapeMode函数:
取设备环境映射比例的函数,获取hdc指定的设备环境X、Y方向上的逻辑比例并存入lpSize中

GetWindowExtEx( HDC hdc, 
                LPSIZE lpSize );

MM_ISOTROPIC和MM_ANISOTROPIC映射模式的置:

MM_ISOTROPIC模式: X轴和Y轴同时按比例变化,图像不变形,就算我们拉扯强制改变窗口大小,系统也会按X轴和Y轴方向上比较小的比例裁剪,保存各向同性。

MM_ANISOTROPIC模式: X轴和Y轴不按按比例变化,图像会变形。

API函数:
SetWindowExtEx函数:
该函数的作用是以指定的值为设备环境设置窗口的水平的和垂直的范围

BOOL SetWindowExtEx(HDC hdc, 
                    int nXExtent,   // 以逻辑单位给出的窗口的水平范围
                    int nYExtent,   // 以逻辑单位给出的窗口的垂直范围
                    LPSIZE lpSize); // 保存了先前的窗口的范围

SetViewportExt函数:
函数功能为该函数以设备单位或像素点设置“视区范围”。

实例:

SetMapMode(hdc, MM_ISOTROPIC);     // 设置映射模式
SetWindowExtEx(hdc, 50, 10, NULL);  // 设置窗口范围
SetViewportExtEx(hdc, 100, 100, NULL);  // 设置视口范围

SetWindowExtEx函数和SetViewportExt函数作用的真正有意义的是两个范围的比值。 在X轴方向上:100/50=2,说明一个逻辑单位映射2个像素。在Y轴方向上:100/10=10,说明一个逻辑单位映射10个像素。这里需注意: MM_ISOTROPIC模式下,当X轴与Y轴映射比例不同时,由于各向同性,系统则按比例小的映射。但为MM_ANISOTROPIC模式时,这时将会按实际比例映射,多数情况下,图像会变形。

坐标轴方向默认情况下x轴朝右y轴朝下,如果想让某一个轴反向,只需要在两个SetExt函数中选一个为目标轴的参数传负数即可


二、原点移动

原点的移动使得窗口绘制图形更方便和容易,一般使用下面其中一个函数就可达到目标需要。

API函数:
SetViewportOrgEx函数:
设置哪个设备点映射到窗口原点(0,0),即视口原点所在。

BOOL SetViewportOrgEx(HDC hdc, 
                      int X,       // 新Viewport的x坐标
                      int Y,       // 新Viewport的y坐标
                      LPPOINT lpPoint ); // 原来的Viewport的坐标

SetWindowOrgEx函数:
该函数用指定的坐标设置为设备环境的窗口原点。

GetViewportOrgEx和GetWindowOrgEx来获取目前视端口和窗口的原点。

可以这样理解,所有的绘图都是从窗口左上角位置开始,绘制完成后,我们将窗口原点与视口原点重合,这样形成的图形就是我们所看见的。利用SetViewOrg将视口的(0, 0)移动到了(x, y),这就相当于将逻辑点(0, 0)映射到了设备点(x, y)上,因此不用再移动逻辑点,里面的映射关系已经包含了;若利用SetWindowOrg将逻辑点(0, 0)移动到了(x, y),就相当于将逻辑点(x, y)映射到了设备点(0, 0)上,所有的一切都可以用逻辑坐标到设备坐标的映射上来;

在这里插入图片描述

SetMapMode(hdc, MM_TEXT);  // 设置映射模式

SetViewportOrgEx(hdc, 100, 100, NULL);   // 移动视口原点
//SetWindowOrgEx(hdc, -100, -100, NULL);  // 移动窗口原点

SetRect(&rect1, 0, 0, 100, 100);   // 设置矩形1
SetRect(&rect2, -100,-100, 0,0);   // 设置矩形2

hbrush1=CreateSolidBrush(RGB(255, 0, 0));
hbrush2=CreateSolidBrush(RGB(0, 255, 0));
FillRect(hdc, &rect1, hbrush1);   // 绘制矩形1
FillRect(hdc, &rect2, hbrush2);   // 绘制矩形2

如果不移动原点,矩形2是不能显示的,通过移动原点,矩形1和2都能显示,代码中移动视口原点和移动窗口原点效果是一样的。

三、坐标转换

问题背景:设想,你一直在一种公制的映射模式下编程,但是诸如鼠标击中测试之类的消息携带的击键坐标都是设备坐标(即像素点的位置),想要知道鼠标是否击中了某个区域那肯定得现将击中的设备坐标转化成公制坐标后才能进行判断,不可能拿设备坐标和公制坐标这两个不能相提并论的东西进行比较吧。

DptoLP函数:
将设备坐标转变为逻辑坐标,转变依赖于设备的图形模式,窗口和坐标的起点及范围的设置,和转换的内容。

BOOL DptoLP(HDC hdc, 
            LPPOINT lpPoints,// 指向POINT结构数组的指针,每个POINT结构中的X和Y坐标将被转换。
            int nCount);// 规定数组中点的数目

LPtoDp函数:
该函数把逻辑坐标转换为设备坐标,此转换依赖于设备环境的映射方式,原点的设置、窗口和观察口的范围及全局转换。


作者留言:
映射模式刚开始理解可能会有点小困难,想要快速掌握,那就是自己将代码敲几遍,改变其中的参数,观察运行结果,然后再自己总结分析,效果明显哦。

发布了30 篇原创文章 · 获赞 37 · 访问量 5501

猜你喜欢

转载自blog.csdn.net/qq_45021180/article/details/99691601