1、第 6 章 GDI+/ 陈宝楷_Saturday, September 15, 2007GDI+(Graphics Device Interface Plus 图形设备接口加)是 Windows XP 和 Windows Server 2003 操作系统的子系统,也是 .NET 框架的重要组成部分,负责在屏幕和打印机上绘制图形图像和显示信息。顾名思义,GDI+是 Windows 早期版本所提供的图形设备接口 GDI 的后续版本。GDI+是一种应用程序编程接口(API),通 过一套部署为托管代码 的类来展现。这套类被称为 GDI+的“托管类接口 ”。GDI+最早是于 2001 年随 Window
2、s XP 一起推出的一种 API,后来又被包装 进.NET 框架的托管类库之中,成为.NET 中窗体绘图的主要工具。GDI+不但在功能上比 GDI 要强大很多,而且在代码编写方面也更简单,因此会很快成为 Windows 图形图像程序开 发的首选。本章将介绍 GDI+的特点和新增功能,以及 GDI+ API 的具体使用方法,包括二维矢量图形的绘制、图像处理的应用、以及文字的显示。6.1 概述GDI+与 GDI 一样,都具有设备无关性。应用程序的程序员 可利用 GDI+这样的图形设备接口在屏幕或打印机上显示信息,而不需要考 虑特定显示 设备的具体情况。 应用程序的程序员调用 GDI+类提供的方法,
3、而 这些方法又反过来相应地调用特定的设备驱动程序。GDI+将应用程序与图形硬件隔离,而正是 这种隔离允许开发人员创 建设备无关的应用程序。设备驱动程序计算机硬件GDI+ 引擎GDI二维矢量图形 图像 文字.NET 框架 Win32/64 (C+)GDI+的体系结构本节首先介绍 GDI+的几个主要新增的特性及其功能,然后说明它给 Windows 图形图像程序的开发模式带来的变化,最后 给出一个代码实例,介绍如何在 VC+中使用 GDI+进行程序开发。1GDI+的功能GDI+主要提供了以下三种功能:1)二维矢量图形矢量图形包括坐标系统中的系列点指定的绘图基元(如直线、曲线和图形)。例如,直线可通过
4、它的两个端点来指定,而矩形可通过确定其左上角位置的点并 给出其宽度和高度的一对数字来指定。简单路径可由通 过直线连接的点的数组 来指定。 贝塞尔样条是由四个控制点指定的复杂曲线。GDI+提供了存储基元自身相关信息的类(结构)、存储基元绘制方式相关信息的类,以及实际进行绘制的类。例如, Rectangle 结构存储矩形的位置和尺寸;Pen 类存储有关线条颜色、线条粗细和线型的信息;而 Graphics 类具有用于绘制直 线、矩形、路径和其它 图形的方法(类似于 GDI 中的 CDC 类)。 还有几种 Brush 类,它们存储有关如何使用颜色或图案来填充封闭图形和路径的信息。用户可以在图元文件中记
5、录矢量图像(图形命令的序列)。GDI+提供了 Metafile 类,可用于记录、显示和保存图元文件 。MetafileHeader 和 MetaHeader 类允许您检查图元文件头中存储的数据。2)图像处理某些种类的图片很难或者根本无法用矢量图形技术来显示。例如,工具栏按钮上的图片和显示为图标的图片就难以指定为直线和曲线的集合。 拥挤 的棒球运动场的高分辨率数字照片会更难以使用矢量技术来制作。 这种类型的图像可存 储为位图,即代表屏幕上 单个点颜色的数字数组。GDI+提供了 Image、Bitmap 和 Metafile 类,可用于 显示、操作和保存位图。它们支持众多的图像文件格式,还可以进
6、行多种图像处理的操作。3)文字显示版式就是使用各种字体、字号和样 式来显示文本。 GDI +为这种复杂任务提供了大量的支持。GDI+中的新功能之一是子像素消除锯齿,它可以使文本在 LCD 屏幕上呈现时显得比较平滑。4)功能汇总GDI+的 C+封装包含 54 个类、12 个函数、 6 类(226 个)图像常量、55 种枚举和 19 种结构。 GDI+的托管类接口则包含 大约 60 个类、 50 个枚举和 8 个结构。这两种封装中的Graphics 类都是 GDI+的核心功能,它是实际绘制直线、曲线、 图形、图像和文本的类。通过这些类和接口可以实现: 使用笔绘制线条和形状 使用刷填充形状 使用图像
7、、位图和图元文件 混合线条和填充 字体和文本 构造并绘制曲线 用颜色渐变的梯度刷填充形状 构造并绘制轨迹 变换 图形容器 区域 重新着色 读取元数据等非常丰富强大的功能。2GDI+新增特性1)渐变画刷渐变画刷(gradient brush 梯度刷)通过提供用于填充图形、路径和区域的线性渐变画笔和路径渐变画笔,GDI+扩展了 GDI 的功能。渐变画笔还可用于绘制直线、曲线和路径。线性渐变画笔可用于使用颜色来填充图形,画笔在 图形中移动时 ,颜色会逐渐改变。例如,假定通过指定图形左边为蓝色、右 边为绿色, 创建了一个水平渐变 画笔。当用水平渐变画笔填充该图形时,随着画笔从图形的左 边移至右边, 颜
8、色就会由蓝 色逐渐变为绿色。用类似方法定义的垂直渐变画笔填充的图形, 颜色从上到下变化。 图 6-1 显示了用水平渐变画笔填充的椭圆和用斜式渐变画笔填充的区域。图 6-1 水平和斜式渐变画笔用路径渐变画笔填充图形时,可 选择不同的方法来指定当从 图形的一部分至另一部分移动画笔时颜色的变化方式。一种 选择是指定中心颜色和 边缘颜色,在从 图形中间向外边缘移动画笔时,像素逐渐从一种 颜色变化到另一种颜色。 图 6-2 显示了用路径渐变画笔填充的路径(该路径是用一对贝塞尔样条创建的)。图 6-2 路径渐变画笔2)基数样条函数GDI+支持在 GDI 中不支持的基数样条(cardinal spines)
9、。基数样条是一连串单独的曲线,这些曲线连接起来形成一条较长的光滑曲线。样条由点的数 组指定,并通 过该数组中的每一个点。基数样条平滑地(没有锐 角)通过数组中的每一个点 ,因此,比通过连接直线创建的路径更光滑精准。图 6-3 显示了两个路径:一个以基数样条的形式创建;另一个通过连接直线创建。图 6-3 基数样条路径和折线路径3)持久路径对象在 GDI 中,路径属于设备上下文,并且会在绘制时被毁坏。利用 GDI +,绘图由Graphics 对象执行,可以创建并维护几个与 Graphics 对象分开的持久的路径对象(persistent path object) GraphicsPath 对象。绘
10、图操作不会破坏 GraphicsPath 对象,因此可以多次使用同一个 GraphicsPath 对象来绘制路径。4)变换和矩阵对象GDI+提供了 Matrix(矩阵) 对象,它是一种可以使(缩放、旋转和平移等)变换(transformation)简易灵活的强 大工具。矩阵对象一般与变换对象联合使用。例如,GraphicsPath 对象具有 Transform 方法,此方法接收 Matrix 对象作为参数。 单一的 33 矩阵可存储一种变换或一个变换序列。 图 6-4 显示了一个路径在执行两种变换前后的情况。图 6-4 路径的变换5)可伸缩区域GDI+ 通过对可伸 缩区域(Scalable R
11、egions)的支持极大地扩展了 GDI。在 GDI 中,区域被存储在设备坐标中,而且,可应用于区域的惟一变换是平移。而 GDI+在全局坐标中存储区域,并且允许区域发生任何可存 储在变换矩阵中的变换 (如缩放和旋转)。 图 6-5 显示一个区域在执行三种变换(缩放、旋 转和平移)前后的情况。图 6-5 区域的三种变换(缩放、旋转和平移)6) 混色在图 6-5 中,可以在变换区域(用蓝色阴影画笔填充)中看到未变换区域(用红色填充)。这是由 GDI+支持的 混色(Alpha Blending,透明混合)实现的。使用 混色,可以指定填充颜色的透明度。透明色与背景色相混合 填充色越透明,透出的背景色就
12、越多。 图 6-6显示四个用相同颜色(红色)填充、但透明 层次不同的椭圆 。图 6-6 不同透明度7)丰富的图像格式支持GDI+提供 Image、Bitmap 和 Metafile 类,可以用不同的格式加载、保存和操作图像。GDI+支持 BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF 共 9 种常见的图像格式。8)GDI+的不足虽然,相对于 GDI 来说,GDI+ 确实增加了许多新特性,而且功能更强大,使用也更方便。但是,这并不等于 GDI+ 就能够完全代替 GDI。因为 GDI+实际上 GDI+ 是 GDI 的封装和扩展,GDI+的 执行效率一般要低于 GDI
13、 的。另外,GDI+不支持图的位运算,那么就不能进行异或绘图等操作。而且在 VC 中, GDI+ 还不支持双缓存机制(如内存 DC 和显示 DC),这将大大影响 GDI+ 在高速图形、图像、动画和视频等方面的应用。3编程模式的改变GDI+的出现,也使基于 GDI 的编程模式产生了很大变化:GDI+ 用一个“无状态模式” ,取代了 GDI 中(需要先将各种工具和项目选入 DC 对象后,才能进行绘图的)“状态模式” 。主要体现在以下几个方面:1)DC 句柄和 图形对象设备上下文(DC = Device Context)是 GDI 中使用的一种结构,用于存 储与特定显示设备的功能、以及指定如何在该设
14、备 上绘制项目之属性相关的信息。用于屏幕显示的 DC 还与特定窗口相关联。为了使用 GDI API 进行绘图,必 须首先 获得一个 DC 的句柄(HDC = Handle to a DC),然后将该句柄作为参数,传递给实际进行绘图的 GDI 函数。 还可以将此句柄作为参数,传递给获取和设 置 DC 属性的 GDI 函数。使用 GDI+,不需要再(直接)使用句柄或设备上下文,而是只需(通过 HDC)创建一个Graphics 对象 ,然后用熟悉的面向对象方式来调用其中的各种 绘图方法,例如:myGraphicsObject.DrawLine(正如 DC 是 GDI 的核心,Graphics 对象也
15、位于 GDI+的核心。DC 和 Graphics 对象的作用相似,但在使用设备上下文( GDI)的基于句柄的编程模式和使用 Graphics 对象(GDI+)的面向对象的编程模型之间,存在一些基本的差异。Graphics 对象(像 DC 一样)与屏幕上的特定窗口关 联,并具有指定如何绘制项目的属性(如 SmoothingMode 和 TextRenderingHint)。但是,Graphics 对象不受笔、刷、路径、图像或字体的约束,这与设备上下文不同。例如,使用设备上下文绘制线条之前,必须先调用SelectObject 以使笔对象和 DC 关联,即将笔选入 DC 中。在设备上下文中绘制的所有
16、线条均使用该笔,直到选择另一支不同的笔 为止。在 GDI+中,将 Pen 对象作为参数传递给Graphics 类的 DrawLine 方法。可以在一系列的 DrawLine 调用的每个调用中,使用不同的Pen 对象,而不必将给定的 Pen 对象与 Graphics 对象关联 。2)绘制线条的两种方法下面每个示例都从点(20, 10)到点(200, 100)绘制一条宽为 3 的红色线条。第一个示例 调用 GDI,第二个示例则通过托管 类接口调用 GDI+;它们都有分别使用 API 和 MFC 的两个版本。(1)用 GDI 画线 API要使用 GDI 绘制线条,需要两个 对象:设备上下文和笔。在
17、WM_PAINT 的消息响应代码中,通过调用 BeginPaint,可以获得设备上下文句柄;通过调用 CreatePen,则可以获得笔句柄。 再调用 SelectObject 以将笔 选入设备上下文。 调用 MoveToEx,将笔的当前位置 设在(20, 10),然后 调用 LineTo,在笔的当前位置与位置 (200, 100) 之间绘制一条线条。请注意,所有这些函数和类型,都是全局的。而且 MoveToEx 和 LineTo 均将 hdc(设备上下文的句柄)作为参数接收。WM_PAINT:HDC hdc; / DC 句柄PAINTSTRUCT ps; / 点结构HPEN hPen; / 笔
18、句柄HPEN hPenOld; / 用于保存原笔的句柄hdc = BeginPaint (hWnd , /获得 DC 句柄,开始绘制,其中 hWnd 为窗口句柄hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0) ); / 创建红色画笔,宽 3hPenOld = SelectObject(hdc, hPen); / 选笔入 DCMoveToEx(hdc, 20, 10, NULL); / 最后一个参数是返回用的旧当前点的结构指针LineTo(hdc, 200, 100) ; / 画线SelectObject(hdc, hPenOld); / 选原笔入 DCD
19、eleteObject(hPen); / 删除创建的笔EndPaint(hWnd, / 绘制结束break; MFC利用 MFC 进行 GDI 绘图,步骤与 API 的差不多,只是 MFC 将各种 GDI 功能封装到了不同的类中。例如,笔的类为 CPen、点的 类为 CPoint、设备上下文的类为 CDC。而且 所有的绘图函数都被封在 CDC 类中,所以只能作 为其对象的成员函数才能被使用,当然也就不用再带 HDC 句柄作为输入参数了。void CGdipDemoView:OnDraw(CDC* pDC) CGdipDrawDoc* pDoc = GetDocument();ASSERT_VA
20、LID(pDoc);if (!pDoc) return;/ TODO: 在此处为本机数据添加绘制代码CPen pen(PS_SOLID, 3, RGB(255, 0, 0); / 创建红色画笔,宽 3pDC-SelectObject( / 选入 DC/pDC-SelectObject(new CPen(PS_SOLID, 3, RGB(255, 0, 0); /上两步可以合并pDC-MoveTo(20, 10); / 将当前点移到直线的起点pDC-LineTo(200, 100); / 画线(2)用 GDI+画 线 API使用 GDI+和托管类接口绘制线条,需要 Graphics 对象和 Pe
21、n 对象。绘制线条涉及调用Graphics 类的 DrawLine 方法。 DrawLine 方法的第一个参数是 Pen 对象。与前面 GDI 示例中显示的技术(将笔选入设备上下文)相比, 这是一个更加简单 而灵活的方案。WM_PAINT:HDC hdc;PAINTSTRUCT ps; / paint_struct 描绘结构Pen *myPen;Graphics *myGraphics;hdc = BeginPaint(hWnd, myPen = new Pen(0xffff0000, 3); / 创建一个笔,宽 3,红色myGraphics = new Graphics(hdc); / 利用
22、 DC 句柄创建图形对象myGraphics-DrawLine(myPen, 20, 10, 200, 100); / 调用图形对象的画线方法EndPaint(hWnd, break; / 必须在 退出时 delete 之. MFC利用 MFC 进行 GDI+绘图,步骤与 API 的差不多。只是代码改在 OnDraw 函数中,而且获取 DC 句柄的方法不同。void CGdipDemoView:OnDraw(CDC* pDC) CGdipDrawDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);if (!pDoc) return;/ TODO: 在此处为
23、本机数据添加绘制代码Graphics myGraphics(pDC-m_hDC); / 利用 DC 句柄创建图形对象Pen myPen(Color(255, 0 , 0), 3); / 创建一个笔, 宽 3,红色myGraphics.DrawLine( / 调用图形对象的画线方法/ 上两步也可以合并:/ myGraphics.DrawLine(3)作为参数的笔、刷、路径、图像和字体前面的示例显示:创建和维护 Pen 对象可以与提供绘制方法的 Graphics 对象分开。 创建和维护 Brush、GraphicsPath、Image 和 Font 对象也可以与 Graphics 对象分开,Gra
24、phics 类提供的许多绘制方法都将这些对象作为参数接收。例如,Brush 对象作为参数传递 至 FillRectangle 方法,GraphicPath 对象作为参数传递至DrawPath 方法。同 样,Image 和 Font 对象传递至 DrawImage 和 DrawString 方法。这与 GDI不同,在 GDI 中,需要将笔、刷、路径、图像或字体选入 DC,然后将 DC 的句柄作为参数传递至绘制函数或采用 CDC 类对象的函数来绘图。4)方法重载许多 GDI+方法都是重载的,即,若干方法共享同一名称,却有不同的参数列表。这一点与用 MFC 封装后的 GDI 类似,但是 GDI+中的
25、重载方法要更多一些。 (注意,在 .NET、C#、Java 和 VB 中,都把类的成员函数称为方法。当我们在 C+中,使用 .NET 框架类库中的类和功能时,也常常将其成 员函数改称为方法。 )例如,画线的重载方法有:Status DrawLine(const Pen* pen, REAL x1, REAL y1, REAL x2, REAL y2);Status DrawLine(const Pen* pen, const PointFStatus DrawLine(const Pen* pen, INT x1, INT y1, INT x2, INT y2);Status DrawLine
26、(const Pen* pen, const Point其中,typedef int INT; class Point public: INT X; INT Y; ;typedef float REAL; class PointF public: REAL X; REAL Y; ;5)无当前位置前面所述的 DrawLine 方法中显示:线条的起点和终点均被作为参数接收。这与 GDI 方案不同,在 GDI 中,调用 MoveToEx(hdc, x1, y1, NULL)或 pDC-MoveTo(x1, y1)来设置当前笔位置之后,再调用 LineTo (hdc , x2 , y2)或 pDC-L
27、ineTo(x2, y2)来绘制一条从(x1, y1) 到(x2 , y2) 的线条。 GDI+从总体上已经放弃了当前位置的概念。6)绘制和填充的不同方法论及绘制轮廓和填充图形内部时,GDI+要比 GDI 更灵活。GDI 有一个 Rectangle 函数,可一步完成绘制轮廓和填充矩形内部。 轮廓由当前选定的笔 绘制,而内部 则由当前选定的刷来填充。GDI+使用不同的方法来绘制轮廓和填充矩形内部。Graphics 类的 DrawRectangle 方法将 Pen 对象作为其参数之一,而 FillRectangle 方法将 Brush 对象作为其参数之一。7)构造区域GDI 提供几种用于创建区域的
28、函数(在 MFC 中,它们被封装在 CRng 类里):CreateRect- Rgn、CreateEllpticRgn、CreateRoundRectRgn、CreatePolygonRgn 和 CreatePolyPolygonRgn。您或许希望 GDI+中的 Region 类也有类似的构造函数,将矩形、椭圆、 圆角矩形和多边形作为参数接收,但事实并非如此。GDI+中的 Region 类提供一个接收 Rectangle 对象的构造函数和另一个接收 GraphicsPath 对象的构造函数。 如果想基于 椭圆、圆角矩形或多边形构造区域,可以通过创建一个 GraphicsPath 对象(可包含椭
29、圆的对象等),然后将其传递至 Region构造函数来轻松实现。GDI+通过组合图形和路径,使得构成复杂区域十分简单。Region 类具有 Union 和Intersect 方法,可用于 扩展具有路径的现有区域或其它区域。GDI+方案一个很好的功能就是 GraphicsPath 对象在作为参数传递至 Region 构造函数时 不会被破坏(在 GDI 中,可以使用 PathToRegion 函数将路径转换为区域,但在此 过程中,路径将被破坏)。另外,GraphicsPath 对象在作为参数传递给 Union 或 Intersect 方法时也不会被破坏,因此,在一些单独的区域中,可以将给定的路径作
30、为构造块使用。例如:Region region1(rect1);Region region2(rect2);region1.Union(onePath);region2.Intersect(onePath);4GDI+的使用下面通过一个简单的例子,来 说明如何使用 GDI+进行应用程序开发。1)GDI+开 发包若采用的是 Visual C+ 2005,则已经包含了开发 GDI+应用程序所需的所有东西。包括: 动态链接库文件 gdiplus.dll 静态链接库文件 gdiplus.lib 代码中所需要的头文件 gdiplus*.h 帮助文档 gdicpp.chm 和 gdicpp.chi如果你使
31、用的操作系统是 Windows XP 或 Windows Server 2003,则 GDI+所对应的动态链接库,已经被包含在其中。gdiplus.dll 一般位于操作系统 的 WinSxS(Windows side-by-side assembly,视窗并行程序集 )目 录中,例如:C:WINDOWSWinSxSx86_Microsoft.Windows.GdiPlus_6595b64144ccf1df_1.0.0.0_x-ww_8d353f13gdiplus.dll(1661KB,2002.10.8)C:WINDOWSWinSxSx86_Microsoft.Windows.GdiPlus_
32、6595b64144ccf1df_1.0.2600.2180_x-ww_522f9f82gdiplus.dll(1672KB,2004.8.4)而 GDI 的动态链接库 gdi32.dll,却一般在操作系统的 32 位系统目录中:F:WINDOWSsystem32gdi32.dll(272KB,2004.8.4)如果开发工具采用 Visual C+ 6.0,而且操作系统是 Windows 2000,则需要安装 GDI+的开发包。如果你已经安装了 .NET 框架, 则其中已经包含了 该开发包。如果 还没有安装,则需要自己去微软的网站免费下载 GDI+开发包或.NET 框架(可能需要先通 过微软的
33、正版操作系统软件验证)。2)VC 中的 设置 在 VS05 中, 选“ 项目/*属性”菜单项,打开 项目的属性 页窗口,先选“所有配置”,再选“配置属性/ 链 接器/输入” 项 ,在右 边上部的“附加依赖项”栏的右边, 键入GdiPlus.lib(参见下图)后按“应用” 钮,最后按“确定” 钮关闭对话 框。/ 或使用#pragma comment(lib , “gdiplus.lib“); / 陈宝楷 在要使用 GDI+的文件(如视图类的头文件或代码文件) 头部包含 GDI+的头文件:#include 并加上使用 GDI+命名空间的 using 指令(区分大小写,注意首字母大写):using
34、namespace Gdiplus;下面是 VC05 中,GDI+头文件和动态链接库文件,缺省所在的目录:C:Program FilesMicrosoft Visual Studio 8VCPlatformSDKincludeGdiPlus*.hC:Program FilesMicrosoft Visual Studio 8VCPlatformSDKLibGdiPlus.lib下面是 VC05 中,GDI 头文件和 动态链接库文件,缺省所在的目录:C:Program FilesMicrosoft Visual Studio 8VCPlatformSDKincludeWinGDI.h(API)C
35、:Program FilesMicrosoft Visual Studio 8VCatlmfcincludeafxwin.h(MFC)C:Program FilesMicrosoft Visual Studio 8VCPlatformSDKLibGdi32.lib3)存在的问题另外,VC05 与 GDI+存在一些问题,例如:(1)重画问题GDI+程序往往在窗口被创建时,不能自动重画(没有自动调用 OnDraw 函数)。解决办法是,在创建图形对象后,自己调用视图类(基类 CWnd)的成员函数 RedrawWindow:BOOL RedrawWindow(LPCRECT lpRectUpdate
36、= NULL, CRgn* prgnUpdate = NULL, UINT flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); 其中,lpRectUpdate 为窗口客户区中需要重画的矩形(NULL 表示整个客户区矩形重画)、prgnUpdate 表示需要重画的区域(NULL 表示整个客户区矩形区域重画)、flags 为特征标志(RDW_INVALIDATE 指定范 围无效、 RDW_UPDATENOW 立即更新、 RDW_ERASE 擦除背景)。例如:Graphics graph(pDC-m_hDC);RedrawWindow(); /
37、一般输入参数取缺省值即可/ 相当于 Invalidate(); UpdateWindow();的综合效果 / this-Invalidate();注意:不能在 OnDraw 和 OnPaint 函数中调用 RedrawWindow,那 样会造成反复调用,产生死循环。其实,只要 GDI+的两个初始化语句放置的位置对(必须放在 CWinApp: InitInstance ();语句之前,参见下面 4)中的说 明),就不会出 现该问题。(2)new 问题不能使用 new 来动态创建 GDI+对象。解决办法是(我摸索出的,不一定最好),打开(缺省)位于 C:Program FilesMicrosoft
38、 Visual Studio 8VCPlatformSDKInclude 目录中的GdiplusBase.h 头文件,并注释 掉里面 GdiplusBase 类的内容(该类其实只含 new、new、delete 和 delete这四个运算符的重载), 使其成为一个空类 (不要删除整个类)。对实验室中的写保护机器,不能修改安装目 录中的 GdiplusBase.h 头文件,解决办法是: 将该头文件复制到你的项目目录中; 注释掉该头文件里面 GdiplusBase 类的内容(保留类定义); 在你项目中所有的#include 语句之前,包含“GdiplusBase.h“头文件,形如:#include
39、 “gdiplusBase.h“#include 则编译系统会优先包含项目目录中的 gdiplusBase.h 头文件,从而屏蔽掉原来位于平台SDK 的 Include 目录中的同名 头文件。 / 技巧你也可以在有些使用 new 的地方改用 / 在 C#可运行改为Pen *pPen = / 切记之 Pen pen = Pen();又例如,你也可以将代码:graphics.DrawPolygon(new Pen(Color:Green), points, n);改为Pen pen(Color:Green);graphics.DrawPolygon(或直接改为graphics.DrawPolygo
40、n((3)调试问题因为现在版本的 VC05 存在许多 Bug,特 别是 GDI+程序在调试时的问题就更多。解决办法是: 在编译运行时,不使用 Debug 配置,而改用 Release 配置; 运行时不使用调试运行(F5),而改用不调试直接运行(Ctrl +F5 ); 最好是用静态链接的 MFC 库,而不用 DLL 动态库。常用的调试方法有: 使用 MessageBox 信息框: 在视图类中的常用格式为MessageBox(L“提示信息“); 在应用程序类和文档类中的常用格式为MessageBox(NULL, L“提示信息“, L“标题“, MB_OK); / Win32 API 设置断点,然后
41、逐步运行(F10)或 F11。 运行当前位置,然后逐步运行(F10) 利用调试界面中的“局部变量”和“ 监视 1”等窗口,来 查看变量当前的值4)用 MFC 开发 GDI+程序创建一个名为 GDIPlusDemo 的 MFC 单文档应用程序项目。首先要进行 GDI+系统的初始化,这需要在应用程序类 CGDIPlusDemoApp 中声明一个成员变量:ULONG_PTR m_gdiplusToken; / ULONG PTR 为 int64 类型【注:如果编译出现ULONG_PTR : undeclared identifier 错误, 则可在 StdAfx.h 中加入如入语句:#ifndef
42、ULONG_PTR #define ULONG_PTR unsigned long* #endif】/ By ZWL 2010-8-23并在该类的初始化函数 CGDIPlusDemoApp:InitInstance() 中加入以下代码来对 GDI+进行初始化:GdiplusStartupInput gdiplusStartupInput;GdiplusStartup(注意:这两个语句必须加在应用程序类的 InitInstance 函数中的CWinApp: InitInstance (); / 陈宝楷:测试过,但没有问题?语句之前,不然以后会造成视图 窗口不能自动重画、程序中不能使用字体等等一系
43、列问题。还要在 CGDIPlusDemoApp:ExitInstance() 函数中加入以下代 码来关闭 GDI +:GdiplusShutdown(m_gdiplusToken);上面的 InitInstance 和 ExitInstance 都是应用程序类的重写型成 员函数。而且,缺省时无ExitInstance,需要自己利用 属性窗口来添加(不要手工添加)。接下来就可以利用 GDI+进行绘图了。在 OnDraw 函数中画图:CGDIPlusDemoView:OnDraw (CDC* pDC) Graphics graph(pDC-m_hDC); / 创建图形对象Pen bluePen(C
44、olor(255, 0, 0, 255); / 创建蓝色笔Pen redPen(Color(255, 255, 0, 0); / 创建红色笔int y = 255; / y 的初值for (int x = 0; x nativeMatrix = nativeMatrix;而封装类中的克隆方法 Clone,不接受参数,而经常是传递两个参数到底层的 GDI+平面 API 函数。Matrix:Clone 传递 nativeMatrix(作为输入参数)和 GpMatrix 指针变量的地址(作为输出参数)给平面 API 的 GdipCloneMatrix 函数:GpStatus WINGDIPAPI G
45、dipCloneMatrix(GpMatrix *matrix, GpMatrix *cloneMatrix);例如: / 对象* Clone() const Matrix *Clone() const GpMatrix *cloneMatrix = NULL;GdipCloneMatrix(nativeMatrix, return new Matrix(cloneMatrix);该平面 API 函数返回一个 GpStatus 类型的值。在 GdiplusGpStubs.h 中,枚举类型 GpStatus被定义为与枚举类型 Status 等价:typedef Status GpStatus;在
46、封装类中,大多数方法都返回一个状态值,指出方法是否成功。但是也有一些方法用布尔变量来返回状态。例如, Matrix 类的 IsInvertible 方法:BOOL IsInvertible() const BOOL result = FALSE;GdipIsMatrixInvertible(nativeMatrix, return result;其中,平面 API 函数 GdipIsMatrixInvertible 的函数原型为 :GpStatus WINGDIPAPI GdipIsMatrixInvertible(GDIPCONST GpMatrix *matrix, BOOL *resul
47、t);另一个封装类是 Color,它只有一个(被定义为 DWORD 的)ARGB 类型的字段。当你传递一个 Color 对象到某个封装方法 时, 该方法也会将 ARGB 字段传到底层的 GDI+平面 API函数。例如 Pen:SetColor:Status SetColor(const ColorColor:GetValue 方法返回 ARGB 字段的值。其中,平面 API 函数 GdipSetPenColor 的函数原型为:GpStatus WINGDIPAPI GdipSetPenColor(GpPen *pen, ARGB argb);6.2 GDI+的 MFC 编程1基础封装在 GDI
48、+ API 中的各种 C+类、函数、常量、枚举和结构,都被定义在 Gdiplus.h 头文件所包含的一系列头文件中。所以,采用 MFC 进行 GDI+编程,必须包含 Gdiplus.h 头文件。由上节最后一小小节“56)GDI+ 平面 API”中的讨论可知,封装在 GDI+类中方法,最后都需要调用 GDI+平面 API 中的相关底 层函数,才能完成 实际 的操作。所以, 为了运行 GDI+应用程序,在操作系统平台中,必须安装动态链接库 Gdiplus.dll。该动态链接库所对应的静态库文件为 GdiPlus.lib,而且它不是 C+和 MFC 的缺省链接库。所以,必须在项目设置,添加该库作为链
49、接器输入的附加依赖项。因为在 Gdiplus.h 头文件中,将所有的 GDI+的类、函数、常量、枚举和结构等都定义在了命名空间 Gdiplus 中。所以,一般在 GDI+程序中,都必须使用如下的命名空间声明:using namespace Gdiplus;例如:#include #pragma comment(lib, “Gdiplus.lib”); / 陈宝楷using namespace Gdiplus;1)GdiPlus.h/* Copyright (c) 1998-2001, Microsoft Corp. All Rights Reserved.* Module Name:* Gdiplus