您的位置:首页 > 其它

关于设备坐标和逻辑坐标在vc中的相关

2010-01-06 17:52 295 查看
一 坐标系统概述
众所周知,VC中的坐标系统分为逻辑坐标和设备坐标,逻辑坐标就是内存中虚拟的坐标,我们可以理解为看不见的,而设备坐标就是跟具体的设备相联系的坐标系统,如:显示器和打印机等。不同的映射模式决定了设备坐标和逻辑坐标之间的转换关系,也就是两种坐标系统在相互转换时,逻辑单位和设备单位之间的某种比例关系。这里需要说明的是:Windows中,只有对需要设备环境句柄作参数的GDI函数,映射方式才会起作用。
对于视口和窗口的概念比较的容易混淆,其实窗口和视口是与映射模式相关联的:映射方式就是用于从"窗口"(逻辑坐标)到"视口"(设备坐标)的映射。"视口"是基于设备坐标的,通常,视口与客户区相同;"窗口"是基于逻辑坐标的,逻辑坐标可以是像素、毫米、英寸等。
下面的公式是将窗口(逻辑)坐标转化为视口(设备)坐标:
 
xViewport = (xWindow - xWinOrg)*xViewExt/xWinExt + xViewOrg
yViewport = (yWindow - yWinOrg)*yViewExt/yWinExt + yViewOrg
(xWindow,yWindow)是待转换的逻辑点,(xViewport,yViewport)是转换后的设备坐标。

设备坐标的视口原点(xViewOrg,yViewOrg)和逻辑坐标的窗口原点(xWinOrg,yWinOrg)默认情况下均被设置成(0,0),但具体情况下可以改变;(xWinExt,yWinExt)是逻辑坐标的窗口范围;(xViewExt,yViewExt)是设备坐标的窗口范围,在多数映射方式下,范围是映射方式所隐含的,不能改变。注意:每个范围自身没有什么意义,但是视口范围和窗口范围的比是逻辑单位转换为设备单位的换算因子。例如,对于MM_LOENGLISH模式,xViewExt/xWinExt 表示每0.01英寸(一个逻辑单位)中水平像素数。
二 映射模式及视图缩放
对于标准的映射模式,这里不作讨论,下面重点的说明MM_ISOTROPIC和MM_ANISOTROPIC两种映射模式,这两种模式用户可以控制逻辑单位和像素之间的比例。两种方式的概念上的差异在于各向同性和各向异性,也就是坐标的建立是否可以定义不同的方向和单位长度。下面举一个视图缩放的例子来具体说明。

 
Void CMyView::OnPrepareDC()
{
CView::OnPrepareDC(pDC,pInfo);
CDC* pDC;
pDC->SetMapMode(MM_ANISOTROPIC);

int xLogPixPerInch = pDC->GetDeviceCaps(LOGPIXELSX);
int yLogPixPerInch = pDC->GetDeviceCaps(LOGPIXELSY);
pDC->SetWindowExt(100,100);
pDC->SetViewportExt(xLogPixPerInch, yLogPixPerInch);
}

我们说过视口的幅度通常是屏幕客户区域的大小,而在上面的代码里我们并没有这样做。这样做可以吗?其实,在MM_TEXT的映射模式下,缺省的窗口范围和视口范围均为(1,1),从中可见,我们要设置的只是xViewExt/xWinExt的比值来决定显示比例的大小,而与xViewExt(视口范围)和xWinExt(窗口范围)的关系不大。GetDeviceCaps(LOGPIXELSX)和GetDeviceCaps(LOGPIXELSY)代表每逻辑英寸对应的水平像素点数;xLogPicPerInch/100代表每0.01逻辑单位对应的水平像素数目;那么100是什么意思呢?这就对应着每逻辑英寸对应的逻辑单位数目,也就是每逻辑单位对应于0.01逻辑英寸,有人会问,这不正是MM_LOENGLISH 方式吗?那么我们作做这样一个测试(见Demo),在OnDraw()中分别设置两种映射模式,看看到底有什么不同。

 
void CTestView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDC->SetMapMode(MM_ANISOTROPIC);
int xLogPixPerInch = pDC->GetDeviceCaps(LOGPIXELSX);
int yLogPixPerInch = pDC->GetDeviceCaps(LOGPIXELSY);
pDC->SetWindowExt(100,100);
pDC->SetViewportExt(xLogPixPerInch, yLogPixPerInch);
pDC->Rectangle(100,100,200,200); //矩形的长度和宽度正好对应于一个逻辑英寸

