Lesson11 图形的保存和重绘主讲人:孙鑫
http://www.sunxin.org
在 OnLButtonUp函数中
0088:4400
② CGraph graph(…);
CGraph的对象在栈中的内存
0088:4400
① CPtrArray m_ptrArray;
③ m_ptrArray.Add(&graph)
http://www.sunxin.org
的对象发生析构,
内存被回收。
如何解决这个问题呢?
http://www.sunxin.org
在 OnLButtonUp函数中
1244:EE00
CGraph的对象在堆中的内存
① CPtrArray m_ptrArray;
② CGraph *pGraph;
0088:4660
1244:EE00
1244:EE00
pGraph的内存
④ m_ptrArray.Add(pGraph);
③ pGraph=new CGraph(…);
http://www.sunxin.org
仍然能索引到 CGraph的对象
pGraph的内存被回收坐标空间
Microsoft Windows下的程序运用坐标空间和转换来对图形输出进行缩放、旋转、平移、斜切和反射。
一个坐标空间是一个平面的空间,通过使用两个相互垂直并且长度相等的轴来定位二维对象。
(0,0) XmaxXmin
Ymax
Ymin
http://www.sunxin.org
坐标空间
Win32应用程序设计接口 (API)使用四种坐标空间:
世界坐标系空间、页面空间、设备空间、和物理设备空间。应用程序运用世界坐标系空间对图形输出进行旋转、斜切或者反射。
Win32 API把世界坐标系空间和页面空间称为逻辑空间;最后一种坐标空间 (即物理设备空间 )通常指应用程序窗口的客户区;但是它也包括整个桌面、
完整的窗口 (包括框架、标题栏和菜单栏 )或打印机的一页或绘图仪的一页纸。物理设备的尺寸随显示器、打印机或绘图仪所设置的尺寸而变化。

