Windows映射模式

最近在学Win32的编程,看的是《Windows程序设计第5版》一书,这本书是台湾人翻译的,有些译法和大陆不一样,书中还有一些错误的地方,很多时候需要中英文对照阅读,下载请点击

Windows应用程序绘制图形时使用的是一种逻辑单位,每个逻辑单位的大小由映射模式决定,
这个逻辑单位既可以与设备单位(屏幕或打印机上的一个像素点)相同,也可以是一种物理单
位(如毫米),还可以是用户自定义的一种单位。在Windows应用程序中,只要与输出有关系,都
要使用映射模式。本文的目的是帮助读者了解映射模式的一些基本知识,并对在使用中经常
出现的一些问题提出解决方案。

(1)Windows坐标系统
Windows坐标系分为逻辑坐标系和设备坐标系两种,GDI支持这两种坐标系。一般而言,
GDI的文本和图形输出函数使用逻辑坐标,而在客户区移动或按下鼠标的鼠标位置是采用设备坐标。
<1>逻辑坐标系是面向DC的坐标系,这种坐标不考虑具体的设备类型,在绘图时,Windows会根据当前设置的映射模式将逻辑坐标转换为设备坐标。
<2>设备坐标系是面向物理设备的坐标系,这种坐标以像素或设备所能表示的最小长度单位为单位,X轴方向向右,Y轴方向向下。设备坐标系的原点位置(0,
0)不限定在设备显示区域的左上角。

一、映射模式基本知识
当Windows应用程序在其客户区绘制图形时,必须给出在客户区的位置,其位置用x和y
两个坐标表示,x表示横坐标,y表示纵坐标。在所有的GDI绘制函数中,这些坐标使用的是一
种”逻辑单位”。当GDI函数将输出送到某个物理设备上时,Windows将逻辑坐标
转换成设备坐标(如屏幕或打印机的像素点)。逻辑坐标和设备坐标的转换是由映射模式决
定的。映射模式被储存在设备环境中。GetMapMode函数用于从设备环境得到当前的映射模
式,SetMapMode函数用于设置设备环境的映射模式。
1.逻辑坐标

 

好了,下面开始正文:

逻辑坐标是独立于设备的,它与设备点的大小无关。使用逻辑单位,是实现”所见即所得”的基础。当程序员在调用一个画线的GDI函数LineTo,画出25.4mm(1英寸)
长的线时,他并不需要考虑输出的是何种设备。若设备是VGA显示器,Windows自动将其转化为96个像素点;若设备是一个300dpi的激光打印机,Windows自动将其转化为300个像素点。

设备坐标系分为屏幕坐标系、窗口坐标系和客户区坐标系三种相互独立的坐标系。
1.屏幕坐标系以屏幕左上角为原点,一些与整个屏幕有关的函数均采用屏幕坐标,如GetCursorPos()、SetCursorPos()、CreateWindow()、MoveWindow()。弹出式菜单使用的也是屏幕坐标。
2.窗口坐标系以窗口左上角为坐标原点,它包括窗口标题栏、菜单栏和工具栏等范围。
3.客户区坐标系以窗口客户区左上角为原点,主要用于客户区的绘图输出和窗口消息的处理。鼠标消息的坐标参数使用客户区坐标,CDC类绘图成员函数使用与客户区坐标对应的逻辑坐标。

在看到GDI(GDI Graphic Device
Interface图形设备接口)
映射方式这一节的时候,书中又是逻辑坐标,又是设备坐标,又是视口,窗口,又是视埠什么的,搞得人头都大了。虽然我现在还没有完全读懂,但是我感觉我已经抓住了理解这些东西的主线,下面的东西就当作我的笔记吧:

2.设备坐标

 

1.逻辑坐标和设备坐标

Windows将GDI函数中指定的逻辑坐标映射为设备坐标,在所有的设备坐标系统中,单位以像素点为准,水平值从左到右增大,垂直值从上到下增大。

(2)坐标之间的相互转换
 编程时,有时需要根据当前的具体情况进行三种设备坐标之间或与逻辑坐标的相互转换。
1.MFC提供了两个函数CDC::DPtoLP()和CDC::
LPtoDP()用于设备坐标与逻辑坐标之间的相互转换。
2.MFC提供了两个函数CWnd::ScreenToClient()和CWnd::ClientToScreen()用于屏幕坐标与客户区坐标的相互转换。

 
 首先,逻辑坐标这个名词就让很多人望而却步,确实,不能“望文生义”地理解的翻译就不是好翻译 
 ——鲁迅。哈哈,开个玩笑,我们要理解这两个东西,首先要想到如果你要用Win32要绘制一个东西,该怎么做呢?比如绘制一个矩形,假设我们调用的是Rectangle(hdc,30,20,50,80),(这个函数的用法是Rectangle(hdc,left,top,right,bottom),我叫雷锋,不用谢我)。可以看到,跟很多GDI函数一样,这个函数里面使用了很多数字,坐标。让我们回忆一下小学知识,绘制一个东西,不仅应当搞清楚他的长度,还应该搞清楚他的单位,那么这里的30,20,50,80的单位是什么呢?很多人会说,是像素!这个答案是对的,但是又不全对。事实上,Windows默认的映射方式(Mapping
Mode,简称就是MM)是MM_TEXT,在MM_TEXT映射方式(TEXT实际上跟文字没有多大关系,是这种映射方式下的坐标方向,从左到右,从上到下,跟文字阅读方式一样)下,这个单位确实是像素。实际上,逻辑坐标和设备坐标的区别就在于他们的单位不一样!