pDC->SetMapMode(MM_LOENGLISH);
pDC->Rectangle(100,-100,200,-200); //矩形的长度和宽度正好对应于一个实际英寸
}

我们从结果中可以看出,显示在屏幕上时,两个正方形并不能重合,而在打印预览中可以重合。这是什么原因呢?前面我们说过,就是对应于MM_ANISOTROPIC映射方式下,100的含义是"每逻辑单位对应于0.01逻辑英寸",而在MM_LOENGLISH映射模式下,每逻辑单位对应的是0.01实际英寸,逻辑英寸要比实际英寸大一些,注意,这仅仅是对于显示器来说,有逻辑英寸和实际英寸的区别,在对于打印机而言,没有区别!
设置窗口不同的窗口幅度和视口幅度的比值就可以轻松实现缩放。
最后,用一个公式来说明以上论述的内容:

图一

= 每逻辑单位对应的像素数目
= 每0.01逻辑英寸中水平像素的数目
各参数的意义:
GetDeviceCaps(LOGPIXELSX): 每逻辑英寸对应的水平像素点数
100: 每逻辑英寸对应的逻辑单位数
xViewExt: 视口范围
xWinExt: 窗口范围

映射模式是指在实际应用中某些人可能习惯使用某中计量单位(英寸、厘米等),而在Windows中实际处理的是pixel,所以需要进行一些设置,能让系统进行相应的转换。
举个例:

在 PowerBuilder 中使用的 PB units,它对pixel的转换大约是 X 轴(4.57比1),Y轴大约是(4比1),因此在用户在使用PB时是输入的PB units,而系统要将其转换为pixel处理,(在vc 中通常是 LPtoDP完成)

在VC的映射模式中所指的窗口是代表逻辑坐标体系(或逻辑单位)的表示方式
视口是代表物理坐标体系(或设备单位)的表示方式

在大多数的GDI函数处理时,采用的是逻辑坐标,而在Windows内部则采用的是设备单位,因此需要通过设置逻辑坐标与设备单位的关系,让Windows能处理它们的转换关系

你所提到的两本书中都有描述转换关系的公式

SetWindowOrg和SetViewportOrg是用来设置逻辑坐标与设备坐标体系的方向的,在物理坐标体系中X轴向右为增量,Y轴向下为减量,而你可通过设置不同的原点位置来完成方向的转换
详细的方法你可看书中的公式

论非定比例的映射模式:SetMapMode(MM_ANISOTROPIC)

映射模式要用到的四个函数:

SetWindowExt

SetViewportExt

SetWindowOrg

SetViewportOrg

视口中用的是设备坐标,窗口中用的是逻辑坐标。因为这个特点,给人的感觉就是视口是一张真正的存在的纸,而窗口是你看到的纸的那一部分。

对于前两个函数有一种非常好的理解方式,最开始时我就是这么理解的。同样一张纸(比如一张A4纸),然后用两种单位来比划这张纸,例如用英寸和厘米,或是你自己定义一个单位。SetViewportExt说这有一张100*200像素大小的纸,SetWindowExt说“我不用你的单位,我说它是4000*80000”。

后来才发现,这个问题用数学来描述到简洁、明白的多,就是两个坐标系建立映射的过程。

先不管我们看到了什么,就想像同一个空间被两个坐标系表示,一个是Viewport,一个是Window。空间中一个确定点,在Viewport中有个坐标(x0,y0),在Window中也有一个坐标(x1,y1)。你通过设置这两个函数,也就告诉了系统这两个坐标是怎么映射并转化的。

比如在设备坐标中x0=300,y0=200,x1=3000,y1=-200.这样你画图时就相当于是在wx-wy坐标里画。

再说SetWindowOrg,SetViewportOrg

如果只设置SetViewportOrg,对你在wx-wy坐标里画图没有任何影响,只是你移动了视角,视野变了而以。

而SetWindowOrg则是在相对于那张纸平移了自己的坐标系(wx-wy),

表面上看,两个好像没有什么区别,但下面这种情况就显现区别出来了。先将SetViewportExt设为整个窗口大小,SetWindowExt可以随便设。

