最新推荐
基于 Visual C++.NET 的
GDI+开发教程
基于 Visual C++.NET 的 GDI+开发教程
以前开发人员可以通过 GDI(Graphics Device
Interface)在 Windows 窗口中绘制图形、文本和图像。
但 GDI 的图像处理能力却非常欠缺,位图超过 256 色
就会失真或不能显示,因此开发人员在焦急的等待新一
代的图形处理工具。
在漫长的等待后,我们终于等来了 GDI+。GDI+
是 GDI 的新版本,它在 GDI 的基础上添加了许多新特
性,为开发人员提供了处理二维矢量图形、文本、图像
以及图形数据矩阵的一系列 API 接口。
本文从实际应用出发,着重讨论了 GDI+和
CImage 类的一般使用方法以及在图像处理等方面的
使用方法。
⊙ 使用 CIMAGE 类
Visual C++的 CBitmap 类和静态图片控件的功能是比较
弱的,它只能显示出在资源中的图标、位图、光标以及图元
文件的内容,而不像 VB 中的 Image 控件可以显示出绝大多
数的外部图像文件(BMP、GIF、JPEG 等)。因此,想要在对
话框或其他窗口中显示外部图像文件则只能借助于第三方提
供的控件或代码。现在,MFC 和 ATL 共享的新类 CImage
为图像处理提供了许多相应的方法,这使得 Visual C++在图
像方面的缺憾一去不复返了。
全文阅读
⊙ 相关专题
Visual C++.NET 编程基础讲座
Visual C++ .Net 编
程实战
网友留言
本文章的留言内容:
1
⊙ GDI+慨述
在这一节中首先介绍一下 GDI+的新特性以及其编程方式的改变,然后介绍用
Visual C++.NET 在基于对话框和单文档/多文档等应用程序中使用 GDI+的一般方
法。
全文阅读
⊙ GDI+绘图基础
GDI+提供从简单到复杂图形绘制的大量方法,并且我们可以通过对路径和区
域的操作构造出更复杂的图形,这在 CAD 等场合极为有用。当然,在绘图之前我
们有必要搞清一些基本内容,如坐标空间、画笔和画刷等。
全文阅读
⊙ 字体和文本绘制
点击这里留言
字体是文字显示和打印的外观形式,它包括了文字的字样、风格和尺寸等多方
面的属性。适当地选用不同的字体,可以大大地丰富文字的外在表现力。例如,把
文字中某些重要的字句用较粗的字体显示,能够体现出突出、强调的意图。当然,
文本输出时还可使用其格式化属性和显示质量来优化文本显示的效果。
全文阅读
⊙ 图像处理
在以往的图像处理中,常常要根据不同图像文件的格式及其数据存储结构在不
同格式中进行转换。某个图像文件的显示也是依靠对文件数据结构的剖析,然后读
取相关图像数据而实现的。现在,GDI+提供了 Image 和 Bitmap 类使我们能轻松
容易地处理图像。
全文阅读
GDI+接口是 Microsoft Whistler 操作系统中的一部分,它是 GDI 的一个新版本,不仅在 GDI
基础上添加许多新特性而且对原有的 GDI 功能进行优化。在为开发人员提供的二维矢量图
形、文本、图像处理、区域、路径以及图形数据矩阵等方面构造了一系列相关的类,如
Bitmap(位图类)、Brush(画刷类)、Color(颜色类)、Font(字体类)、Graphics(图形类)、Image(图
像类)、Pen(画笔类)和 Region(区域类)等。其中,图形类 Graphics 是 GDI+接口中的一个核
心类,许多绘图操作都可用它来完成。
我们首先介绍一下 GDI+的新特性以及其编程方式的改变,然后介绍用 Visual C++.NET
在基于对话框和单文档/多文档等应用程序中使用 GDI+的一般方法。
GDI+新特性
GDI+与 GDI 相比,增加了下列新的特性:
1、渐变画刷
以往 GDI 实现颜色渐变区域的方法是通过使用不同颜色的线条来填充一个裁剪区域而
达到的。现在 GDI+拓展了 GDI 功能,提供线型渐变和路径渐变画刷来填充一个图形、路径
和区域,甚至也可用来绘制直线、曲线等。这里的路径可以视为由各种绘图函数产生的轨迹。
2
2、样条曲线
对于曲线而言,最具实际意义的莫过于样条曲线。样条曲线是在生产实践的基础上产生
和发展起来的。模线间的设计人员在绘制模线时,先按给定的数据将型值点准确地"点"到图
板上。然后,采用一种称为"样条"的工具(一根富有弹性的有机玻璃条或木条),用压铁强迫
它通过这些型值点,再适当调整这些压铁,让样条的形态发生变化,直至取得合适的形状,
才沿着样条画出所需的曲线。如果我们把样条看成弹性细梁,那么压铁就可看成作用在这梁
上的某些点上的集中力。GDI+的 Graphics:: DrawCurve 函数中就有一个这样的参数用来调
整集中力的大小。除了样条曲线外,GDI+还支持原来 GDI 中的 Bezier 曲线。
3、持久的路径对象
我们知道,在 GDI 中,路径是隶属于一个设备环境(上下文),也就是说一旦设备环境指
针超过它的有效期,路径也会被删除。而 GDI+是使用 Graphics 对象来进行绘图操作,并
将路径操作从 Graphics 对象分离出来,提供一个 GraphicsPath 类供用户使用。这就是说,
我们不必担心路径对象会受到 Graphics 对象操作的影响,从而可以使用同一个路径对象进
行多次的路径绘制操作。
4、矩阵和矩阵变换
在图形处理过程中常需要对其几何信息进行变换以便产生复杂的新图形,矩阵是这种图
形几何变换最常用的方法。为了满足人们对图形变换的需求,GDI+提供了功能强大的 Matrix
类来实现矩阵的旋转、错切、平移、比例等变换操作,并且 GDI+还支持 Graphics 图形和
区域(Region)的矩阵变换。
5、Alpha 混色
在图像处理中,Alpha 用来衡量一个像素或图像的透明度。在非压缩的 32 位 RGB 图
像中,每个像素是由四个部分组成:一个 Alpha 通道和三个颜色分量(R、G 和 B)。当 Alpha
值为 0 时,该像素是完全透明的,而当 Alpha 值为 255 时,则该像素是完全不透明。
Alpha 混色是将源像素和背景像素的颜色进行混合,最终显示的颜色取决于其 RGB 颜
色分量和 Alpha 值。它们之间的关系可用下列公式来表示:
显示颜色 = 源像素颜色 X alpha / 255 + 背景颜色 X (255 - alpha) / 255
GDI+的 Color 类定义了 ARGB 颜色数据类型,从而可以通过调整 Alpha 值来改变线条、
图像等与背景色混合后的实际效果。
除了上述新特性外,GDI+还将支持重新着色、色彩修正、消除走样、元数据以及 Graphics
容器等特性。
GDI+编程模块的变化
为了简化 GDI+的编程开发过程,Microsoft 对 GDI+的编程模块作了一些调整,这主要
体现在以下几个方面:
3
1、不再使用设备环境或句柄
我们知道,在使用 GDI 绘图时,必须要指定一个设备环境(DC)。MFC 为设备环境提供
了许多由基类 CDC 派生的设备环境类,如 CPaintDC、CClientDC 和 CWindowDC 等,用
来将某个窗口或设备与设备环境类的句柄指针关联起来,所有的绘图操作都与该句柄有关。
而 GDI+不再使用这个设备环境或句柄,取而代之是使用 Graphics 对象。
与设备环境相类似,Graphics 对象也是将屏幕的某一个窗口与之相关联,并包含绘图
操作所需要的相关属性。但是,只有这个 Graphics 对象与设备环境句柄还存在着联系,其
余的如 Pen、Brush、Image 和 Font 等对象均不再使用设备环境。
2、绘图方式的变化
先来看看同样绘制一条从点(20, 10)到点(200, 100)直线的 GDI 和 GDI+代码,假设这些
代码都是添加在 OnDraw 函数中。
GDI 绘制该直线的代码如下:
void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CPen newPen( PS_SOLID, 3, RGB(255, 0, 0) );
CPen* pOldPen = pDC->SelectObject( &newPen );
pDC->MoveTo( 20, 10 );
pDC->LineTo( 200, 100);
pDC->SelectObject( pOldPen );
}
GDI+绘制该直线的代码如下:
void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
using namespace Gdiplus; // 使用名称空间
Graphics graphics( pDC->m_hDC );
Pen newPen( Color( 255, 0, 0 ), 3 );
graphics.DrawLine(&newPen, 20, 10, 200, 100);
}
从上面代码可以看出,GDI 先创建一个 CPen(画笔)对象,然后通过 SelectObject 将该
画笔选入到设备环境(pDC)中。接下来调用相应的画线函数,最后恢复设备环境中原来的
4
GDI 对象。而 GDI+是先使用 Graphics 类创建一个与 pDC 设备环境相关联的 Graphics 对
象,然后使用 Pen 类进行画笔的创建,最后调用相应的画线方法。由于 Pen 和设备环境是
相互独立的,因而不需要像 GDI 那样恢复设备环境中原来的设置,而且 Pen 和 Graphics
对象的创建不存在先后次序。
2、Graphics 绘图方法直接将 Pen、Brush 等对象作为自己的参数
从上面的代码可以看出,Graphics 绘图方法直接将 Pen 对象作为自己的参数,从而避
免了在 GDI 使用 SelectObject 进行繁琐的切换,类似的还有 Brush、Path、Image 和 Font
等。
3、不再使用"当前位置"
我们知道,GDI 绘图操作(如画线)中总存在一个被称为"当前位置"的特殊位置。每次画
线都是以此当前位置为起始点,画线操作结束之后,直线的结束点位置又成为了当前位置。
设置当前位置的理由是为了提高画线操作的效率,因为在一些场合下,总是一条直线连着另
一条直线,首尾相接。有了当前位置的自动更新,就可避免每次画线时都要给出两点的坐标。
尽管有其必要性,但是单独绘制一条直线的场合总是比较多的,因此 GDI+取消这个"当前位
置"以避免当无法确定"当前位置"时所造成的绘图的差错,取而代之的是直接在 DrawLine 中
指定直线起止点的坐标。
4、形状轮廓绘制和填充采用不同的方法
GDI 总是让形状轮廓绘制和填充使用同一个绘图函数,例如 Rectangle。我们知道,轮
廓绘制需要一个画笔,而填充一个区域需要一个画刷。也就是说,不管我们是否需要填充所
绘制的形状,我们都需要指定一个画刷,否则 GDI 采用默认的画刷进行填充。这种方式确
实给我们带来了许多不便,现在 GDI+将形状轮廓绘制和填充操作分开而采用不同的方法,
例如 DrawRectangle 和 FillRectangle 分别用来绘制和填充一个矩形。
5、简化区域的创建
我们知道,GDI 提供了许多区域创建函数,如 CreateRectRgn、CreateEllpticRgn、
CreateRoundRectRgn、CreatePolygonRgn 和 CreatePolyPolygonRgn 等。诚然,这些函
数给我们带来了许多方便。但在 GDI+中,由于为了便于将区域引入矩阵变换操作,GDI+
简化一般区域创建的方法,而将更复杂的区域创建交由 Path 接管。由于 Path 对象是与设
备环境分离开来的,因而可以直接在 Region 构造函数中加以指定。
6、用 Visual C++.NET 使用 GDI+的一般方法
在 Visual C++.NET 使用 GDI+一般遵循下列步骤:
(1) 在应用程序中添加 GDI+的包含文件 gdiplus.h 以及附加的类库 gdiplus.lib。通常
gdiplus.h 包含文件添加在应用程序的 stdafx.h 文件中,而 gdiplus.lib 可用两种进行添加:
第一种是直接在 stdafx.h 文件中添加下列语句:
5
#pragma comment( lib, "gdiplus.lib" )
另一种方法是:选择"项目"ò"属性"菜单命令,在弹出的对话框中选中左侧的"链接器"ò"
输入"选项,在右侧的"附加依赖项"框中键入 gdiplus.lib,结果如图 1 所示。
图 1
(2) 在应用程序项目的应用类中,添加一个成员变量,如下列代码:
ULONG_PTR m_gdiplusToken;
其中,ULONG_PTR 是一个 DWORD 数据类型,该成员变量用来保存 GDI+被初始化
后在应用程序中的 GDI+标识,以便能在应用程序退出后,引用该标识来调用 Gdiplus::
GdiplusShutdown 来关闭 GDI+。
(3) 在应用类中添加 ExitInstance 的重载,并添加下列代码用来关闭 GDI+:
int CEx_GDIPlusApp::ExitInstance()
{
Gdiplus::GdiplusShutdown(m_gdiplusToken);
return CWinApp::ExitInstance();
}
(4) 在应用类的 InitInstance 函数中添加 GDI+的初始化代码:
BOOL CEx_GDIPlusApp::InitInstance()
{
CWinApp::InitInstance();
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
...
6
}
(5) 在需要绘图的窗口或视图类中添加 GDI+的绘制代码。
下面分别就单文档和基于对话框应用程序为例,说明使用 GDI+的一般过程和方法。
1. 在单文档应用程序中使用 GDI+
在上面的过程中,我们就是以一个单文档应用程序 Ex_GDIPlus 作为示例的。下面列出
第 5 步所涉及的代码:
void CEx_GDIPlusView::OnDraw(CDC* pDC)
{
CEx_GDIPlusDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
using namespace Gdiplus;
Graphics graphics( pDC->m_hDC );
Pen newPen( Color( 255, 0, 0 ), 3 );
HatchBrush newBrush( HatchStyleCross,
Color(255, 0, 255, 0),
Color(255, 0, 0, 255));
// 创建一个填充画刷,前景色为绿色,背景色为蓝色
graphics.DrawRectangle( &newPen, 50, 50, 100, 60);
// 在(50,50)处绘制一个长为 100,高为 60 的矩形
graphics.FillRectangle( &newBrush, 50, 50, 100, 60);
// 在(50,50)处填充一个长为 100,高为 60 的矩形区域
}
编译并运行,结果如图 2 所示。
图 2
7
2. 在基于对话框应用程序中使用 GDI+
步骤如下:
(1) 创建一个默认的基于对话框的应用程序 Ex_GDIPlusDlg。
(2) 打开 stdafx.h 文件添加下列代码:
#include
#pragma comment( lib, "gdiplus.lib" )
(3) 打开 Ex_GDIPlusDlg.h 文件,添加下列代码:
class CEx_GDIPlusDlgApp : public CWinApp
{
...
public:
virtual BOOL InitInstance();
ULONG_PTR m_gdiplusToken;
...
};
(4) 在 CEx_GDIPlusDlgApp 类的属性窗口中,单击"重写"工具按钮,为该添加
ExitInstance 的重载:
int CEx_GDIPlusDlgApp::ExitInstance()
{
Gdiplus::GdiplusShutdown(m_gdiplusToken);
return CWinApp::ExitInstance();
}
(5) 定位到 CEx_GDIPlusDlgApp::InitInstance 函数处,添加下列 GDI+初始化代码:
BOOL CEx_GDIPlusDlgApp::InitInstance()
{
CWinApp::InitInstance();
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
...
}
(6) 定位到 CEx_GDIPlusDlgDlg::OnPaint 函数处,添加下列 GDI+代码:
void CEx_GDIPlusDlgDlg::OnPaint()
8