http://www.sunxin.org
转换
如要在物理设备上绘制输出,Windows把一个矩形区域从一个坐标空间拷贝到 (或映射到 )另一个坐标空间,直至最终完整的输出呈现在物理设备上(通常是屏幕或打印机) 。
如果该应用程序调用了 SetWorldTransform函数,那么映射就从应用程序的世界坐标系空间开始;否则,映射在页面空间中进行。在 Windows把矩形区域的每一点从一个空间拷贝到另一个空间时,它采用了一种被称作转换的算法,
转换是把对象从一个坐标空间拷贝到另一个坐标空间时改变 (或转变 )这一对象的大小、方位、和形态,尽管转换把对象看成一个整体,但它也作用于对象中的每一点或每条线。
http://www.sunxin.org
转换
Ymax
Ymin
世界坐标系空间下图是运用 SetWorldTransform函数而进行的一个典型转换。
Ymax
Ymin
页面空间
Ymin
Ymax
设备空间 物理设备
http://www.sunxin.org
页面空间到设备空间的转换
页面空间到设备空间的转换是原 Windows接口的一部分。这种转换确定与一特定设备描述表相关的所有图形输出的映射方式。
所谓映射方式是指确定用于绘图操作的单位大小的一种量度转换。映射方式是一种影响几乎任何客户区绘图的设备环境属性。
另外还有四种设备环境属性:窗口原点、
视口原点、窗口范围和视口范围,这四种属性与映射方式密切相关。
http://www.sunxin.org
页面空间到设备空间的转换
页面空间到设备空间的转换所用的是两个矩形的宽与高的比率,其中页面空间中的矩形被称为窗口,设备空间中的矩形被称为视口,Windows把窗口原点映射到视口原点,把窗口范围映射到视口范围,就完成了这种转换,如下图所示:
窗口原点视口原点窗口视口页面空间设备空间
http://www.sunxin.org
设备空间到物理空间的转换
设备空间到物理空间的转换有几个独特之处:它只限于平移,并由 Windows的窗口管理部分控制,这种转换的唯一用途是确保设备空间的原点被映射到物理设备上的适当点上。没有函数能设置这种转换,也没有函数可以获取有关数据。
http://www.sunxin.org
默认转换
一旦应用程序建立了设备描述表,并立即开始调用 GDI绘图或输出函数,则运用默认页面空间到设备空间的转换和设备空间到客户区的转 换 (在应用程序调用 SetWorldTransform函数之前,不会出现世界坐标空间到页面空间的转换 )。
默认页面空间到设备空间的转换结果是一对一的映射;即页面空间上给出的一点映射到设备空间的一个点。正如前文讲到的,这种转换没有以矩阵指定,而是通过把视口宽除以窗口宽,把视口高除以窗口高 而得出的。在默认的情况下,视口尺寸为 1x1个象素,窗口尺寸为 1x1
页单位。
设备空间到物理设备 (客户区、桌面或打印机 )的转换结果总是一对一的;即设备空间的一个单位总是与客户区、桌面、或打印机上的一个单位相对应。这一转换的唯一用途是平移。无论窗口移到桌面的什么 位置,它永远确保输出能够正确无误地出现在窗口上。
默认转换的一个独特之处是设备空间和应用程序窗口的 y轴方向。在默认的状态下,y轴正向朝下,负 y方向朝上。
http://www.sunxin.org
逻辑坐标和设备坐标
几乎在所有的 GDI函数中使用的坐标值都是采用的逻辑单位。 Windows必须将逻辑单位转换为,设备单位,,即像素。这种转换是由映射方式、窗口和视口的原点以及窗口和视口的范围所控制的。
Windows对所有的消息 (如 WM_SIZE,WM_MOUSEMOVE、
WM_LBUTTONDOWN,WM_LBUTTONUP),所有的非
GDI函数和一些 GDI函数 (例如 GetDeviceCaps函数 ),永远使用设备坐标。
,窗口,是基于逻辑坐标的,逻辑坐标可以是象素、毫米、
英寸等单位;,视口,是基于设备坐标 (象素 )的。通常,
视口和客户区是相同的。
缺省的映射模式为 MM_TEXT。在这种映射模式下,逻辑单位和设备单位相同。
http://www.sunxin.org
逻辑坐标和设备坐标的相互转换
窗口 (逻辑 )坐标转换为视口 (设备 )坐标的两个公式:
xViewport=(xWindow-xWinOrg)* +xViewOrg
yViewport=(yWindow-yWinOrg)* +yViewOrg
视口 (设备 )坐标转换为窗口 (逻辑 )坐标的两个公式:
xWindow=(xViewPort-xViewOrg)* +xWinOrg
yWindow=(yViewPort-yViewOrg)* +yWinOrg
xViewExt
xWinExt
yViewExt
yWinExt
xWinExt
xViewExt
yWinExt
yViewExt
http://www.sunxin.org
在 MM_TEXT映射方式下逻辑坐标和设备坐标的相互转换
窗口 (逻辑 )坐标转换为视口 (设备 )坐标的两个公式:
xViewport = xWindow-xWinOrg+xViewOrg
yViewport = yWindow-yWinOrg+yViewOrg
视口 (设备 )坐标转换为窗口 (逻辑 )坐标的两个公式:
xWindow = xViewport-xViewOrg+xWinOrg
yWindow = yViewport-yViewOrg+yWinOrg
http://www.sunxin.org
视口和窗口原点的改变
CDC中提供了两个成员函数函数 SetViewportOrg和
SetWindowOrg,用来改变视口和窗口的原点。
如果将视口原点设置为 (xViewOrg,yViewOrg),则逻辑点 (0,0)就会被映射为设备点
(xViewOrg,yViewOrg)。如果将窗口原点改变为
(xWinOrg,yWinOrg),则逻辑点 (xWinOrg,yWinOrg)
将会被映射为设备点 (0,0),即左上角。
不管对窗口和视口原点作什么改变,设备点 (0,0)
始终是客户区的左上角。
http://www.sunxin.org
关于图形错位的说明
当我们在窗口中点击鼠标左键的时候,得到的是设备坐标
(680,390),在 MM_TEXT的映射模式下,逻辑坐标和设备坐标是相等的,所以我们利用集合类保存的这个点的坐标是以象素为单位,坐标值为 (680,390)。在调用 OnDraw函数前,在 OnPaint函数中调用了 OnPrepareDC函数,调整了显示上下文的属性,将视口的原点设置为了 (0,-150),这样的话,窗口的原点,也就是逻辑坐标 (0,0)将被映射为设备坐标 (0,-150),在画线的时候,因为 GDI的函数使用的是逻辑坐标,而图形在显示的时候,Windows需要将逻辑坐标转化为设备坐标,因此,原先保存的坐标点 (680,390)(在
GDI函数中,作为逻辑坐标使用),根据转换公式
xViewport = xWindow-xWinOrg+xViewOrg 和 yViewport =
yWindow-yWinOrg+yViewOrg,得到设备点的 x坐标为 680-
0+0=680,设备点的 y坐标为 390-0+(-150)=240,于是我们看到图形在原先显示地方的上方出现了。
http://www.sunxin.org
关于解决方法的说明
首先我们在绘制图形之后,在保存坐标点之前,调用
OnPrepareDC函数,调整显示上下文的属性,将视口的原点设置为 (0,-150),这样的话,窗口的原点,也就是逻辑坐标 (0,0)将被映射为设备坐标 (0,-150),然后我们调用 DPtoLP
函数将设备坐标 (680,390)转换为逻辑坐标,根据设备坐标转换为逻辑坐标的公式:
xWindow = xViewport-xViewOrg+xWinOrg,
yWindow = yViewport-yViewOrg+yWinOrg,得到逻辑点的 x
坐标为 680-0+0=680,y坐标为 390-(-150)+0=540,将逻辑坐标 (680,540)保存起来,在窗口重绘时,会先调用
OnPrepareDC函数,调整显示上下文的属性,将视口的原点设置为了 (0,-150),然后 GDI函数用逻辑坐标点 (680,540)
绘制图形,被 Windows转换为设备坐标点 (680,390),和原先显示图形时的设备点是一样的,当然图形就还在原先的地方显示出来。
http://www.sunxin.org
OnPrepareDC会随时根据滚动窗口的位置来调整视口的原点。
http://www.sunxin.org
CreateCompatibleBitmap返回的位图对象只包含相应设备描述表中的位图的位图信息头,不包含颜色表和象素数据块。因此,
选入该位图对象的设备描述表不能像选入普通位图对象的设备描述表一样应用,必须在 SelectObject函数之后,调用 BitBlt将原始设备描述表的颜色表及象素数据块拷贝到兼容设备描述表。
http://www.sunxin.org