第 6章 图形设备接口由于 Windows 系统为基础操作系统和硬件之间提供了图形用户接口( GUI),因此图形是
Windows程序的主要组成部分。在当今的 GUI 操作系统世界里图形学是十分重要的,Windows当然也毫不例外。本章主要介绍在 Windows应用程序实现中如何理解 MFC的封装、设备描述表、图形对象以及如何使用图形学的基本概念,讨论用来显示绘图称之为设备描述表的数据类型和结构,
并描述用来建立 Windows图形的每个基础图形对象。
设备描述表与图形对象当 Windows程序 ( 包括 Windows自己 ) 给显示器或其它别的输出设备 ( 如打印机 ) 绘制文本和图形时,它不像 DOS程序那样直接画到硬件上,而是通过图形设备接口来实现绘制的 。 实际上,在 Windows世界里,直接写到硬件上的应用程序被认为是禁忌,应用程序使用一个称之为设备描述表
( DC) 的概念,它表示物理设备 ( 如监视器,打印机,绘图仪或某个别的物理设备 ) 的逻辑形式 。 MFC提供几种 DC的不同类型,在给设备绘制图形前应用程序必须显式请求 DC。
然而,设备描述表不仅限于物理设备,DC也可指逻辑设备。逻辑设备的一个例子就是元文件。它是以与设备无关的格式存储图面的结构的集合。另一个例子是位图,它是图形图像的像素集合。在位图上或元文件上绘图如同在显示器上或打印机上绘图一样容易。
Win32 API 提供四种类型设备描述表:
l 显示描述表 。 支持视频显示器上图形操作 。
l 信息描述表 。 提供设备数据的检索 。
l 内存描述表 。 支持位图上的图形操作 。
l 打印描述表 。 支持在打印机或绘图仪上的图形操作 。
图形设备接口 (Graphics Device Interface,简称 GDI)在 Windows
系统中用以扩展设备描述表,它是 Windows系统结构的主要组成部分。在 SDK程序里,DC作为参数发送至 GDI函数调用,以给
Windows提供要在上面绘图设备的特性说明。 GDI给 Windows提供全部绘图功能; DC表示提供抽象层的设备,该抽象层使应用程序与直接绘图到硬件上的有害性相隔离(图 6-1)展示硬件抽象)。
通过调用合适的设备驱动程序以响应 Windows图形函数调用的途径,GDI提供此隔离层。
下面分别讲述各种 GDI对象和拥有的属性,
l CFont类:字体 CFont对象用于输出文字时选用不同风格和大小的字体 。 可选择的风格包括:是否为斜体,粗体,字体名称,下划线等 。 颜色和背景色不属于字体的属性 。
l CBrush类:刷子 CBrush对象决定填充区域时所采用的颜色或模板 。 对于一个固定色的刷子来讲它的属性为颜色,是否采用网格和网格的类型如水平的,垂直的,交叉的等 。 你也可以利用 8*8的位图来创建一个自定义模板的刷子,在使用这种刷子填充时系统会利用位图逐步填充区域 。
l CPen类:画笔 CPen对象在画点和画线时有用 。 它的属性包括颜色,宽度,线的风格,如虚线,实线,点划线等 。
l CBitmap类:位图 CBitmap对象可以包含一幅图像,可以保存在资源中 。 关于位图编程技术请见下一章 。
CRgn类:多边形 CRgn对象是一种特殊的 GDI对象,该对象利用多边形可以很好的限制作图区域或是改变窗口外型。
在 Windows中使用 GDI对象可以按以下步骤加以实现:
1,首先在绘制文字,图形,图象等之前,我们需要创建一个合法的
GDI对象,不同的对象创建方法不同,有些对象必须经过初始化才能使用 。 我们定义在代码块内的图形对象,用相应的 CreateX( ) 方法初始化对象 。 例如,使用 CreatePalette()去创建 CPalette对象 。
2,然后需要将建立的 GDI对象选入 DC中,同时保存 DC中原来的 GDI
对象 。 一般通过使用 CDC:,SelectObject()方法选择新对象进入当前设备描述表 。 此方法返回指向被取代对象的指针 ( 一个指向
CGdiObject派生类的指针 ) 。 注意:如果你选入一个非法的对象将会引起异常 。
3,当图形对象结束它的任务时,先前选中的图形对象返回设备描述表,保留事物的原来状态 。 在使用完后恢复原来的对象,这一点特别重要,如果保存一个临时对象在 DC中,而在临时对象被销毁后可能引起异常 。 有一点必须注意,每一个对象在重新创建前必须销毁 。
注意:当对象越出范围时,在栈上声明的对象自动删除。恢复 DC的先前状态后,声明作为指针并与新操作符定位在一起的图形对象必须显式删除。
图形对象介绍
Windows提供了显示设备描述表定义的逻辑显示面,GDI
提供了在 DC上使用的绘图工具 。 MFC图形对象类封装了
Windows绘图工具,这些类都从称为 CGdiObject 的公用基类派生的 。 MFC定义了几种图形对象类型及其它们相应的
Windows绘图工具,这些图形对象和绘图工具类有:笔,刷子,字体,位图,调色板,区域 。
上述的 CGdiObject类根据图像对象句柄及其他,提供到原始 Win32 API的接口。我们决不要直接创建 CGdiObject
对象,可从它的派生类之一(如 CPen或 CFont)创建对象。
笔和刷子在 Windows应用程序开发中画点的方法很简单,我们可以通过调用下列函数
COLORREF CDC::SetPixel( int x,int y,COLORREF crColor )
就可在指定点位置画上指定颜色,同时返回原来的颜色值 。 我们也可通过调用函数
COLORREF CDC::GetPixel( int x,int y)
得到指定点的颜色 。 在 Windows中应该少使用画点的函数,因为这样做执行效率比较低 。
刷子和画笔在 Windows作图中是使用最多的 GDI对象,本小节在讲解刷子和画笔使用方法的同时也讲述一些基本作图函数 。
在画点或画线时系统使用当前 DC中的画笔,所以在创建画笔后必须将其选入
DC才会在绘图时产生效果。画笔可以通过 CPen对象来产生,通过调用 CPen:,
CreatePen( int nPenStyle,int nWidth,COLORREF crColor )来创建。
字体
CFont类封装了一个 Windows图形设备接口 ( GDI) 字体并提供管理字体的成员函数 。 为使用一个 CFont对象,可构造一个 CFont对象并用 CreatFont,
CreateFontIndirect,CreatePointFont 或 CreatePointFontIndirect 将 一 个
Windows 字 体 附 加 给 它,然 后 用 对 象 的 成 员 函 数 操 纵 字 体 。
CreatePointFont和 CreatePointFontIndirect常比使用 CreateFontIndirect更容易,
因为它们自动将字体高度从点大小变为逻辑单位 。
在这里我假定读者已经利用 Application Wizard生成了一个 SDI界面的程序代码。接下来的你只需要在 CView派生类的 OnDraw成员函数中加入绘图代码就可以了。在这里我需要解释一下 OnDraw函数的作用,OnDraw函数会在窗口需要重绘时自动被调用,传入的参数 CDC* pDC对应的就是 DC
环境。使用 OnDraw的优点就在于在你使用打印功能的时候传入 OnDraw的
DC环境将会是打印机绘图环境,使用打印预览时传入的是一个称为
CPreviewDC的绘图环境,所以你只需要一份代码就可以完成屏幕窗口显示 /打印预览 /打印机绘图三重功能。我们可以利用 Windows的设备无关性和微软为打印预览所编写的上千行代码很容易的完成一个具有所见即所得的 Windows应用软件。
输出文字一般使用下列两个函数:
l CDC::BOOL TextOut( int x,int y,const CString& str )
l CDC::int DrawText( const CString& str,LPRECT
lpRect,UINT nFormat )
其中,输出文字对 TextOut来讲只能输出单行的文字,而
DrawText可以指定在一个矩形中输出单行或多行文字,并且可以规定对齐方式和使用何种风格。 nFormat可以是多种以下标记的组合(利用位或操作)以达到选择输出风格的目的。
调色板调色板是存储颜色信息的 Windows GDI对象。 GDI调色板对象基本上是一个系统使用的颜色查找表用以决定在 256色调色板显示设备上显示哪种颜色。 Windows为系统定义了 20种调色板颜色,留下 236种颜色供应用程序自己使用。 MFC将
Windows彩色调色板封装到 CPalette类中。 Windows使用逻辑调色板(它是所要求颜色的列表)和系统调色板(它定义在硬件调色板内当前可用的颜色槽)。 CPalette类为创建和控制
CPalette对象提供 9种方法和 1个操作符。
注意:虽然技术上,术语硬件调色板和系统调色板可变换地使用,但系统调色板实际是硬件调色板中值的复制。
Windows Palette Manager使用系统调色板值。
多边形类多边形也是一个 GDI对象,同样遵守其他 GDI对象的规则,只是通常都不将其选入 DC中。在 MFC中多边形有 CRgn表示。多边形用来表示一个不同与矩形的区域,和矩形具有相似的操作。如:检测某点是否在内部,
并操作等。此外还得到一个包含此多边形的最小矩形。
多边形的作用主要在于当重新在窗口中作图时使用它可以提高效率 。
因为当引发窗口重绘的原因是使某个区域失效,而失效的区域可以用多边形来表示 。 假设窗口大小为 800*600,当其上面的另一个窗口从
(0,0,20,20)移动到 (20,20,40,40),这时 (0,0,20,20)区域就失效了,而你只需要重绘这部分区域而不是所有区域,这样你程序的执行效率就会提高 。
我们可以通过调用 API函数 int GetClipRgn( HDC hdc,HRGN hrgn)就可以得到失效区域,但是一般用不着那么精确而只需得到包含该区域的最小矩形就可以了,所以可以利用 int CDC::GetClipBox( LPRECT lpRect )完成这一功能。
映射方式在 Windows中,用 Windows GDI图形函数创建所有矢量图形和字体。矢量图形是与分辨率无关的形状的数学描述,
将向量描述映像到光栅设备(如视频显示)作为光栅(位图的)图像供观看。基 MFC设备描述表类 CDC提供封装了
Win32 GDI函数的矢量图形方法。这些方法取参数指定什么位置显示矢量图形;。然而,在 DC上任何图形的出现受 DC
当前正使用的系统坐标的影响。因为不同的坐标系统可强烈地改变 DC中图像的可视尺度因子。下面粗略地浏览
Windows提供的不同坐标系统。
GDI坐标系统
GDI支持两种类型坐标系统:物理坐标系统和逻辑坐标系统。物理坐标系统是物理设备的坐标系统(如视频显示器的)。显示器物理坐标窗口从原点( 0,0)位置 (即显示器左上角 )开始,X轴向右增长,Y轴向下延伸。显示器物理坐标窗口的右下角对应于显示器右下角,但视频显示器坐标的实际数值与用户使用的显示器的分辩率有关。显示器物理坐标窗口的右下角典型位置数值为:( 640,480),( 800,600)或( 1024,
768)等。物理坐标系统如下图 6-5所示:
Y
X
原点 ( 0,0)
输出设备映像方式
Windows使用映像方式将逻辑坐标系统规划到物理坐标系统里。逻辑窗口的原点定义在窗口的左上角,所有其它的点都参照原点。 映像方式还定义在转换逻辑单元到物理设备单元时用的逻辑测量单位,此映像方式也确定设备的 X 和 Y轴的原点。 GDI使用 DC的当前映像方式将逻辑坐标转换到相应设备坐标。在很多情况下使用系统预定义的映射系统非常的恰当,但并非总是这样。假设我们需要编写一个绘制函数图形的应用程序,把整个屏幕映射为一个平面直角坐标系会更加的恰当。所谓的映射方式简单点讲就是坐标的安排方式,系统默认的映射方式为 MM_TEXT即
X坐标向右增加,Y坐标向下增加,(0,0)在屏幕左上方,DC中的每一点就是屏幕上的一个象素。也许你会认为这种方式下是最好理解的,但是一个点和象素对应的关系在屏幕上看来是正常的,但到了打印机上就会很不正常。因为我们作图是以点为单位并且打印机的分辨率远远比显示器高( 800DPI 800点每英寸)所以在打印机上图形看起来就会很小。这样就需要为打印另做一套代码而加大了工作量。如果每个点对应 0.1毫米那么在屏幕上的图形就会和打印出来的图形一样大小。
我们可以通过下列函数
int CDC::SetMapMode( int nMapMode )
指定映射方式,但对当前映射模式的操作是在设备上下文中进行的,因此,MFC在 CDC
类中提供了成员函数 SetMapMode来改变当前所使用的映射模式。