有一个现像,你只用SetViewportOrg设坐标后(不用SetWindowOrg),改变窗口的大小、形状后,坐标原点离左边窗的距离在像素数上是不变的。

而只用SetWindowOrg设置坐标后,改变窗口后,坐标原点离左边窗的距离是改变的

坐标系可以为我们的绘图带来很大的方便 。下面介绍一下如何在VC中建立我们想要的坐标系。
一 设备坐标和逻辑坐标
  设备坐标(Device Coordinate)又称为物理坐标(Physical Coordinate),是指输出设备上的坐标。通常将屏幕上的设备坐标称为屏幕坐标。设备坐标用对象距离窗口左上角的水平距离和垂直距离来指定对象的位置,是以像素为单位来表示的,设备坐标的X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。
  逻辑坐标(Logical Coordinate)是系统用作记录的坐标。在缺省的模式(MM_TEXT)下,逻辑坐标的方向和单位与设备坐标的方向和单位相同,也是以像素为单位来表示的,X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。逻辑坐标和设备坐标即使在缺省模式下其数值也未必一致,除了在以下两种情况下:
  1. 窗口为非滚动窗口
  2. 窗口为滚动窗口,但垂直滚动条位于滚动边框的最上端,水平滚动条位于最左端,但如果移动了滚动条这两种坐标就不一致了。
  在VC中鼠标坐标的坐标位置用设备坐标表示,但所有GDI绘图都用逻辑坐标表示,所以用鼠标绘图时,那么必须将设备坐标转换为逻辑坐标,可以使用CDC 函数DptoLP()将设备坐标转化为逻辑坐标,同样可以用LptoDP()将逻辑坐标转化为设备坐标。

  二 坐标模式
  为了在不同的领域使用逻辑坐标,Windows提供了以下8种坐标模式:
  分别为MM_TEXT、MM_HIENGLISH、MM_LOENGLISH、MM_HIMETRIC、MM_LOMETRIC、MM_TWIPS、MM_ANISOTROPIC和MM_ISOTROPIC。

  三 实例解析
  (一) 建立以左上角为原点,X轴和Y轴为1000的坐标,如下图
  我们可以用以下代码:
  void CTtView::OnDraw(CDC* pDC)
   {
    CTtDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    CRect rect;
    GetClientRect(&rect);

    pDC->SetMapMode(MM_ANISOTROPIC);
    pDC->SetViewportOrg(0,0);
    pDC->SetViewportExt(rect.right,rect.bottom);

    pDC->SetWindowOrg(0,0);
    pDC->SetWindowExt(1000,1000);

    pDC->MoveTo(50,50);
    pDC->LineTo(50,950);
    pDC->LineTo(950,950);
    pDC->LineTo(50,50);
   }
  代码分析:
  1. GetClientRect(&rect); 取得客户区矩形区域,将其存放在rect中
  2. 用pDC->SetMapMode(MM_ANISOTROPIC); 设置映射模式
  3. 通过pDC->SetViewportOrg(0,0);设置逻辑坐标的原点。
  4. 通过pDC->SetViewportExt(rect.right,rect.bottom);和
pDC->SetWindowExt(1000,1000);来确定逻辑坐标下和设备坐标下的尺寸对应关系
  5. 在MM_ANISOTROPIC模式下,X轴单位和Y轴单位可以不相同
  6. 坐标方向的确定方法是如果逻辑窗范围和视口范围符号相同,则逻辑坐标的方向和视口的方向相同,即X轴向右为正,Y轴向下为正。
  7. 如果将显示模式改为MM_ISOTROPIC,那么X轴单位和Y轴单位一定相同,感兴趣的读者可以自己使一下。
  (二) 建立以视窗中心为原点的坐标,如下:
  用如下代码:
  void CTtView::OnDraw(CDC* pDC)
   {
    CTtDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    CRect rect;
    GetClientRect(&rect);

    pDC->SetMapMode(MM_ANISOTROPIC);
    pDC->SetViewportOrg(rect.right/2,rect.bottom/2);
    pDC->SetViewportExt(rect.right,rect.bottom);

    pDC->SetWindowOrg(0,0);
    pDC->SetWindowExt(1000,-1000);

    pDC->MoveTo(150,150);
    pDC->LineTo(-150,-200);
    pDC->LineTo(150,-150);
    pDC->LineTo(150,150);
   }
 代码分析:
  1. 用 pDC->SetViewportOrg(rect.right/2,rect.bottom/2); 设置视口的原点。