Windows中包括以下3种设备坐标,以满足各种不同需要:

 

下面我们拿出一个公式

(1)客户区域坐标,包括应用程序的客户区域,客户区域的左上角为(0,0)。

GetWindowRect()
得到的是在屏幕坐标系下的RECT(即以屏幕左上角为原点)
GetClientRect()
得到的是在客户区坐标系下的RECT(即以所在窗口左上角为原点,去掉了标题栏计算,仅仅是个大小,返回值的左上角永远为0,0) 
  
CRect rect;

         
 图片 1

(2)屏幕坐标,包括整个屏幕,屏幕的左上角为(0,0)。屏幕坐标用在WM_MOVE消息中(对于非子窗口)以及下面的Windows函数中:CreateWindow和MoveWindow(都对于非子窗口)、GetMessage、GetCursorPos、GetWindowRect、WindowFromPoint和SetBrushOrg中。用函数ClientToScreen和ScreenToClient可以将客户区域坐标转换成屏幕区域坐标,或反之。

GetWindowRect(&rect);

要讲上面的公式,就要先说一下视口(Viewport 台湾译作视埠)和窗口(Window
台湾译作视窗)

(3)全窗口坐标,包括一个程序的整个窗口,包括标题条、菜单、滚动条和窗口框,窗口的左上角为(0,0)。使用GetWindowDC得到的窗口设备环境,可以将逻辑单位转换成窗口坐标。

ScreentoClient(&rect);

首先,不要被这两个名字迷惑了,这两个坐标是跟映射有关的,跟屏幕坐标系,窗口坐标系,客户端坐标系是相对独立的两个知识。

3.逻辑坐标与设备坐标的转换方式

不等同于

其实公式拿出来,学数学的小伙伴是不是就懂了大半了,这个公式非常重要,理解了这个公式,后面的很多东西就能理解,首先,公式中的Window,WinOrg,WinExt,就是带了Win的东西,就是使用的逻辑坐标的值,就跟GDI函数中的一样,逻辑坐标的单位可能是像素(MM_TEXT映射)、毫米(单位是0.1mm,在MM_LOMETRIC映射下)等等等等(看下图).

映射方式定义了Windows如何将GDI函数中指定的逻辑坐标映射为设备坐标。要继续讨论映射方式我们要介绍Windows有关映射模式的一些术语:我们将逻辑坐标所在的坐标系称为”窗口”,将设备坐标所在的坐标系称为”视口”。

CRect rect;

图片 2

“窗口”依赖于逻辑坐标,可以是像素点、毫米或程序员想要的其他尺度。

GetClient(&rect);
举个例如:有个单文档程序

就是说我们在调用Win32函数绘图的时候,要知道自己使用的单位(根据映射模式确定的)。因为绘图函数里的数值,使用的就是这些单位,虽然默认的MM_TEXT映射模式使用的单位就是像素,但是很多时候其他单位也很有用,比如你要做一个屏幕尺子的时候,你要用尺子量一下物体有几厘米。尺子上的刻度就可以用其他的映射模式来画。但是屏幕在显示的时候却不能只知道逻辑坐标几厘米啊,屏幕得知道具体的像素位置才行啊!那这个时候,就需要用到上面的公式转换了。讲到这里,公式里的ViewExt/WinExt是什么意思就很明显了。那就是在当前逻辑坐标系下(比如几厘米,打比方哈),实际上是上面映射模式表格里的单位)对应的设备坐标应该是多少个像素!这样转换过后,得到实际的Viewport,就是该逻辑点在屏幕上的位置。

“视口”依赖于设备坐标(像素点)。通常,视口和客户区域等同。但是,如果程序员用GetWindowDC或CreateDC获取了一个设备环境,则视口也可以指全窗口坐标或屏幕坐标。点(0,0)是客户区域的左上角。x的值向右增加,y的值向上增加。

CRect rect;

所以说视口和窗口实际上是表示的同一块区域,只不过是因为单位和原点的不同,需要进行映射,逻辑单位就是窗口,就是Window,就是像素,毫米,英寸,就是给人用的单位,就是设备无关的单位,设备单位就是视口,就是Viewport,就只能是像素,就是给设备用的单位,确定的一厘米,在不同的设备上的像素数可能会有区别,所以是设备相关的单位。

对于所有映射模式,Windows都用下面两个公式将窗口坐标转换成视口坐标:

View调用GetWindwoRect(&rect),
得到的坐标是:左上角(33,99),右下角(1040,524),这是View相对于屏幕的坐标,当调用ScreenToClient(&rect),rect变成为:左上角(-2,-2),右上角(1007,423),-2,-2是client和window之间的间隔差–border。

 

  1. xViewport = (xWindow – xWinOrg) * (xViewExt / xWinExt) + xViewOrg
  2. yViewport = (yWindow – yWinOrg) * (yViewExt / yWinExt) + yViewOrg

rect =
CRect(0,0,1,1),当调用ClientToScreen(&rect),rect变成为:左上角(35,101),右上角(36,102),这是也有2个单位的border的作用。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website