设备坐标(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. C语言之内存四区模型和函数调用模型

      内存四区模型 流程说明1.操作系统把物理硬盘代码load到内存2.操作系统把c代码分成四个区3.操作系统找到main函数入口执行 1.内存四区: 一个由c/C++编译的程序占用的内存分为以下几个部 ...

  2. [转载]Java动态生成word文档(图文并茂)

    很多情况下,软件开发者需要从数据库读取数据,然后将数据动态填充到手工预先准备好的Word模板文档里,这对于大批量生成拥有相同格式排版的正式文件非常有用,这个功能应用PageOffice的基本动态填充功 ...

  3. 【Hive】建表

    1.建表 Create [EXTERNAL] TABLE [IF NOT EXISTS] table_name ( [(col_name data_type [COMMENT col_comment] ...

  4. 【Hive】窗口函数

    我们都知道在sql中有一类函数叫做聚合函数,例如sum().avg().max()等等, 这类函数可以将多行数据按照规则聚集为一行,一般来讲聚集后的行数是要少于聚集前的行数的. 但是有时我们想要既显示 ...

  5. 【python】self用法详解

    在介绍Python的self用法之前,先来介绍下Python中的类和实例我们知道,面向对象最重要的概念就是类(class)和实例(instance). 类是抽象的模板,比如学生这个抽象的事物,可以用一 ...

  6. dataGridView的使用经验

    1.dataGridView是dataGrid的替代品,包含了dataGrid的全部功能. 2.为dataGridView赋值,一般将其数据设置为一个DataTabel.例子如下: DataTable ...

  7. New Concept English three (26)

    34w/m 54words No one can avoid being influenced by advertisements. Much as we may pride ourselves on ...

  8. 15 分钟学会 Eclipse GMF

    背景 坦白说:过去在 Eclipse 里使用 Graphical Editor Framework(GEF)创建图形化编辑器 既慢又痛苦.这个过程包括理解复杂的框架和大量的冗余代码.但也说明 GEF ...

  9. => 应用在js回调函数中

    => 可以简化以前的回调函数的调用,具体来说: 今后,几乎所有的回调函数都可用箭头函数简化 比如: 1. 所有回调函数都可: 去function改=> 2. 如果函数体只有一句话: 可省略 ...

  10. Java8新特性Optional、接口中的默认方法与静态方法

    Optional Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念 ...