2. 用pDC->SetViewportExt(rect.right,rect.bottom);和pDC->SetWindowExt(1000,-1000);来确定设备坐标和逻辑坐标的单位对应关系。
  3. 因为逻辑窗范围和视口范围的符号不一致,纵坐标取反,所以Y轴向上为正。

MM_LOENGLISH、MM_HIENGLISH、MM_LOMETRIC、MM_HIMETRIC、MM_TWIPS这一组是Windows提供的重要的固定比例映射模式。

它们都是x值向右方向递增,y值向下递减,并且无法改变。它们之间的区别在于比例因子见下:(我想书上P53页肯定是印错了,因为通过程序实验x值向右方向也是递增的)

MM_LOENGLISH 0.01英寸
MM_HIENGLISH 0.001英寸
MM_LOMETRIC 0.1mm
MM_HIMETRIC 0.01mm
MM_TWIPS 1/1440英寸 //应用于打印机,一个twip相当于1/20磅,一磅又相当于1/72英寸。

在VC里, 准确的说是在Windows编程中,映射模式有:MM_TEXT,(设备坐标)
MM_HIMETRIC,(逻辑坐标)
MM_ISOTROPIC,MM_ANISOTROPIC,.....在的开发环境下,(Windows 默认的模式是MM_TEXT),我们可以通过SetMapMode()来改变当是的映射模式~
MM_TEXT :X向右方向递增加,Y向下方向递增加,我们可以通过SetViewportOrg()和SetWindowOrg()来改变坐标原点的位置.
void CInSide_VCView::OnDraw(CDC* pDC)
{
pDC->TextOut (0,0,"TEST");
pDC->SelectStockObject (GRAY_BRUSH);
pDC->SetMapMode (MM_TEXT);
//pDC->SetWindowOrg (100,100); 交替使用这两句看有什么现象
//pDC->SetViewportOrg(CPoint(100,100));
pDC->Ellipse (CRect(0,0,300,300));
pDC->TextOut (0,0,"22");

}
固定比例模式:X向右方向递减(我认为是增加),Y向下方向递减,MM_LOENGLISH(0.01英寸) MM_HIENGLISH(0.001英寸) MM_LOMETRIC(0.1mm) MM_HIMETRIC(0.01mm) MM_TWIPS(1/1400英寸,一般用于打印机)
可变比例模式:M_ISOTROPIC(1:1),MM_ANISOTROPIC(可为任意比例),用SetWindowsExt() 和SetViewportExt()来设定比例,
void CInSide_VCView::OnDraw(CDC* pDC)
{
CRect rectClient;
GetClientRect(rectClient);
pDC->SelectStockObject (m_nColor);
pDC->SetMapMode (MM_ANISOTROPIC);
pDC->SetWindowExt (1000,1000);
pDC->SetViewportExt (rectClient.right,rectClient.bottom );
pDC->SetViewportOrg (rectClient.right/2,rectClient.bottom/2);
pDC->Ellipse (CRect(-500,-500,500,500));
}
物理坐标,即我们现实中的尺寸,屏幕中一英寸是现实中的12英寸,如果我们使用MM_LOENGLISH(0.01英寸)的映射模式,那么26.75英寸将是电脑的26.75/12=2.23(英寸),在电脑里1个逻辑单位是0.01英寸,所以,26.75英寸转换为逻辑单位是223单位,但是这过程中有省略,为了防止这样的情况,我们可以使用物理坐标保存。

转换函数;DPtoLP() 设备坐标到逻辑坐标; LPtoDP()逻辑坐标到设备坐标;物理坐标到逻辑坐标的转换全是由我们自己计算;

MFC 中一般情况下有下面的情况:
CDC类中的所有成员函数用的是逻辑坐标做参数
CWND类中的所有成员函数用的是设备坐标做参数
所有的选中-测试(HIT-TEST)操作都是用的设备坐标,有些函数只能使用设备坐标如:CRect::PtInRect()
所有要长期保存的值一般用逻辑坐标,用设备坐标则用户对窗口滚动就会改变,该坐标就会失效了;

下一例子,测试鼠标左键点的区域是不是在指定的地方
void CMyView::OnLButtonDown(UINT uFlags,CPoint point)
{
CRect rect=m_rect;
CClientDC dc(this);
dc.SetMapMode(MM_TEXT);
dc.LPtoDP(rect);
if(rect.PtInRect(point))
{
MessageBox("YES");
}

}

