DPtoLP/LPtoDP 和 ScreenToClient/ClientToScreen
设备坐标(Device Coordinate)又称为物理坐标(Physical Coordinate),是指输出设备上的坐标。通常将屏幕上的设备坐标称为屏幕坐标。设备坐标用对象距离窗口左上角的水平距离和垂直距离来指定对象的位置,是以像素为单位来表示的,设备坐标的X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。
逻辑坐标(Logical Coordinate)是系统用作记录的坐标。在缺省的模式(MM_TEXT)下,逻辑坐标的方向和单位与设备坐标的方向和单位相同,也是以像素为单位来表示的,X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。逻辑坐标和设备坐标即使在缺省模式下其数值也未必一致,除了在以下两种情况下:
1. 窗口为非滚动窗口
2. 窗口为滚动窗口,但垂直滚动条位于滚动边框的最上端,水平滚动条位于最左端,但如果移动了滚动条这两种坐标就不一致了。
在VC中鼠标坐标的坐标位置用设备坐标表示,但所有GDI绘图都用逻坐标表示,所以用鼠标绘图时,那么必须将设备坐标转换为逻辑坐标,可以使用CDC 函数DPtoLP()将设备坐标转化为逻辑坐标,同样可以用LPtoDP()将逻辑坐标转化为设备坐标。
ScreenToClient和ClientToScreen实际上是转换一个参照物的概念,如ie客户区上一个button,相对于ie的坐标是(x, y),ie客户区相对于屏幕原点的坐标是(x0 , y0),那么button的screen坐标就是(x+x0, y+y0) 。ScreenToClient和ClientToScreen都假定坐标是设备坐标。
在EX05C中(EX05C是一个例子程序,只需看函数中的代码即可):
我们现在来看看逻辑坐标和物理坐标是怎么转换的。
void CEX05CView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
// TODO: calculate the total size of this view
CSize sizeTotal(800, 1050);
CSize sizePage(sizeTotal.cx/2, sizeTotal.cy/2);
CSize sizeLine(sizeTotal.cx/50, sizeTotal.cy/50);
SetScrollSizes(MM_LOENGLISH, sizeTotal, sizePage, sizeLine);
}
上面程序中的SetScrollSizes(MM_LOENGLISH, sizeTotal, sizePage, sizeLine);制定了映射模式为
MM_LOENGLISH,整个客户端逻辑区域为800 x 1050逻辑单位(sizeTotal);横向翻页的大小为400逻辑单位,纵向翻页的大小为525逻辑单位(sizePage); 横向一列的大小为800 / 50 = 16逻辑单位,纵向一行的大小为1050 / 50 = 21逻辑单位。
在MM_LOENGLISH映射模式下,每逻辑单位是0.01英寸。
void CEX05CView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CRect rectEllipse(m_pointTopLeft, m_sizeEllipse);
CRgn circle;
CClientDC dc(this);
OnPrepareDC(&dc);
// TRACE("Check Point3: HORZSIZE = %d, VERTSIZE = %d/n",dc.GetDeviceCaps(HORZSIZE), dc.GetDeviceCaps(VERTSIZE));
// HORZSIZE = 320mm, VERTSIZE = 240mm
TRACE("/nBefore LPtoDP:/n");
TRACE("rectEllipse.top = %d, rectEllipse.bottom = %d, rectEllipse.left = %d, rectEllipse.right = %d/n",
rectEllipse.top, rectEllipse.bottom, rectEllipse.left, rectEllipse.right);
dc.LPtoDP(rectEllipse);
TRACE("/nAfter LPtoDP:/n");
TRACE("rectEllipse.top = %d, rectEllipse.bottom = %d, rectEllipse.left = %d, rectEllipse.right = %d/n",
rectEllipse.top, rectEllipse.bottom, rectEllipse.left, rectEllipse.right);
// LPtoDP将rectEllipse从逻辑坐标转换成设备坐标
circle.CreateEllipticRgnIndirect(rectEllipse);
// TRACE("Check Point2: point = (%d, %d)/n", point.x, point.y);
// point为物理设备坐标
if(circle.PtInRegion(point))
{
SetCapture();
// Causes all subsequent mouse input to be sent to the current CWnd object regardless of the
// position of the cursor.
m_bCaptured = TRUE;
CPoint pointTopLeft(m_pointTopLeft);
dc.LPtoDP(&pointTopLeft);
m_sizeOffset = point - pointTopLeft;
// m_sizeOffset, point, pointTopLeft皆为设备坐标
::SetCursor(::LoadCursor(NULL, IDC_CROSS));
}
CScrollView::OnLButtonDown(nFlags, point);
}
我们来看看rectEllipse在语句dc.LPtoDP(rectEllipse);前后的变化:
Before LPtoDP:
EX05C: rectEllipse.top = -219, rectEllipse.bottom = -319, rectEllipse.left = 199, rectEllipse.right = 299
After LPtoDP:
EX05C: rectEllipse.top = 178, rectEllipse.bottom = 259, rectEllipse.left = 162, rectEllipse.right = 243
-219(逻辑坐标)是怎样转换为178(设备坐标)的呢?
设备坐标:屏幕的左上方为(0, 0),屏幕的右边为x坐标的正方向,屏幕的下边为y轴的正方向。
逻辑坐标:屏幕的左上方为(0, 0),右边为x坐标的正方向,对于不同的映射模式,y轴的正方向是不一样的,对
于MM_LOENGLISH而言,向上的方向是正方向,向下的方向是负方向。
在MM_LOENGLISH映射模式下,219个逻辑单位的长度为:
219 * 0.01 = 2.19英寸 = 2.19 * 2.54 = 5.5626 厘米
在上面程序中的CheckPoint3中,我们可以得到屏幕的物理尺寸为:320毫米 x 240毫米,另外补充说明一下,本电脑的屏幕分辨率是1024 * 768。因此,
(5.5626 / 24) * 768 = 178.0032像数
以上就是-219(逻辑坐标)转换为178(设备坐标)的详解。其他几个坐标也是如此转换而来。
现在我们看看卷动右边的滚动条的情况下,坐标是怎样变化的。
先将滚动条往下滚动两行,然后随便将椭圆拖放到一个位置,如下图所示:
TRACE语句的输出结果为:
Before LPtoDP:
EX05C: rectEllipse.top = -469, rectEllipse.bottom = -569, rectEllipse.left = 801, rectEllipse.right = 901
After LPtoDP:
EX05C: rectEllipse.top = 347, rectEllipse.bottom = 428, rectEllipse.left = 651, rectEllipse.right = 732
逻辑坐标-469是怎样转换成设备坐标347的呢?
在不考虑卷动的情况下,逻辑坐标-469应该转换成的设备坐标为:
(469 * 0.01 * 2.54 / 24) * 768 = 381.2032像数 = 381像数。
由于在垂直方向向下滚动了两行即 (1050 / 50) * 2 = 42逻辑单位,转换成设备坐标为:
(42 * 0.0.1 * 2.54 / 24) * 768 = 34.1736像数 = 34像数。
因此,考虑到卷动了2行的情况,逻辑坐标-469转换成设备坐标应该是:
381 - 34 = 347像数
这就是在有卷动的情况下,逻辑坐标和设备坐标转换的详细说明。
DPtoLP/LPtoDP 和 ScreenToClient/ClientToScreen的更多相关文章
- GDI编程
图形设备接口(GDI)是一个可执行程序,它接受Windows应用程序的绘图请求(表现为GDI函数调用),并将它们传给相应的设备驱动程序,完成特定于硬件的输出,象打印机输出和屏幕输出.GDI负责Wind ...
- GDI编程小结
图形设备接口(GDI)是一个可运行程序,它接受Windows应用程序的画图请求(表现为GDI函数调用),并将它们传给对应的设备驱动程序,完毕特定于硬件的输出,象打印机输出和屏幕输出.GDI负责Wind ...
- VC++学习之GDI概述
VC++学习之GDI概述 图形设备接口(GDI)是一个可执行程序,它接受Windows应用程序的绘图请求(表现为GDI函数调用),并将它们传给相应的设备驱动程序,完成特定于硬件的输出,象打印机输出和屏 ...
- VC实现卡拉OK字幕叠加
一. GDI编程基础 字幕叠加,应当是属于图形.图像处理的范畴.在Windows平台上,图形.图像处理的方法当然首选GDI(Graphics Device Interface,图形设备接口).GDI是 ...
- MFC绘图基础
·MFC中三种坐标系统: 1.屏幕坐标系 坐标原点位于屏幕左上角 2.(非客户区)窗口坐标系 坐标原点位于窗口左上角(包括标题栏) 3.客户区坐标系 坐标原点位于客户区左上角(不包括标题栏) ·坐标系 ...
- TrMemo控件
unit TrMemo; {$R-} interface uses Windows, Messages, Controls, StdCtrls, Classes; const TMWM__Specia ...
- GetWindowRect、GetClientRect、ScreenToClient与ClientToScreen
GetWindowRect是取得窗口在屏幕坐标系下的RECT坐标(包括客户区和非客户区),这样可以得到窗口的大小和相对屏幕左上角(0,0)的位置. GetClientRect取得窗口客户区(不包括非客 ...
- ClientToScreen 和ScreenToClient 用法
ClientToScreen( )是把窗口坐标转换为屏幕坐标 ScreenToClient( )是把屏幕坐标转换为窗口坐标 屏幕坐标是相对于屏幕左上角的,而窗口坐标是相对于窗口用户区左上角的 VC下, ...
- 坐标的相对转换ClientToScreen与ScreenToClient
假如一个有一个TEdit的实例edt_Position,edt_Position所在容器有好几层,所在的窗体为frmMain.现要弹出一个FORM,FORM的容器为frmMain,弹出的位置在edt_ ...
随机推荐
- LCS 的 NlogN作法
这个算法其实是因为LIS有NlogN的作法,把LCS转化为了LIS来做. 对于序列A{},B{},我们可以记录下来B中的每个元素在A中出现的位置,按顺序保存在一个新序列当中, 如果有多个位置按倒序写, ...
- 图解MySQL 内连接、外连接、左连接、右连接、全连接
用两个表(a_table.b_table),关联字段a_table.a_id和b_table.b_id来演示一下MySQL的内连接.外连接( 左(外)连接.右(外)连接.全(外)连接). MySQL版 ...
- switch遇到0的问题
你是否经常有switch来代替if else?是否因为使用了switch,提高代码的执行效率而庆幸?好吧,你和我一样,但也许你没有遇到下面的问题. 这个小程序,会输出什么呢?会是'00'么? 结果 ...
- 数据库基础 非关系型数据库 MongoDB 和 redis
数据库基础 非关系型数据库 MongoDB 和 redis 1 NoSQL简介 访问量增加,频繁的读写 直接访问(硬盘)物理级别的数据,会很慢 ,关系型数据库的压力会很大 所以,需要内存级的读写操作, ...
- Qt5学习笔记——QRadioButton与QbuttonGroup
[我是小标题:使用QToolButton实现radio button功能.] QRadioButton是什么? 下图是Windows系统中典型的radio button显示效果. QRadio ...
- Tomcat应用服务器被黑客 肉鸡攻击 记录
线上一台应用服务器报警,负载过高,这个就诡异了,因为只是一个普通的服务器,应用使用人员不到10个人,咋会负载高,肯定有问题哪,登陆上去查看, top查看哪个占据的cpu资源比较多 [root@aew0 ...
- 《Drools7.0.0.Final规则引擎教程》第3章 3.1 Hello World 实例
3.1 Hello World 实例 在上一章中介绍了Drools5x版本中规则引擎使用的实例,很明显在Drools7中KnowledgeBase类已经标注为"@Deprecated&quo ...
- WindowManager实现悬浮可拖动效果
现在360手机卫士有个流量统计的效果,开启流量统计后,在桌面上会出现一个显示流量的窗体,在任何界面都可以自由拖动. 模仿这个功能,做了一个统计手机信号强度的Demo, 界面效果如下: 从上面的截图可以 ...
- ng 过滤器
1.ng中自带的过滤器过滤器:实现对数据的筛选.过滤.格式化. 过滤器是一个有返回值的方法. 过滤器语法:{{ expression |过滤器1:'参数' | 过滤器2:'参数' }} | --> ...
- jQuery 绑定事件总结
目前已知有: $("..").bind("事件名",fn); $("parent").on("事件名","se ...