💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## 1.3 GDI+的 MFC 编程 本节介绍利用 MFC 进行 GDI+编程的必要的准备,并通过例子说明 GDI+编程的具体步 骤,最后给出如何解决存在的 new 操作符问题的方法。 C++封装的 GDI+的(英文)帮助内容,位于 VS08 的“目录/Win32 和 COM 开发/Graphics and Multimedia/GDI+”,主要的参考资料位于其子目录“GDI+ Reference”中。 ### 1.3.1 设置与初始化 封装了 GDI+ API 的各种 C++类、函数、常量、枚举和结构,都被定义在 Gdiplus.h 头 文件所包含的一系列头文件中。所以,采用 MFC 进行 GDI+编程,必须包含 Gdiplus.h 头文 件。 从 14.1.2 的有关 GDI+平面 API 的讨论可知,封装在 GDI+类中方法,最后都需要调用GDI+平面 API 中的相关底层函数,才能完成实际的操作。所以,为了运行 GDI+应用程序,在操作系统平台中,必须安装动态链接库 Gdiplus.dll。对 Windows XP 及以上版本,该 DLL已经自动被操作系统包含。 该动态链接库所对应的静态库文件为 GdiPlus.lib,而且它在 VC08 及之前的早期版本中 不是 C++和 MFC 的默认链接库。所以,对早期的 VC 版本必须在项目设置,添加该库作为 链接器输入的附加依赖项。但是对 VC08 SP1 及 VC10,该库已经成为标准链接库之一,不 必再为链接器输入的附加依赖项添加此库。 因为在 Gdiplus.h 头文件中,将所有的 GDI+的类、函数、常量、枚举和结构等都定义在 了命名空间 Gdiplus 中。所以,一般在 GDI+程序中,都应该使用如下的命名空间声明: ``` using namespace Gdiplus; ``` (1)VC 中的设置 为了在 MFC 应用程序中能使用 GDI+,必须包含 GDI+头文件、使用 GDI+命名空间。 对 VC08 及之前的版本,还要为项目添加 GDI+链接库。 1) 包含头文件、使用命名空间——在要使用 GDI+的文件(如视图类的头文件或代码 文件)头部包含 GDI+的头文件: ``` #include &lt;gdiplus.h&gt; ``` 并加上使用 GDI+命名空间的 using 指令(区分大小写,注意首字母大写): ``` using namespace Gdiplus; ``` 2) 添加链接库(对 VC08 SP1 及 VC10 不必添加)——在 VS08 及其早期版本中,选 “项目/*属性”菜单项,打开项目的属性页窗口,先选“所有配置”,再选“配置 属性/链接器/输入”项,在右边上部的“附加依赖项”栏的右边,键入 GdiPlus.lib(参见图 14-9)后按“应用”钮,最后按“确定”钮关闭对话框。 (2)GDI+的初始化与清除 为了在 MFC 应用程序中使用采用 C++封装的 GDI+ API,必须在 MFC 项目的应用程序 类 中 , 调 用 GDI+ 命 名 空 间 中 的 GDI+ 启 动 函 数 GdiplusStartup 和 GDI+ 关 闭 函 数 GdiplusShutdown,来对 GDI+进行初始化(装入动态链接库 Gdiplus.dll,或锁定标志+1)和 清除(卸载动态链接库 Gdiplus.dll,或锁定标志-1)工作。它们一般分别在应用程序类的InitInstance 和 ExitInstance 重载方法中调用。 ![image](https://box.kancloud.cn/2016-04-18_57144a7d82d90.jpg) 图 14-9 在项目属性对话框中添加静态链接库 函数 GdiplusStartup 和 GdiplusShutdown,都被定义在 GdiplusInit.h 头文件中: ``` Status WINAPI GdiplusStartup( OUT ULONG_PTR *token, const GdiplusStartupInput *input, OUT GdiplusStartupOutput *output); void GdiplusShutdown(ULONG_PTR token); ``` 其中: 类型 ULONG_PTR,是用无符号长整数表示的指针,被定义在 basetsd.h 头文件中: ``` typedef _W64 unsigned long ULONG_PTR; ``` 输出参数 token(权标),供关闭 GDI+的函数使用,所以必须设置为应用程序类的 成员变量(或全局变量,不提倡)。 结构 GdiplusStartupInput 和 GdiplusStartupOutput,都被定义在 GdiplusInit.h 头文件中。 GDI+启动输入结构指针参数 input,一般取默认构造值即可,即(设:无调 试事件回调过程、不抑制背景线程、不抑制外部编解码): ``` input = GdiplusStartupInput(NULL, FALSE, FALSE); ``` GDI+启动输出结构指针参数 output,一般不需要,取为 NULL 即可。 注意,采用 MFC 进行 GDI+ API 编程时,在使用任何 GDI+的功能调用之前,必须先调用 GDI+启动函数 GdiplusStartup 来进行初始化 GDI+的工作;在完成所有的 GDI+功能调用 之后,必须调用 GDI+关闭函数 GdiplusShutdown 来进行清除 GDI+的工作。 (3)过程框图 图 14-10 是使用 MFC 进行 GDI+编程的设置、准备与初始化过程的逻辑框图。 ![](https://box.kancloud.cn/2016-04-18_57144a7d98257.png) 图 14-10 GDI+的设置、准备与初始化 ### 1.3.2 编程例子 下面通过一个简单的例子,来说明如何使用 GDI+进行应用程序开发。 (1)创建和设置 创建一个名为 Gdip 的传统界面 MFC 单文档应用程序项目,在应用程序类和视图类的CPP 代码文件中,包含头文件并使用命名空间: ``` #include <gdiplus.h> using namespace Gdiplus; ``` 对 VC08 及之前的版本还需在项目属性中添加链接库 GdiPlus.lib。 (2)初始化与清除 然后再进行 GDI+系统的初始化,这需要在应用程序类 CGdipApp 中声明一个成员变量: ``` ULONG_PTR m_gdiplusToken; // ULONG PTR 为 int64 类型 并在该类的初始化函数 CGdipApp::InitInstance()中加入以下代码来对 GDI+进行初始化: GdiplusStartupInput gdiplusStartupInput; GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL); ``` 注意:这两个语句必须加在应用程序类的 InitInstance 函数中的 ``` CWinApp::InitInstance(); ``` 语句之前,不然以后会造成视图窗口不能自动重画、程序中不能使用字体等等一系列问题。 还要在 CGdipApp::ExitInstance()中加入以下代码来关闭 GDI+: ``` GdiplusShutdown(m_gdiplusToken); ``` 上面的 InitInstance 和 ExitInstance 都是应用程序类的重写型方法。而且,默认时 VC08 SP1 及其以前版本是不会自动生成 ExitInstance 方法代码的(不过 VC10 会自动生成此方法),需 要自己利用属性窗口来添加(不要手工添加)。 (3)绘图 接下来就可以利用 GDI+进行绘图了。下面的代码段是在 OnDraw 函数中画一个带网格 的透明度连续变化的图: ``` CGdipView::OnDraw(CDC* pDC) { …… Graphics graph(pDC-&gt;m_hDC); // 创建图形对象 Pen bluePen(Color(0, 0, 255)); // 创建蓝色笔 Pen redPen(Color(255, 0, 0)); // 创建红色笔 int y = 255; // y 的初值 for (int x = 0; x &lt; 256; x += 5) { // 绘制红蓝网线 graph.DrawLine(&bluePen, 0, y, x, 0); graph.DrawLine(&redPen, 255, x, y, 255); y -= 5; } // 画一组绿色透明度垂直渐变的水平线(填满正方形) for (y = 0; y &lt; 256; y++) { Pen pen(Color(y, 0, 255, 0)); // α 随 y 变的绿色笔 graph.DrawLine(&pen, 0, y, 255, y); } // 画一组品红色透明度水平渐变的垂直线(填满扁矩形) for (int x = 0; x &lt; 256; x++) { Pen pen(Color(x, 255, 0, 255)); // α 随 x 变的品红色笔 graph.DrawLine(&pen, x, 100, x, 200); } } ``` 运行的结果如图 14-11 所示。其中,左图为第一个循环所绘制的结果、中图为前两个循 环所绘制的结果、右图为全部三个循环所绘制的结果。 ![image](https://box.kancloud.cn/2016-04-18_57144a7db42f7.jpg) ![image](https://box.kancloud.cn/2016-04-18_57144a7dc809d.jpg) ![image](https://box.kancloud.cn/2016-04-18_57144a7dd93bb.jpg) 图 14-11 透明度的连续变化 ### 1.3.3 new 问题 在 VC08(包括 SP1)中使用 GDI+时,不能用 new 来动态创建 GDI+对象。解决办法有 如下两种: (1)修改 GdiplusBase 类 打开(默认)位于“C:\Program Files\Microsoft SDKs\Windows\v6.0A\ Include\”目录中 的 Gdiplus Base.h 头文件,并注释掉里面 Gdiplus Base 类的内容(该类其实只含 new、new[]、 delete 和 delete[]这四个运算符的重载),使其成为一个空类(但不要删除整个类)。 为了不修改原始安装目录中的 Gdiplus Base.h 头文件,可以: + 将该头文件复制到你的项目目录中。 + 注释掉该头文件里面 Gdiplus Base 类的内容(保留类定义)。 + 在项目中所有的#include <gdiplus.h>语句之前,包含"Gdiplus Base.h"头文件,形如: ``` #include "gdiplusBase.h" #include &lt;gdiplus.h&gt; ``` + 则编译系统会优先包含项目目录中的 gdiplus Base.h 头文件,从而屏蔽掉原来位于 平台 SDK 的 Include 目录中的同名头文件。 (2)用&代替 new 也可以在有些使用 new 的地方改用&,例如将代码 Pen *pPen = **new** Pen(Color::Red); 改 为 Pen *pPen = **&**Pen(Color::Red);。