在用与设备环境(DC)有关的类时,经常对窗口坐标和视口坐标之间的关系有点搞不清楚,在网上查了一下,发现下面这篇文章挺有用的
(http://embeded-life.blog.163.com/blog/static/105932519200942511331799/),这里贴出来分享一下。
另外参考http://www.360doc.com/content/10/0803/10/1066008_43320327.shtml和百度文库文章(http://wenku.baidu.com/view/1e1409284b73f242336c5f6c.html?from=rec&pos=1&weight=3&lastweight=1&count=5,这篇文章用图形化的方法直观而深入浅出地介绍了窗口与视口的映射关系,让人茅塞顿开)
CDC::SetWindowOrg
CPoint SetWindowOrg(int x ,int y );
CPoint SetWindowOrg(POINT point )
返回值:CPoint对象,是窗口初始位置的前一次取值(逻辑单位)。
参数: x 指定窗口初始位置的X逻辑坐标。
y 指定视图端口初始位置的Y逻辑坐标。
point 指定窗口初始位置。其值必须在设备坐标系统范围内。可以为该参数传递POINT结构或CPoint对象。
说明:
设置设备上下文的窗口初始位置。它和设备上下文窗口一起说明了GDI如何将逻辑坐标中的点映射到实际设备坐标中。换言之,它们说明了GDI如何将逻辑坐标转换为设备坐标。窗口初始位置表明在设备坐标系统中的点,GDI将视图端口初始位置与该点映射。窗口初始位置是由SetWindowOrg成员函数在逻辑坐标系统中指定的。GDI在映射其它点时遵从同样的过程,这需要窗口初始位置与视图端口初始位置的映射。例如,所有以窗口初始位置为中心的圆周上的点同样是以视图端口初始位置为中心的圆周上的点。同样地,通过窗口初始位置的直线上的所有点也将形成一条通过视图端口初始位置的直线。
在CMyStatic::OnPaint() 函数中测试
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CRect rect;
GetClientRect(&rect);//取得控件大小
dc.SetMapMode(MM_ANISOTROPIC);//设置成可以改变作标轴方向和比例的,但下面没改变比例
1.先讨论X轴正向向左,Y轴正向向下的情况,即默认的状态
dc.SetWindowExt(100, 100);//逻辑上是长100个单位,宽100个单位
dc.SetViewportExt(200,200);//设备是长200像素,宽200像素
即一个逻辑单位 = 200 / 100 = 2个像素
如果上面第一行改成dc.SetWindowExt(100, 200);//那么,一个逻辑长单位就是2像素,逻辑高单位就是1个像素
同样的dc.Rectangle(0, 0, 10, 10);第一个就是正方形,而第二个就是长是宽2倍的长方形
2.现在谈谈关于设备原点和逻辑原点的问题。
我的理解是,设备原点永远在左上角,也就是最初的(0,0)的位置
逻辑原点是可以移动的,Y轴的正向是向上还是向下是可以改变的
现在考虑Y轴向下为正向时,逻辑原点的移动问题。
dc.SetWindowOrg(0, 0);//这个可省略
dc.SetViewportOrg(rect.right, rect.bottom);
这个就是把设备的(rect.right, rect.bottom)映射为逻辑原点(0, 0)
那就就相当于,把逻辑原点从原来设备原点(0,0)平移到了(rect.right, rect.bottom)
即整个作标系,向右平移了rect.right,向下平移了rect.bottom.
dc.SetViewportOrg(0, 0);//这个可省略
dc.SetWindowOrg(rect.right, rect.bottom);
这个是把逻辑的(rect.right, rect.bottom)映射为设备的(0, 0);也可以说是把设备的(0, 0)映射为逻辑的(rect.right, rect.bottom)
可以想像,设备原点(0, 0)永远在左上角,由于Y轴是向下为正,X轴向右为正,那逻辑原点就被向上移动了rect.bottom,向左移动了rect.right,这样,刚好逻辑坐标(rect.right, rect.bottom)落在了右上角的位置。
然而dc.Rectangle(0, 0, rect.right, rect.bottom);画出的长方形,就正好完全落在窗口这外了
要想和dc.SetViewportOrg(rect.right, rect.bottom);达到相同的效果,
必须要把逻辑原点向右和向下移动,也就是,移动动后的逻辑坐标(-rect.right, -rect.bottom);落在设备原点上,即把逻辑的(-rect.right, -rect.bottom)映射为设备的(0, 0);
dc.SetWindowOrg(-rect.right, -rect.bottom);
如果SetWindowOrg和SetViewportOrg同时使用,例如
dc.SetWindowOrg(x, y);
dc.SetViewportOrg(x1, y1);//不推荐同时用,因为通常会把自己给搞糊涂
即把逻辑坐标(x, y)映射为设备坐标(x1, y1),就相当于把逻辑原点向右移(x1 - x2), 向下移(y1 - y2)
综上所述,X轴正向向右,Y轴正向向下的情况下,
dc.SetWindowOrg(-rect.right, -rect.bottom);等价于dc.SetViewportOrg(rect.right, rect.bottom);
而dc.SetViewportOrg(rect.right, rect.bottom);和dc.SetViewportOrg(rect.right, rect.bottom);刚互补,同时运行则效果抵消。
3.现在讨论X轴正向向左,Y轴正向向上的情况
dc.SetWindowExt(100, -100);//逻辑上是长100个单位,宽100个单位
dc.SetViewportExt(200,200);//设备是长200像素,宽200像素
或
dc.SetWindowExt(100, 100);//逻辑上是长100个单位,宽100个单位
dc.SetViewportExt(200,-200);//设备是长200像素,宽200像素
如果
dc.SetWindowExt(100, -100);//逻辑上是长100个单位,宽100个单位
dc.SetViewportExt(200,-200);//设备是长200像素,宽200像素
则Y轴仍向下
以下面为例:
dc.SetWindowExt(100, -100);//逻辑上是长100个单位,宽100个单位
dc.SetViewportExt(200,200);//设备是长200像素,宽200像素
dc.SetWindowOrg(0, 0);//这个可省略
dc.SetViewportOrg(0, rect.bottom);
设备(0, rect.bottom)映射为逻辑原点(0, 0),即把逻辑原点,移到左下角了
dc.SetViewportOrg(0, 0);//这个可省略
dc.SetWindowOrg(0, rect.bottom);
逻辑(0, rect.bottom);映身为设备原点(0,0),即设备原点(0, 0)映射为逻辑(0, rect.bottom);由于Y轴向上,所以相当于把逻辑原点由设备原点,向下移动rect.bottom,这样,逻辑(0, rect.bottom)刚好落在设备原点(0,0)上。即逻辑原点移动到了左下角。
这时可以发现,dc.SetViewportOrg(0, rect.bottom);与dc.SetWindowOrg(0, rect.bottom);等价了
推广开来,即dc.SetViewportOrg(x, y);与dc.SetWindowOrg(-x, y);等价
总结:保持X轴正向向右不变,当Y轴正向向下时
dc.SetViewportOrg(x, y);与dc.SetWindowOrg(-x, -y);等价
当Y轴正向向上时
dc.SetViewportOrg(x, y);与dc.SetWindowOrg(-x, y);等价
需要强调一点,设备原点永远不会移动,保持在(0,0),设备的坐标系永远不会变,变的是逻辑原点在设备坐标系中的位置和逻辑坐标轴的方向。