一般设置映射模式在OnPrepareDC() 中比较合适~,OnPrepareDC在OnDraw前调用!

MFC 对140 种Windows 消息提供了直接的消息控制函数,特别注意下面5个:WM_CREATE,WM_CLOSE,WM_DESTROY,WM_NCDESTROY,WM_QUERYENDSESSION.

WM_CREATES 是WINDOWS 发送给视图的第一个消息,所以在OnCreate()中不能调用那写依赖窗口完全处于激活的WINDOWS函数!,一般来说可以在 OnInitialUpdate()中调用,如:设置映射模式~,但是必须注意,OnInitialUpdate()在视图生存之间可能被调用多次~

Windows应用程序绘制图形时使用的是一种逻辑单位,每个逻辑单位的大小由映射模式决定, 这个逻辑单位既可以与设备单位(屏幕或打印机上的一个像素点)相同,也可以是一种物理单 位(如毫米),还可以是用户自定义的一种单位。在Windows应用程序中,只要与输出有关系,都 要使用映射模式。本文的目的是帮助读者了解映射模式的一些基本知识,并对在使用中经常 出现的一些问题提出解决方案。
一、映射模式基本知识
当Windows应用程序在其客户区绘制图形时,必须给出在客户区的位置,其位置用x和y 两个坐标表示,x表示横坐标,y表示纵坐标。在所有的GDI绘制函数中,这些坐标使用的是一 种"逻辑单位"。当GDI函数将输出送到某个物理设备上时,Windows将逻辑坐标 转换成设备坐标(如屏幕或打印机的像素点)。逻辑坐标和设备坐标的转换是由映射模式决 定的。映射模式被储存在设备环境中。GetMapMode函数用于从设备环境得到当前的映射模 式,SetMapMode函数用于设置设备环境的映射模式。

1.逻辑坐标
逻辑坐标是独立于设备的,它与设备点的大小无关。使用逻辑单位,是实现"所见即所得"的基础。当程序员在调用一个画线的GDI函数LineTo,画出25.4mm(1英寸) 长的线时,他并不需要考虑输出的是何种设备。若设备是VGA显示器,Windows自动将其转化为96个像素点;若设备是一个300dpi的激光打印机,Windows自动将其转化为300个像素点。
2.设备坐标
Windows将GDI函数中指定的逻辑坐标映射为设备坐标,在所有的设备坐标系统中,单位以像素点为准,水平值从左到右增大,垂直值从上到下增大。
Windows中包括以下3种设备坐标,以满足各种不同需要:
(1)客户区域坐标,包括应用程序的客户区域,客户区域的左上角为(0,0)。
(2)屏幕坐标,包括整个屏幕,屏幕的左上角为(0,0)。屏幕坐标用在WM_MOVE消息中(对于非子窗口)以及下面的Windows函数中:CreateWindow和MoveWindow(都对于非子窗口)、GetMessage、GetCursorPos、GetWindowRect、WindowFromPoint和SetBrushOrg中。用函数ClientToScreen和ScreenToClient可以将客户区域坐标转换成屏幕区域坐标,或反之。
(3)全窗口坐标,包括一个程序的整个窗口,包括标题条、菜单、滚动条和窗口框,窗口的左上角为(0,0)。使用GetWindowDC得到的窗口设备环境,可以将逻辑单位转换成窗口坐标。
3.逻辑坐标与设备坐标的转换方式
映射方式定义了Windows如何将GDI函数中指定的逻辑坐标映射为设备坐标。要继续讨论映射方式我们要介绍Windows有关映射模式的一些术语:我们将逻辑坐标所在的坐标系称为"窗口",将设备坐标所在的坐标系称为"视口"。
"窗口"依赖于逻辑坐标,可以是像素点、毫米或程序员想要的其他尺度。
"视口"依赖于设备坐标(像素点)。通常,视口和客户区域等同。但是,如果程序员用GetWindowDC或CreateDC获取了一个设备环境,则视口也可以指全窗口坐标或屏幕坐标。点(0,0)是客户区域的左上角。x的值向右增加,y的值向上增加。
对于所有映射模式,Windows都用下面两个公式将窗口坐标转换成视口坐标:
xViewport=(xWindow-xWinOrg)*(xViewExt/xWinExt)+xViewOrg
yViewport=(yWindow-yWinOrg)*(yViewExt/yWinExt)+yViewOrg
其中,(xWindow,yWindows)是待转换的逻辑点,(xViewport,yViewport)是转换后的设备点。如果设备坐标是客户区域坐标或全窗口坐标,则Windows在画一个对象前,还必须将这些坐标转换成屏幕坐标。
这两个公式使用了分别指定窗口和视口原点的点:(xWinOrg,yWinOrg)是逻辑坐标的窗口原点;(xViewOrg,yViewOrg)是设备坐标的视口原点。在缺省的设备环境中,这两个点均设置为(0,0),但它们可以改变。此公式意味着,逻辑点(xWinOrg,yWinOrg)总被映射为设备点(xViewOrg,yViewOrg)。
Windows还能将视口(设备)坐标转换为窗口(逻辑)坐标:
xWindow=(xViewport-xViewOrg)*(xWinExt/xViewExt)+xWinOrg
yWindow=(yViewport-yViewOrg)*(yWinExt/yViewExt)+yWinOrg
可以使用Windows提供的两个函数DPtoLP和LPtoDP在设备坐标及逻辑坐标之间互相转换。
4.映射模式的种类
Windows定义了表1所列出的8种映射方式。
上述映射模式中又可分成以下3类:
映 射 方 式 逻 辑 单 位 X 轴 增 加 Y 轴 增 加 毫 米
MM_TEXT 像 素 点 右 下 与 设 备 有 关
MM_LOMETRIC 0. 1mm 右 上 0.1
MM_HIMETRIC 0. 01mm 右 上 0.01
MM_LOENGLISH 0. 254mm 右 上 0.254
MM_HIENGLISH 0. 0254mm 右 上 0.0254
MM_TWIPS 0.0176mm 右 上 0.0176
MM_ISOTROPIC 任 意(x=y) 可 选 可 选 可 设
MM_ANISOTROPIC 任 意(x!=y) 可 选 可 选 可 设

