【转载】设备坐标(窗口/window)和逻辑坐标(视口/viewport)
一、预备知识
1.窗口是基于逻辑坐标的.
2.视口是基于设备坐标.
3.设备坐标是以像素为单位的,逻辑坐标是以.cm,m,mm,.....
4.系统最后一定要把逻辑坐标变为设备坐标.
5.设备坐标有3种:
a、客户区域坐标,包括应用程序的客户区域,客户区域的左上角为(0, 0)。
b、屏幕坐标,包括整个屏幕,屏幕的左上角为(0, 0)。屏幕坐标用在WM_MOVE消息中(对于非子窗口)以及下面的Windows 函数中:CreateWindow 和MoveWindow(都对于非子窗口)、GetMessage、GetCursorPos、 GetWindowRect、WindowFromPoint 和SetBrushOrg 中。
用函数ClientToScreen 和ScreenToClient可以将客户区域坐标转换成屏幕区域坐标,或反之。
c、全窗口坐标,包括一个程序的整个窗口,包括标题条、菜单、滚动条和窗口框,窗口的左上角为(0,0)。使用GetWindowDC得到的窗口设备环境,可以将逻辑单位转换成窗口”坐标。
要用到两个函数:
CDC::LPtoDP 将逻辑坐标转换为设备坐标
CDC::DPtoLP 将设备坐标转换为逻辑坐标
设备坐标(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);
9 // 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();
25 // 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像数
这就是在有卷动的情况下,逻辑坐标和设备坐标转换的详细说明。
1. void CEX05CView::OnLButtonDown(UINT nFlags, CPoint point)中的point是设备坐标,而非逻辑坐标。如果要该函数中画图,就一定要将point转换成逻辑坐标。
2. InvalidateRect(rectOld, TRUE);中的rectOld是设备坐标,而非逻辑坐标。
3. pDC->Ellipse(CRect(m_pointTopLeft, m_sizeEllipse)); m_pointTopLeft, m_sizeEllipse皆为逻辑坐标。
画图时,皆用逻辑坐标。InvalidateRect时或者判断某点是否位于某个区域之中时皆用设备坐标。
void CEX05CView::OnDraw(CDC* pDC)
{
CEX05CDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here CBrush brushHatch(HS_DIAGCROSS, RGB(, , ));
CPoint point(, ); pDC->LPtoDP(&point);
pDC->SetBrushOrg(point); pDC->SelectObject(&brushHatch);
TRACE("Check Point1: m_pointTopLeft = %d, m_sizeEllipse = %d/n", m_pointTopLeft, m_sizeEllipse);
// m_pointTopLeft, m_sizeEllipse皆为逻辑坐标。画图时,皆用逻辑坐标,InvalidateRect时皆用设备坐标。
pDC->Ellipse(CRect(m_pointTopLeft, m_sizeEllipse));
pDC->SelectStockObject(BLACK_BRUSH);
pDC->Rectangle(CRect(, -, , -));
}
没有上面下划线语句的情况下,椭圆中的填充部分,在屏幕卷动时会不正常(右边滚动条不卷动时是正常的):
有上面下划线语句的情况下,不管是否卷动,椭圆中的填充部分都是正常的。如下图:
本文转自:http://blog.csdn.net/pathuang68/article/details/4219657
【转载】设备坐标(窗口/window)和逻辑坐标(视口/viewport)的更多相关文章
- 移动web开发之视口viewport
× 目录 [1]布局视口 [2]视觉视口 [3]理想视口[4]meta标签[5]总结 前面的话 在CSS标准文档中,视口viewport被称为初始包含块.这个初始包含块是所有CSS百分比宽度推算的根源 ...
- 父窗口window.showModalDialog传值 子窗口window.returnValue返回值
父窗口打开子窗口页面: var fatherWindow = document.all.dealReason;//想传的值 win = window.showModalDialog(strUrl, f ...
- 视口viewport与单位rem的本质
结论: 视口viewport的设置是为了让字的显示在不同的屏幕下保持一致. 单位rem的使用是为了让页面中的布局元素的比例在不同的屏幕下显示的比例保持一致. 现象: 我们看电脑时候的网页的时候的字体大 ...
- 【转载】解决window.showModalDialog 模态窗口中location 打开新窗口问题
来源: <http://bibipear.blog.sohu.com/143449988.html> 在我们的项目中,通常会用到showModalDialog 打开一个模态的子窗口,但是在 ...
- (转)移动端开发总结(一)视口viewport总结
转载链接:移动端开发中,关于适配问题的一点总结(一) 视口 布局视口layout viewport 视觉视口visual viewport 理想视口 缩放 一个重大区别 最小缩放 和最大缩放 分辨率 ...
- 视口viewport
一.viewport 1. 何为视口? 视口是浏览器显示网页的矩形区域. 2. 默认视口:模拟一个大约1000像素宽的视口. 理想视口:因设备.操作系统.浏览器而异,一般而言,手机宽带大约在300-5 ...
- 移动端布局:视口viewport的理解
移动端开发中,有一些基本概念需要理解清楚,才能更好的组织编程逻辑.在刚接触时,移动端视口的缩放和rem单位的缩放搞混淆了,弄得自己很蒙圈.所以仔细总结下自己的理解. 移动端的适配,我理解为两点: 第一 ...
- web 浏览器窗口window 与框架 frameset 的关系
如果页面包含框架,则每个框架都拥有自己的window对象,并且保存在frames集合中,可以通过数字索引(从0开始,从左右到右,从上到下)或者框架名称来访问相对应的window对象. 每个window ...
- 网页打开新窗口——Window.open()详解
转载自:http://blog.csdn.net/business122/article/details/8281142 Window.Open详解 一.window.open()支持环境:JavaS ...
随机推荐
- 转:CSS布局奇淫技巧之-宽度自适应
css这个东西,说难不难,说容易也不容易.我觉得最重要的还是经验的积累,正所谓的不积硅步,无以至千里.这一系列文章讲述几种css特殊布局的实现,也当作为自己做个备忘吧. 首先讲的是三列布局,左右两列宽 ...
- 第五章 Spring3.0 、Hibernate3.3与Struts2的整合 基于Annotation
Annotation的方式是通过注解的方式把Struts2中的Action.Dao层的实现类.Service层的实现类交由Spring管理,不需要在配置文件中进行配置.但为了方便,事务的管理依然使用的 ...
- JAVA集合LIST MAP SET详解
1. 集合框架介绍 我们知道,计算机的优势在于处理大量的数据,在编程开发中,为处理大量的数据,必须具备相应的存储结构,之前学习的数组可以用来存储并处理大量类型相同的数据,但是通过上面的课后练习,会发现 ...
- 面向对象程序设计-C++_课时11new & delete
Dynamic memory allocation new new int; new Stash; new int[10]; new返回这个对象的指针 delete delete p; delete[ ...
- 兔子--Android中的五大布局
LinearLayout:被称为线性布局,分为水平和垂直,设置的垂直或水平的属性值,来排列全部的子元素.全部的子元素都被堆放在其他元素之后,因此一个垂直列表的每一行仅仅会有一个元素,而无论他们有多宽, ...
- USB OTG简单介绍
1 引言 随着USB2.0版本号的公布,USB越来越流行,已经成为一种标准接口.如今,USB支持三种传输速率:低速(1.5Mb/s).全速(12Mb/s)和快速(480Mb/s),四种传输类型:块传输 ...
- 一个SQL update语句
须要每隔一段时间选取最老的商户更新时间戳: update DP_Shop set DP_Shop.LastDate = now() where DP_Shop.ShopId in (select Sh ...
- HTML系列(八):表格
一.基本表格: 表格标记<table>,行标记<tr>,单元格标记<td> 基本语法: <table> <tr> <td>单元格 ...
- 指定hive输出格式
0.11版本以前: sed -e 's/\x01/|/g' file 0.11版本以后: insert overwrite local directory '/opt/aimcpro/libc/tes ...
- VS2013 快捷键 VS RESHARPER 设置
一直用Resharper插件,最近发现Ctrl+E,C快捷见被Resharper的快捷功能吞掉了,折腾了几小时终于找到解决方法了,特记分享之. 采用如下步骤可以让快捷键回到vs2012的默认方式,同时 ...