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_ ...
随机推荐
- ffmpeg的centos、msys2、msvc编译
msys2 和 centos https://ffmpeg.org/download.html https://ffmpeg.zeranoe.com/builds/ Windows MSYS2准备 1 ...
- JAVA定时任务Timer
故事起因 因业务需要,写了一个定时任务Timer,任务将在每天的凌晨2点执行,代码顺利码完,一切就绪,开始测试.运行程序,为了节省时间,将系统时间调整为第二天凌晨1点59分,看着秒针滴答滴答的转动,期 ...
- Mac安装Virtualenv & PyCharm使用Virtualenv
我们使用Python开发的时候,每个项目可能需要不同的Python版本,这时候我们就需要Virtualenv配置虚拟环境. Mac安装Virtualenv sudo pip install virtu ...
- Myeclipse快捷键的设置以及默认的编码格式
设置默认的编码格式
- 【tensorflow:Google】三、tensorflow入门
[一]计算图模型 节点是计算,边是数据流, a = tf.constant( [1., 2.] )定义的是节点,节点有属性 a.graph 取得默认计算图 g1 = tf.get_default_gr ...
- get传输时,会将加号+ 转换为空格
解决办法: 前端: 替换加号为 ‘%2B’, 后端: 直接接收即可.
- js的搜索框
第一种 单独一个form表单提交 <div class="hc-prm-search search flr"> <form action="/user ...
- Oracle Sql Developer 连接oracle
PL/Sql 初次使用需要配置文件内容,对于我这种Oracle新手来说各种配置有点凌乱,所以果断选择Sql Developer. 选择它是因为初次使用的时候它不用想PL/Sql那样配置文件,而只需要添 ...
- 转载 eclipse中的include设置
备注:在10.1版的niosii使用的eclipse中设置的方法是右键->properties->c/c++general->path and symbols->include ...
- Nginx 反向代理与负载均衡详解
序言 Nginx的代理功能与负载均衡功能是最常被用到的,关于nginx的基本语法常识与配置已在Nginx 配置详解中有说明,这篇就开门见山,先描述一些关于代理功能的配置,再说明负载均衡详细. Ngin ...