MM_TEXT映射模式这种映射模式被称为"文本"映射方式,不是因为它对 于文本最合适,而是轴的方向与读文本的方向一致。Windows提供了函数SetViewportOrg和SetWindowOrg 用来设置视口和窗口的原点。缺省的窗口原点和视口原点均为(0,0),可以改变;缺省的窗 口范围和视口范围均为(1,1),不可改变。
度量映射方式MM_LOMETRIC、MM_HIMETRIC、MM_LOENGLISH、MM_HIENGLISH和MM_TWIPS 将1个逻辑单位映射为固定的实际单位,其中1twip等于0.0176mm(1/1440英寸)。其他映射模式对应的物理单位参见表1。设置了映射模式以后,Windows自动设置了窗口及视口的范围,范围本身的值并不重要,但范围比是一个固定的值,对于MM_LOMETRIC,Windows计算范围比xViewExt/xWinExt=0.1mm中水平像素的点数。
自定义映射模式MM_ISOTROPIC和MM_ANISOTROPIC两种映射模式允许程序员设置自己的窗口和视口范围。MM_ISOTROPIC和MM_ANISOTROPIC的区别是所设置的x轴和y轴的的范围必须相同,而MM_ANISOTROPIC所设置的x轴和y轴的的范围可以不同。isotropi的意思是" 在所有方向相同",anisotropic的意思正相反。自定义映射模式中窗口和视口的原点和范围都可以改变,程序员可以设置自己需要的映射模式。函数SetWindowExt和SetViewportExt 用于改变窗口和视口的范围。下面的代码将1个逻辑单位映射成0.396mm(1/64英寸)。
SetMapMode(hDC,MM_ISOTROPIC);
SetWindowExt(64,64);
SetViewportExt(hdc,GetDeviceCaps(hdc,LOGPIXELSX),GetDeviceCaps(hdc, LOGPIXELSY));
二、与映射模式有关的问题的解决

实际应用中,程序员会遇到一些与显示模式有关的问题。例如OLEServer中映射模式 的设置、如何减少逻辑坐标与设备坐标间相互转换的误差等。下面,笔者就讨论一下这两个 问题的解决方法。

