设备对象属性决定着绘图方式,当使用GDI函数绘图时,所绘制的图形和文本的颜色、大小和位置等由设备对象的当前属性决定,应用程序可以使用GDI函数改变设备对象的当前属性。本章介绍设备对象的一些主要属性和用于改变这些属性的有关函数,其它的设备对象属性在后面的章节中陆续介绍。
3.1、图形设备接口 在第二章,我们一直在使用图形设备接口(GDI),GDI的主要目标之一是支持在输出设备(例如显示器、打印机)上建立与设备无关的图形输出。Windows的图形大部分是由GDI.EXE(Windows系统的一个模块,称GDI模块)中的函数处理的。GDI模块通过调用在不同设备驱动程序模块中的例程来控制输出设备。例如,显示设备驱动程序用于访问与视频显示器有关的硬件。通过GDI,Windows可以确定驱动程序能够进行什么工作,并且由于应用程序只与GDI打交道。这样,GDI通过将应用程序与不同的输出设备隔离,使应用程序可以在支持Windows的任何图形输出设备上工作。 图形输出设备可以分为两组:光栅设备和矢量设备。光栅设备将图象表示为点(象素)的图案。这类输出设备包括显示设备、点阵打印机和激光打印机。向量设备用线段来绘制图象,例如绘图仪。Windows的GDI是一种隔离了硬件具体特性的图形语言。虽然输出设备用象素来表示图形,但GDI却可以被用作一个高级的向量绘图系统,也可以被用来进行较低级的象素操作。 在编写Windows应用程序时,程序员不必为颜色过分担心,如果在应用程序中使用的一种颜色不能被该显示器所表示,Windows或为应用程序选择一种最直接的纯颜色(显示设备可以表示的颜色),或通过将几种纯颜色相混合来表示这种颜色。当在彩色显示器上开发的程序运行在单色显示器上时,Windows将使用灰度来表示颜色。应用程序也可以在程序中确定输出设备的有关特性,例如,可表示的颜色数目、设备的显示区的尺寸等,以便最大限度地发挥硬件的能力。
3.2、设备对象属性 设备对象具有许多决定GDI函数在设备对象上如何工作的当前属性。例如,在使用函数TextOut()时,只需要在函数中说明设备对象的句柄、绘制字符的起始坐标、文本和文本长度,而不用说明字体、文本颜色、背景颜色和字符间距等,因为这些特征由设备对象的属性决定。每种设备对象都赋有缺省的属性,可以使用GDI函数改变这些属性中的某一个。表3-1给出了显示设备各个属性的缺省值,当使用GetDC()和BeginPaint()等函数初次得到一个显示设备对象时,该对象的属性具有缺省值。
属性 | 缺省值 | 视区原点 | (0, 0) | 视区范围 | (1, 1) | 窗口原点 | (0, 0) | 窗口范围 | (1, 1) | 背景颜色 | 白色 | 背景方式 | OPAQUE | 位图 | 任意值 | 刷子 | WHITE_BRUSH | 刷子原点 | (0, 0) | 裁剪区 | 用户区/无效矩形区/子窗口区 | 调色板 | DEFAULT_PALETTE |
| | 属性 | 缺省值 | 笔的当前位置 | (0, 0) | 笔的颜色 | BLACK_PEN | 文本颜色 | 黑 | 设备的原点 | 用户区的左上角 | 绘图方式 | R2_COPYPEN | 字体 | SYSTEM_FONT | 字符间距 | 0 | 映射方式 | MM_TEXT | 多边形填充方式 | ALTERNATE | 相对一绝坐标 | ABSOLUTE | 缩放方式 | BLACKONWHITE |
|
在本章以后的各节中将介绍其中一些设备属性,其它属性在以后章节中介绍。
3.3、设备坐标系 为在输出设备上定位和绘制图形对象,必须引入一种坐标系。Windows的各种不同类型设备所使用的坐标称为设备坐标。它们使用笛卡尔坐标系,在这些设备坐标系中,单位都以象素的个数表示(称为设备单位)。x轴上的值自左向右增加,y轴上的值自顶向下增加,见图3-1。
| 图3-1 Windows的设备坐标系 |
本节以视频显示设备为例介绍Windows的设备坐标系,其中的许多内容也适合用于象打印机等硬拷贝输出设备。 在Windows环境中,视频显示设备是一个共享设备,即在同一时刻,显示设备上可以同时显示多个应用程序的输出信息。为了保护一个程序显示的信息不被其他程序破坏,Windows通过将显示区看作不同的设备对象来限制应用程序输出信息的范围。 一个应用程序可获取三种不同的显示设备对象句柄,每种句柄所标识的设备对象代表屏幕上的不同区域。我们可以将这三个句柄所标识的对象视作三个不同的抽象显示设备,都带有如图3-1所示的设备坐标系,但对不同的抽象设备,坐标原点不一样的。这样,当使用相同的起始坐标而使用不同的设备对象句柄调用GetDC函数(例如TextOut())进行绘图时,信息显示的位置不一样。 第一抽象设备是用户区对象,它的坐标原点在用户区的左上角。定位该区域的设备坐标系称为用户区坐标系。使用函数GetDC()或BeginPaint()得到的句柄是标识用户区的句柄。当使用该句柄标识该区域的句柄作为GDI函数的参数时,GDI函数所使用的坐标值是相对于用户区坐标系。 第二个抽象设备是全窗口对象。它包括标题栏、选单、滚动杆和窗口框架等。定位这个区域的坐标系称为全窗口坐标系,它的原点的左上角。使用函数GetWindowsDC()可以获得该设备对象的句柄,然后通过该句柄使用全窗口坐标系在该区域中绘图。应用程序一般不在这个区域中绘图。 第三个抽象设备是整个屏幕对象,其坐标原点在屏幕的左上角,定位该区域的设备坐标系称为屏幕坐标系。使用语句:
HDC hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
可以获得该设备对象的句柄,使用该句柄的GDI函数所使用的坐标是相对于屏幕坐标系。 这三种坐标系方便了程序在不同的区域绘制图形的需要。例如,由于使用用户区坐标系,即使窗口在屏幕上被移动到其他位置,但用户区中显示的信息相对于用户坐标系而言其坐标值不变。 使用坐标系,解决了显示对象的定位的问题的。但以象素单位所建立的坐标系不符合用户(或程序员)的习惯,而且在不同分辨率的输出设备上,一个象素的大小也不相同的,这样,应用程序的输出在不同机器上会得到不同的结果。为解决这个问题,Windows引入了逻辑坐标系。它使得用户可以按照自己习惯使用的尺寸(毫米、英寸等)来描述客体,或绘制图形。用户在程序中使用逻辑坐标系中的坐标值,而由Windows根据映射方式将逻辑坐标系中的坐标值转算成设备坐标系中的坐标值(对于显示设备而言,具体映射到三个坐标系中哪一个取决于GDI函数中所使用的设备对象句柄)。映射方式也同时决定着逻辑坐标值的单位(简称逻辑单位)和坐标轴的正方向。在前面介绍的TextOut和DrawText()使用的是逻辑单位,而函数CreateWindow()使用的是屏幕坐标系中的坐标(设备单位,及象素)。程序员应该清楚什么时候在使用逻辑单位。一般而言,当映射方式起作用时,使用的是逻辑单位,而映射方式起作用的唯一时候是使用了那些需要将设备对象句柄作为参数的GDI函数的时候,但并非总是这样,因此,读者应注意这方面的问题。 表3-2给出的两个函数用于进行用户区坐标和屏幕坐标(注意是设备单位)之间的转换。
表3-2-1 ClientToScreen 函数
用 途 | 将一个点的用户区坐标转换为屏幕坐标。 | 原 型 | VOID ClientToScreen( | | HWND hWnd, | 与用户区相关联的窗口句柄 | LPPOINT lpPoint | 指向一个POINT结构类型的变量,其中一个点的用户区坐标值,并被转换后的屏幕坐标值取代。 | ); | |
| |
|
|
|
|
|
表3-2-2 ScreenToClient 函数
用 途 | 将给定点的屏幕坐标转换为用户区坐标。 | 原 型 | VOID ScreenToClient( | | HWND hWnd, | 与用户区相关联的窗口句柄 | LPPOINT lpPoint | 所指向的变量包含一个点的屏幕坐标值,并被转换后的用户区坐标值取代 | ); | |
| |
3.4、映射方式 影响在用户区绘图的一个主要设备对象属性是“映射方式”。它定义了Windows如何将GDI函数中使用的逻辑坐标映射为设备坐标。其他四个设备对象属性(窗口原点、视区原点、窗口范围和视区范围)都与映射方式密切相关。当Windows将逻辑单位转换为设备单位或象素时,映射方式、窗口原点和视区原点、窗口范围和视区反问决定着这种转换。映射方式也隐含了x轴坐标和y轴的原点和方向。在介绍各种映射方式之前,我们先介绍窗口与视口关系。
3.4.1 窗口与视口 视口是计算机屏幕上一块显示区域,随着在GDI中所使用的设备对象句柄的不同,该区域可是用户区、全窗口区或整个屏幕区,视区中的图形一设备单位定义。与视区中显示的图形相对应的原始图形区域称为窗口。注意,在这里使用的术语“窗口”不是指屏幕上显示的可视窗口对象。这里的“窗口”是从现实世界角度所看到图形,而“视区”是从数据世界角度而言的,是屏幕上的象素形成的图形。在Windows中,视区不是一个裁剪区。图3-2和图3-3说明了窗口和视口的关系。
| | | 图3-3 视区中显示的曲线 |
|
映射方式指的是从“窗口”(逻辑坐标)到“视区”(设备坐标)的变换。“视区”采用设备坐标(象素数)。“窗口”采用逻辑坐标,它可以是象素数、毫米、英寸或任何其他单位。GDI函数使用逻辑坐标。 将“窗口”(逻辑)坐标转换为“视区”(设备)坐标,使用如下两个映射方式:
xViewport = (xWindow - xWinOrg) * xViewExt / xWinExt + xViewOrg | yViewport = (yWindow - yWinOrg) * yViewExt / yWinExt + yViewOrg |
该公式将逻辑坐标系中的点(xWindow, yWindow)变换为设备坐标系中的点(xViewport, yViewport),其中,点(xWinOrg, yWinOrg)是以逻辑单位表示的“窗口”原点,而点(xViewOrg, yViewOrg)是以设备坐标表示的“视区”原点。在缺省情况下,这两个点被设置为(0, 0),但它们可以被改变。注意,逻辑点(xWinOrg, yWinOrg)总是被映射为设备点(xViewOrg, yViewOrg)。 改变上述的公式为:
xWindow = (xViewport - xViewOrg) * xWinExt / xViewExt + xWinOrg | yWindow = (yViewport - yViewOrg) * yWinExt / xViewExt + yWinOrg |
该公式可以将视区坐标转换为窗口坐标。
表3-3-1 DptoLP 函数
用 途 | 将设备点变换为逻辑点。 | 原 型 | BOOL DPtoLP( | | HDC hDC, | 设备对象句柄 | LPPOINT lpPoints, | 指向POINT类型的变量的指针 | int nCount | 要进行变换的点的数目 | ) | |
| |
返回值 | 若变换成功,返回非零。 |
表3-3-2 LPtoDP 函数
用 途 | 将逻辑点变换为设备点。 | 原 型 | BOOL LPtoDP( | | HDC hDC, | 设备对象句柄 | LPPOINT lpPoints, | 指向POINT类型的变量的指针 | int nCount | 欲进行变换的点的数目 | ) | |
| |
返回值 | 若所有的点被变换,返回非零。 |
表3-3给出了两个函数,用于进行设备坐标和逻辑坐标之间的相互变换。例如,函数GetClientRect获取的拥护区域的大小总是以设备单位表示的,若想使用逻辑单位表示用户区大小,可以使用函数DPtoLP();
RECT rect; GetClient(hWnd, &rect); DPtoLP(hDC, (LPPOINT)&rect, 2);
3.4.2 Windows的映射方式 Windows定义了八种映射方式,见表3-4。
表3-4 Windows的映射方式 映射方式 | 逻辑单位单位 | x轴方向 | y轴方向 | MM_TEXT | 象素数 | 向右 | 向下 | MM_LOMETRIC | 0.1mm | 向右 | 向上 | MM_HIMETRIC | 0.01mm | 向右 | 向上 | MM_LOENGLISH | 0.01英寸 | 向右 | 向上 | MM_HIENGLISH | 0.001英寸 | 向右 | 向上 | MM_TWIPS | 1/1440英寸 | 向右 | 向上 | MM_ISOTROPIC | 自定义(x=y)(即x和y的逻辑单位大小一样) | 由比例因子决定若为正,向右。否则,向左 | 由比例因子决定若为正,向下。否则,向上 | MM_ANISOTROPIC | 自定义(x!=y)(即x和y的逻辑单位大小不一样) | 同上 | 同上 |
注:Twips表示“一个点的二十分之一”,在GDI中,一个点的大小定为1/72英寸,所以一个twips是1/1440英寸。 在缺省时,设备对象使用的映射方式是MM_TEXT,即逻辑单位等于物理单位。例如:
TextOut(hDC, 4, 6, "Hello", 5);
在用户区向右偏移4个象素,向下偏移6个象素的位置开始显示信息“Hello”。 可以使用函数SetMapMode()设置映射方式,或使用函数GetMapMode()获取一个设备对象当前的映射方式,见表3-5,例如,语句:
SetMapMode(hDC, MM_LOMETRIC); TextOut(hDC, 100, -200, "Hello", 5);
首先设置MM_LOMETRIC映射方式,然后在离用户区原点向右1厘米、向下2厘米的位置显示信息“Hello”。这里使用了负的坐标值,因为根据窗口原点变换到视口原点的原则,只有窗口坐标的y坐标为负值的点才可以变换到用户区中,y坐标为正值的点落在用户区之外。 <  
1/2 1 2 下一页 尾页 |