设备坐标(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的更多相关文章

  1. GDI编程

    图形设备接口(GDI)是一个可执行程序,它接受Windows应用程序的绘图请求(表现为GDI函数调用),并将它们传给相应的设备驱动程序,完成特定于硬件的输出,象打印机输出和屏幕输出.GDI负责Wind ...

  2. GDI编程小结

    图形设备接口(GDI)是一个可运行程序,它接受Windows应用程序的画图请求(表现为GDI函数调用),并将它们传给对应的设备驱动程序,完毕特定于硬件的输出,象打印机输出和屏幕输出.GDI负责Wind ...

  3. VC++学习之GDI概述

    VC++学习之GDI概述 图形设备接口(GDI)是一个可执行程序,它接受Windows应用程序的绘图请求(表现为GDI函数调用),并将它们传给相应的设备驱动程序,完成特定于硬件的输出,象打印机输出和屏 ...

  4. VC实现卡拉OK字幕叠加

    一. GDI编程基础 字幕叠加,应当是属于图形.图像处理的范畴.在Windows平台上,图形.图像处理的方法当然首选GDI(Graphics Device Interface,图形设备接口).GDI是 ...

  5. MFC绘图基础

    ·MFC中三种坐标系统: 1.屏幕坐标系 坐标原点位于屏幕左上角 2.(非客户区)窗口坐标系 坐标原点位于窗口左上角(包括标题栏) 3.客户区坐标系 坐标原点位于客户区左上角(不包括标题栏) ·坐标系 ...

  6. TrMemo控件

    unit TrMemo; {$R-} interface uses Windows, Messages, Controls, StdCtrls, Classes; const TMWM__Specia ...

  7. GetWindowRect、GetClientRect、ScreenToClient与ClientToScreen

    GetWindowRect是取得窗口在屏幕坐标系下的RECT坐标(包括客户区和非客户区),这样可以得到窗口的大小和相对屏幕左上角(0,0)的位置. GetClientRect取得窗口客户区(不包括非客 ...

  8. ClientToScreen 和ScreenToClient 用法

    ClientToScreen( )是把窗口坐标转换为屏幕坐标 ScreenToClient( )是把屏幕坐标转换为窗口坐标 屏幕坐标是相对于屏幕左上角的,而窗口坐标是相对于窗口用户区左上角的 VC下, ...

  9. 坐标的相对转换ClientToScreen与ScreenToClient

    假如一个有一个TEdit的实例edt_Position,edt_Position所在容器有好几层,所在的窗体为frmMain.现要弹出一个FORM,FORM的容器为frmMain,弹出的位置在edt_ ...

随机推荐

  1. 三十八 Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)介绍以及安装

    elasticsearch(搜索引擎)介绍 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticse ...

  2. Python中的import,from...import以及模块、包、库的概念

    首先,说明一下,我使用的是python3.6.3win32版本,使用的IDE是pycharm2017社区免费版. 刚开始接触python编程不久,有很多概念都不是特别清楚,但是我觉得既然选择,尽自己最 ...

  3. 【.Net】Socket小示例

    引言 项目中用到了Socket,这里做个控制台小示例记录一下. Client 客户端的Receive用了异步方法,保持长连接,可以随时发送消息和响应服务端的消息,如下 static string Cl ...

  4. 几种常见排序算法的C++描述

    基本的排序算法有如下特点: 1.几种容易的算法都是以O(N2)排序的 2.Shell排序编程简单,其也是以O(N2)排序的,在实践中用的很多 3.复杂的排序算法往往都是按照O(NlogN)尽心排序的 ...

  5. AS3中以post和get方式提交数据

    这里主要介绍在as3中用URLRequest对像来post或get数据到服务器. post用于大数据量的提交,get用于小数据量的提交. as3中提交数据: POST方式: 1.新建一个test.fl ...

  6. Leetcode 1013. Partition Array Into Three Parts With Equal Sum

    简单题,暴力找出来就行. class Solution: def canThreePartsEqualSum(self, A: List[int]) -> bool: s = sum(A) if ...

  7. beego配置文件

    关于App配置: #App配置 for Api AppName = ApiService RunMode = dev RouterCaseSensitive = true ServerName = A ...

  8. Spring中的c3p0配置

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/51162560 今天,我们就来详细谈谈Spring中的c3p0配置问题,好了,不耽搁 ...

  9. LG5055 【模板】可持久化文艺平衡树

    题意 您需要写一种数据结构,来维护一个序列,其中需要提供以下操作(对于各个以往的历史版本): 在第 pp 个数后插入数 xx . 删除第 pp 个数. 翻转区间 [l,r][l,r],例如原序列是 { ...

  10. angular中的 input select 值绑定无效,以及多出一个空白选项问题

    问题: <!-- 问题标签 --> <select ng-model="sortType"> <option value="1"& ...