1.OLEServer中映射模式的设置方法
开发OLEServer应用程序时,如果程序员直接调用SetMapMode函数将映射模式设置成度量映射方式中的一种后,在Windows95/98上程序会正常运行,但在WindowsNT上对象显示的大小比边框小。经过笔者研究后,发现WindowsNT上OLEServer应使用基于逻辑英寸的映射方式。在讨论如何设置基于逻辑英寸的映射方式前,我们先介绍一下逻辑英寸的概念。
Windows在显示时以"逻辑英寸"为单位,逻辑英寸比实际的英寸要大。如果Windows程序使用实际英寸,则普通的10磅文本在显示器上就会小到几乎难以辨认,因此Windows使用放大了的"逻辑英寸"来表示文本。逻辑英寸只影响显示,而不影响打印。
使用GetDeviceCaps函数可得到当前设备的各种能力,其第一个参数nIndex指示要获取信息的类型。当nIndex为HORZSIZE和VERTSIZE时,可得到显示区域的宽度和高度;当nIndex 为HORZRES和VERTRES时,可得到每个水平和垂直方向的像素数即分辨率;当nIndex的值为LOGPIXELSX 和LOGPIXELSY时,可得到水平和垂直方向每逻辑英寸所含像素数。
在介绍了逻辑英寸的知识以后,很容易将OLEServer设置为基于逻辑英寸的映射模式。如果程序员仅仅调用SetMapMode(hdc,MM_LOENGLISH)来设置映射模式,当前的映射模式为物理英寸,而不是逻辑英寸。设置逻辑英寸必须自定义窗口和视口的范围,使xViewExt/xWinExt =0.01逻辑英寸中水平像素的点数,当xViewExt=LOGPIXELSX,xWinExt=100时,其比值正好满足上述要求。
以下是设置映射模式的代码。
intxLogPixPerInch=GetDeviceCaps(hdc,LOGPIXELSX);
intyLogPixPerInch=GetDeviceCaps(hdc,LOGPIXELSY);
SetMapMode(MM_ANISOTROPIC);
SetWindowExt(100,100);
SetViewportExt(xLogPixPerInch,yLogPixPerInch);
上述代码中调用SetMapMode函数将映射模式设置为自定义的,该调用必须位于SetWindowExt 和SetViewportExt调用之前,否则设置将会无效。
上述代码实际上将映射模式设置成逻辑MM_LOENGLISH,若程序员需要设置逻辑MM_LOMETRIC、MM_HIMETRIC、MM_HIENGLISH 或MM_TWIPS,只需修改上述代码中的SetWindowExt的参数,该参数实际上是每英寸所包含的各种映射模式下的单位数。根据表1中各映射模式的参数,可得到表2中每英寸所对应的各逻辑单位的个数。
例如,要设置逻辑MM_TWIPS,函数SetWindowExt中的参数为应1440。
2.逻辑坐标与设备坐标转换时误差的处理
表2
映 射 模 式 每 英 寸 所 对 应 的 逻 辑 单 位 数
MM_LOENGLISH 100
MM_HIENGLISH 1000
MM_LOMETRIC 254
MM_HIMETRIC 2540
MM_TWIPS 1440

当我们将映射模式设置成基于逻辑英寸的MM_LOMETRIC时,窗口的范围设为256,视口的范围设为96(在VGA显示器下LOGPIXELSX的值),约2.6个逻辑单位对应1个像素,这显然会造成不小的误差,它会表现在应用程序的各个方面:客户区的一个部分没有被刷新;对象之间本来没有间距,却显示出有间距;对象在屏幕的不同位置上会缩小或增大一个像素等问题。
可以采取以下两个步骤避免转换误差。(1)尽量选择窗口范围和视口范围比可以整除的映射方式,例如基于逻辑英寸的MM_TWIPS其窗口范围和视口范围比1440/96,可简化为15/1,从设备坐标转化为逻辑坐标时没有误差,从消除误差角度看,MM_TWIPS比其他几个映射模式都要好。(2)窗口范围和视口范围比不能整除时,也尽量将其简化,例如,当采用0.3900mm 中的将1个逻辑单位映射成1/64英寸的映射方式时,其窗口范围和视口范围比值为64/96,可简化为2/3。如果我们将逻辑单位的值都取为2的倍数,设备单位的值都取为3的倍数,转换后就没有精度的丢失了。
综上所述,如果我们能够根据映射模式值的特点,逻辑坐标和设备坐标都取经简化的窗口和视口范围值的倍数,则逻辑坐标和设备坐标间的转化将没有误差。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: