第12章 绘图 Windows绘图的实质就是利用Windows提供的图形设 备接口GDI(Graphics Device Interface)将图形绘制 在显示器上在Windows操作系统中,绝大多数图 形界面的应用程序都离不开GDI利用GDI所提供的 众多函数可以方便的在屏幕、打印机及其它输出设 备输出图形和文本操作通过本章学习可以使读者 对GDI绘图有一定的了解12.1 CDC类及其派生类 在MFC中,CDC是设备环境的基类它封装了绘图 所需要的所有成员函数和设备环境的所有功能同 时MFC还提供了几个CDC的派生类,用于具体绘图 操作中,如CClientDC、CMetaFileDC、CPaintDC和 CWindowDC12.1.1 DC基类CDC DC即设备上下文(Device Context),它定义了图形对象的 参数设置以及它们的属性,还有影响输出结果的模式选 择CDC类是设备上下文环境对象的类CDC类提供了 处理显示器以及打印机等设备上下文的成员函数而且 它提供了处理与窗口客户区对应的显示上下文的成员变 量 在Windows系统中,通过CDC对象的成员函数可以进行 所有的绘图操作。
CDC类提供的成员函数可以用于操作 设备描述对象、使用绘图工具、选择图形设备界面,以 及操作颜色和调色板;还可用于取得和设置绘图属性、 映射方式、视图和窗口范围的操作、坐标的转换、区域 的使用、剪取、画线以及绘制图形和文字等操作12.1.2 客户区DC类CClientDC CClientDC即客户区设备描述表,用于客户区的输出 ,与特定窗口关联通过CClientDC类开发者可以访 问目标窗口中客户区这个类派生于CDC类,它在 构造函数中调用GetDC()函数,在析构函数中调用 ReleaseDC()函数也就是说,当一个CClientDC对象 在构造时,它内部会调用GetDC()函数,获得一个设 备描述表对象;当一个CClientDC对象析构时,会自 动释放这个设备描述表资源12.1.3 窗口DC类CWindowDC CWindowDC也派生于CDC类,并且在构造时调用 GetWindowDC()函数获得相应的设备描述表对象,在析 构时调用ReleaseDC()函数释放该设备描述对象所占用的 资源也就是说,我们在创建CWindowDC对象绘图时, 也不需要调用GetDC()和ReleaseDC()函数。
使用CWindowDC对象可以访问整个窗口区域,包括框架 窗口的非客户区和客户区该对象的构造与CClientDC相 同,同样如果要构造一个与视图窗口相关的设备描述表 ,则可利用视类对象的指针来构造这个CWindowDC对 象12.1.3 重绘DC类CPaintDC CPaintDC也是CDC的派生类,当然也可以调用CDC 中提供的各种方法但是CPainDC只能用在 WM_PAINT消息处理函数中,或者说只有 WM_PAINT消息存在于消息队列中时CPaintDC才能 使用当然,在WM_PAINT消息处理函数中要实现 绘图操作也只能用CPaintDC12.1.4 图元文件DC类CMetaFileDC CMetaFileDC类提供了一个面向Windows图元文件的 设备环境,封装了在一个Windows图元文件中绘图 的方法图元文件是一个与设备无关的图片的集合 ,由于它对图像的保存比像素更精确,因而往往在 要求较高的场合下使用例如,AutoCAD的图形保 存等因为CMetaFileDC是一种特殊的DC类,所以 在此我们就不做过多的介绍了12.2 画笔和画刷 画笔和画刷是GDI绘图中最基本的元素。
其中,画笔 主要用来绘制线条、曲线以及勾勒形状轮廓画刷 可用于填充一个封闭图形,所以,画笔主要用来画 线,而画刷可绘制和填充图形通过本节的学习读 者可以了解到画笔和画刷的基本使用过程12.2.1 画笔类Cpen 上一节中我们通过调用CDC类中方法画出的线条均 为黑色实线,而且线宽都是一样的这是因为设备 上下文(DC)中有一个默认的黑色画笔,因此画出 的线条都是一样的如果想要画出其它样式和颜色 的线条,就需要我们自己来定义一个画笔,然后将 其选入设备上下文(DC)中即可12.2.1 画笔类Cpen MFC提供了一个画笔类CPen来创建画笔对象该类 封装了与画笔相关的操作,它有三个构造函数,其 中一个原型声明如下所示12.2.1 画笔类Cpen RGB宏声明以及其定义如下所示12.2.2 改变线型和颜色 在利用CPen类自定义画笔画线的时候,一般需要4步 : (1)自定义一个CPen对象,并进行初始化 (2)利用SelectObject()函数将CPen对象选入设备上 下文的时候,该函数会返回指向先前被选对象的指 针所以,这时候我们还需要定义一个额外的CPen 对象指针来接收该地址。
(3)执行画线操作 (4)利用SelectObject()函数把先前的CPen对象选入 设备描述表中,恢复先前的状态12.2.3 画刷类CBrush 画刷类主要是用来填充某个区域,由于前面我们画 的是线条所以没法使用画刷本小节将以画矩形框 为例介绍画刷的用法,以及和画笔之间的关系 画刷类的构造函数有四个:默认画刷、简单画刷、 网格画刷和位图画刷由于默认画刷为单一的白色 填充,所以我们就不做介绍了下面就后面三种画 刷做简要讲解12.2.4 使用画刷画图 上面一小节简单展示了画刷绘图的几种效果下面 我们编写一个例子程序,实现鼠标拖拽来画一个矩 形图通过这个例子来介绍画刷的具体使用为了 节省篇幅,我们仍然在Graphics项目中进行修改 我们想创建一个透明的画刷,但是CBrush类并没有 提供给我们创建透明画刷的方法如果想要实现透 明画刷,得额外用到两个函数:一个是 GetStockObject()、一个是CBrush类中的静态成员函 数CBrush::FromHandle()12.2.4 使用画刷画图 GetStockObject()函数用于检索预定义的备用笔、刷 子、字体或者调色板的句柄。
该函数的声明如下所 示12.2.4 使用画刷画图 CBrush::FromHandle()为CBrush类中的一个静态成员 函数,它的作用就是将GDI对象句柄转换为画刷对 象利用GetStockObject()获得的是GDI句柄类型, 如果想选入设备描述表必须转换为CBrush画刷对 象该函数的声明如下所示12.3 图形的重绘和保存 大家或许已经发现,当我们调整窗口大小时,前面 所画的图形会立即消失这是因为窗口发生了重绘 ,在OnDraw()函数中执行绘图操作会避免这种情 况如果要使我们所画的图形能够永存,我们需要 对图形进行存盘,即保存操作12.3.1 绘制连续线条 绘制连续线条,即通过拖动鼠标画出不规则的连续 线条,线条形状和鼠标的轨迹吻合12.3.2 线条数据结构设计 可以发现,当我们绘制完图形后,改变窗口的大小 时,图形会消失那是因为我们并没有记录下线条 的轨迹,当调整窗口大小时,窗口会重绘,将之前 的痕迹抹去所以,要想发生重绘后依然保持线条 的痕迹,我们必须将所有的线条保存起来,然后在 OnDraw()函数中做重新绘制操作12.3.3 线条的保存和读取 我们在之前学习过文件的保存,利用MFC提供的串 行化操作可以很方便的完成图形的保存。
要保存线 条首先要使CStroke变成一个可串行化的类12.4 小结 本章首先讲解了CDC类及其派生类的功能和具体使 用情况,还剖析了CPaintDC的隐藏的一些秘密;然 后我们通过例子讲解了画笔和画刷的使用;最后我 们实现了图形的重绘和保存本章的重点内容是 CDC类的使用和图形的重绘,还应该学会怎样把自 定义的设备选入设备描述表难点内容是图形的重 绘和保存,因为这涉及到数据结构的设计。