分享

Windows+GDI和GDI+编程实例剖析

 冉亮 2010-08-04

1.基本概念

GDI
Windows中定义为Graphics Device Interface,即图形设备接口,是Windows API(Application Programming Interface)的一个重要组成部分。它是Windows图形显示程序与实际物理设备之间的桥梁,GDI使得用户无需关心具体设备的细节,而只需在一 个虚拟的环境(即逻辑设备)中进行操作。它的桥梁作用体现在:

(1)
用户通过调用GDI函数将逻辑空间的操作转化为具体针对设备驱动程序的调用。

为实现图形设备无关性,Windows的绘图操作在一个设备描述表上进行。用户拥有自己的"逻辑坐标"系统,它独立于实际的物理设备,与"设备坐标"相对应。开发Windows应用程序时,程序员关心的是逻辑坐标,我们在逻辑坐标系上绘图,利用GDI将逻辑窗口映射到物理设备上。

(2)GDI
能检测具体设备的能力,并依据具体的设备以最优方式驱动这些设备,完成真实的显示。

GDI
函数大致可分类为:设备上下文函数(GetDCCreateDCDeleteDC)、 画线函数(LineToPolylineArc)、填充画图函数(EllipseFillRectPie)、画图属性函数( SetBkColorSetBkModeSetTextColor)、文本、字体函数(TextOutGetFontData)、位图函数( SetPixelBitBltStretchBlt)、坐标函数(DPtoLPLPtoDPScreenToClient ClientToScreen)、映射函数(SetMapModeSetWindowExtExSetViewportExtEx)、元文件函数 (PlayMetaFileSetWinMetaFileBits)、区域函数(FillRgnFrameRgnInvertRgn)、路径函 数(BeginPathEndPathStrokeAndFillPath)、裁剪函数(SelectClipRgn SelectClipPath)等。

GDI
虽然使程序员得到了一定程度的解脱,但是其编程方式仍很麻烦。譬如,显示一张位图,程序员需要进行"装入位图读取位图文件头信息启用设备场景调色板变换"等一连串操作。而有了GDI+,这些问题便迎刃而解了。

顾名思义,GDI+GDI的增强版。它是微软在Windows 2000以后操作系统中提供的新接口,其通过一套部署为托管代码的类来展现,这套类被称为GDI+"托管类接口"GDI+主要提供了以下三类服务:

(1)
二维矢量图形:GDI+提供了存储图形基元自身信息的类(或结构体)、存储图形基元绘制方式信息的类以及实际进行绘制的类;

(2)
图像处理:大多数图片都难以划定为直线和曲线的集合,无法使用二维矢量图形方式进行处理。因此,GDI+为我们提供了BitmapImage等类,它们可用于显示、操作和保存BMPJPGGIF等图像格式。

(3)
文字显示:GDI+支持使用各种字体、字号和样式来显示文本。

GDI
接口是基于函数的,而GDI+是基于C++类的对象化的应用程序编程接口,因此使用起来比GDI要方便。

2.
例程简述

单击此处下载本文例程源代码

本文后续的讲解都基于这样的一个例子工程(例程的开发环境为Visual C++6.0,操作系统为Windows XP),它是一个基于对话框的MFC应用程序,包括2个父菜单:

(1) GDI

GDI
父菜单下包括一个子菜单:

ID
IDM_GDI_DRAW_LINE caption:画线

单击事件:在窗口绘制正旋曲线

(2) GDI+

DIB
位图父菜单下包括两个子菜单:

a. ID
IDM_GDIP_DRAW_LINE caption:画线

单击事件:在窗口绘制正旋曲线

b. caption
:新增功能,其下又包括下列子菜单:

(ⅰ)ID
IDM_Gradient_Brush caption:渐变画刷

单击事件:在窗口演示GDI+的渐变画刷功能

(ⅱ)ID
IDM_Cardinal_Spline caption:基数样条

单击事件:在窗口演示GDI+的基数样条函数功能

(ⅲ)ID
IDM_Transformation_Matrix caption:变形和矩阵对象

单击事件:在窗口演示GDI+的变形和矩阵对象功能

(ⅳ)ID
IDM_Scalable_Region caption:可伸缩区域

单击事件:在窗口演示GDI+的可伸缩区域功能

(ⅴ)ID
IDM_IMAGE caption:图像

单击事件:在窗口演示GDI+的多种图像格式支持功能

(ⅵ)ID
IDM_Alpha_Blend captionAlpha混合

单击事件:在窗口演示GDI+Alpha混合功能

(ⅶ)ID
IDM_TEXT caption:文本

单击事件:在窗口演示GDI+的强大文本输出能力

后续篇章将集中在对上述菜单单击事件消息处理函数的讲解,下面的代码是整个对话框类CGdiexampleDlg的消息映射:

BEGIN_MESSAGE_MAP(CGdiexampleDlg, CDialog)
//{{AFX_MSG_MAP(CGdiexampleDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_COMMAND(IDM_GDI_DRAW_LINE, OnGdiDrawLine)
ON_COMMAND(IDM_GDIP_DRAW_LINE, OnGdipDrawLine)
ON_COMMAND(IDM_Gradient_Brush, OnGradientBrush)
ON_COMMAND(IDM_Cardinal_Spline, OnCardinalSpline)
ON_COMMAND(IDM_Transformation_Matrix, OnTransformationMatrix)
ON_COMMAND(IDM_Scalable_Region, OnScalableRegion)
ON_COMMAND(IDM_IMAGE, OnImage)
ON_COMMAND(IDM_Alpha_Blend, OnAlphaBlend)
ON_COMMAND(IDM_TEXT, OnText)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()


3.GDI
编程

头文件:#include  "math.h "

#define PI 3.1415926


"GDI"
菜单下的"画线"子菜单单击事件消息处理函数的代码如下:

void CGdiexampleDlg::OnGdiDrawLine()
{
// TODO: Add your command handler code here
CClientDC dc(this);
//
逻辑坐标与设备坐标变换
CRect rect;
GetClientRect(&rect);
dc.SetMapMode(MM_ANISOTROPIC);
dc.SetWindowOrg(0, 0);
dc.SetWindowExt(rect.right, rect.bottom);
dc.SetViewportOrg(0, rect.bottom / 2);
dc.SetViewportExt(rect.right, - rect.bottom);
//
创建绘制正旋曲线的pen并将其选入设备上下文
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));
HGDIOBJ oldObject = dc.SelectObject(pen.GetSafeHandle());
//
绘制正旋曲线
dc.MoveTo(0, 0);
for (int i = 0; i < rect.right; i++)
{
dc.LineTo(i, 100 *sin(2 *(i / (rect.right / 5.0)) *PI));
}
//
创建绘制x轴的pen并将其选入设备上下文
CPen penx(PS_SOLID, 1, RGB(0, 0, 255));
dc.SelectObject(penx.GetSafeHandle());
//
绘制X
dc.MoveTo(0, 0);
dc.LineTo(rect.right, 0);
//
恢复原先的pen
dc.SelectObject(oldObject);
}


单击这个按钮,会出现如图1所示的效果,我们来对此进行解读。

 
1 绘制正旋曲线


前文提到,GDI编程需进行设备坐标和逻辑坐标的转化。而屏幕上的设备坐标通常会按客户坐标给出,客户坐标依赖于窗口的客户区域,其起始位置位于客户区域的左上角。为示区别,图2给出了设备坐标和用户逻辑坐标的示例。


2 设备坐标与逻辑坐标


设备坐标与逻辑坐标的转换关系如下:


公式中的<Xvorg, Yvorg>是设备空间中视口的原点,而< Xworg, Yworg >是逻辑空间中窗口的原点。 Xwext/XvextYwext/Yvext分别是窗口与视口水平和垂直范围的比例。

因此,经过程序中的dc.SetWindowOrg (00) dc.SetViewportOrg (0rect.bottom/2)语句我们设置了视口和窗口的原点;而经过程序中的dc.SetWindowExt (rect.rightrect.bottom) dc.SetViewportExt (rect.right-rect.bottom) 语句我们设置了视口和窗口的范围。由于视口和窗口的纵坐标方向相反,设置视口的垂直范围为负值。这样我们得到了一个逻辑坐标原点为客户区水平方向最左边和垂直方向居中的坐标系,我们在这个坐标系上直接绘制正旋曲线,不需要再理睬Windows对话框客户区坐标了。

void CGdiexampleDlg::OnGdiDrawLine()
函数中未指定逻辑设备和物理设备的映射模式,则为缺省的MM_TEXT在这种模式下,一个逻辑单位对应于一个像素点。映射模式是GDI中的一个重要概念,其它的映射模式还有MM_LOENGLlSHMM_HIENGLISH MM_LOMETRICMM_HIMETRIC等。我们可以通过如下语句指定映射模式为MM_TEXT

dc.SetMapMode(MM_TEXT);


值得一提的是,从上述代码可以看出:在GDI编程中,几乎所有的操作都围绕设备上下文dc展开。的确,这正是GDI编程的特点!设备上下文是Windows 使用的一种结构,所有GDI操作前都需取得特定设备的上下文,函数中的CClientDC dc (this) 语句完成这一功能。

归纳可得,利用GDI进行图形、图像处理的一般操作步骤为:
1.
取得指定窗口的DC
2.
确定使用的坐标系及映射方式;
3.
进行图形、图像或文字处理;
4.
释放所使用的DC
5. GDI+
编程

 


"GDI+"
菜单下的"画线"子菜单单击事件消息处理函数的代码如下:

void CGdiexampleDlg::OnGdipDrawLine()
{
// TODO: Add your command handler code here
CClientDC dc(this);
//
逻辑坐标与设备坐标变换
CRect rect;
GetClientRect(&rect);
dc.SetMapMode(MM_ANISOTROPIC);
dc.SetWindowOrg(0, 0);
dc.SetWindowExt(rect.right, rect.bottom);
dc.SetViewportOrg(0, rect.bottom / 2);
dc.SetViewportExt(rect.right, - rect.bottom);

//
创建Graphics对象
Graphics graphics(dc);
//
创建pen
Pen myPen(Color::Red);
myPen.SetWidth(1);
//
画正旋曲线
for (int i = 0; i < rect.right; i++)
{
graphics.DrawLine(&myPen, i, 100 *sin(2 *(i / (rect.right / 5.0)) *PI), i+1, 100 *sin(2 *((i + 1)/(rect.right / 5.0)) *PI));
}
//
X
myPen.SetColor(Color::Blue);
graphics.DrawLine(&myPen, 0, 0, rect.right, 0);
}


由于我们使用的是Visual C++6.0而非VS.Net,我们需要下载微软的GDIPLUS支持包。在微软官方网站下载时需认证Windows为正版,我们可从这个地址下载: http://www./code/legacy/gdi/GDIPlus.zip。一个完整的GDI+支持包至少包括如下文 件:

(1)
头文件:gdiplus.h

(2)
动态链接库的.lib文件:gdiplus.lib

(3)
动态链接库的.dll文件:gdiplus.dll

少了(1)(2)程序不能编译,少了(3)程序能以共享DLL的方式编译但是不能运行,运行时找不到.dll文件。


为使得Visual C++6.0支持GDI+,我们需要在使用GDI+对象的文件的开头添加如下代码:

#define UNICODE
#ifndef ULONG_PTR
#define ULONG_PTR unsigned long*
#endif
#include "c:\gdiplus\includes\gdiplus.h"
using namespace Gdiplus;
#pragma comment(lib, "c:\\gdiplus\\lib\\gdiplus.lib")


Visual C++中使用GDI+必须先进行GDI+的初始化,我们在CWinApp派生类的InitInstance函数中进行此项工作是最好的:

/////////////////////////////////////////////////////////////////////////////
// CGdiexampleApp initialization

BOOL CGdiexampleApp::InitInstance()
{
AfxEnableControlContainer();

// Standard initialization

#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

//
初始化gdiplus的环境
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
//
初始化GDI+.
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);


CGdiexampleDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK){}
else if (nResponse == IDCANCEL){}

//
关闭gdiplus的环境
GdiplusShutdown(gdiplusToken);

return FALSE;
}


单击"GDI+"菜单下的"画线"子菜单,也会出现如图1所示的效果。观察void CGdiexampleDlg::OnGdipDrawLine() 函数,我们发现用GDI+进行图形、图像操作的步骤为:

(1)
创建 Graphics 对象:Graphics 对象表示GDI+绘图表面,是用于创建图形图像的对象;

(2)
使用 Graphics 对象绘制线条和形状、呈现文本或显示与操作图像。
Graphics
对象是GDI+的核心,GDI中设备上下文dcGraphics 对象的作用相似,但在GDI中使用的是基于句柄的编程模式,而GDI+中使用的则是基于对象的编程模式Graphics封装了GDI+ 绘图面,而且此类无法被继承,它的所有成员函数都不是虚函数。

下面,我们来逐个用实际代码实现GDI+的新增功能,这些新增功能包括:渐变的画刷(Gradient Brushes)、基数样条函数(Cardinal Splines)、持久的路径对象(Persistent Path Objects)、变形和矩阵对象(Transformations &Matrix Object)、可伸缩区域(Scalable Regions)Alpha混合(Alpha Blending)和丰富的图像格式支持等。

渐变的画刷

GDI+
提供了用于填充图形、路径和区域的线性渐变画刷和路径渐变画刷。

线性渐变画刷使用渐变颜色来填充图形。

当用路径渐变画刷填充图形时,可指定从图形的一部分移至另一部分时画刷颜色的变化方式。例如,我们可以只指定图形的中心颜色和边缘颜色,当画刷从图形中间向外边缘移动时,画刷会逐渐从中心颜色变化到边缘颜色。

void CGdiexampleDlg::OnGradientBrush()
{
// TODO: Add your command handler code here
CClientDC dc(this);
CRect rect;
GetClientRect(&rect);
//
创建Graphics对象
Graphics graphics(dc);
//
创建渐变画刷
LinearGradientBrush lgb(Point(0, 0), Point(rect.right, rect.bottom), Color::Blue, Color::Green);
//
填充
graphics.FillRectangle(&lgb, 0, 0, rect.right, rect.bottom);
}


本程序使用线性渐变画刷,当画刷从客户区左上角移向客户区右下角的过程中,颜色逐渐由蓝色转变为绿色。


3 GDI+渐变画刷


基数样条函数

GDI+
支持基数样条,基数样条指的是一连串单独的曲线,这些曲线连接起来形成一条较大的曲线。样条由点(Point结构体)的数组指定,并通过该数组中的每一个点。基数样条平滑地穿过数组中的每一个点(不出现尖角),因此比用直线连接创建的路径精确。

void CGdiexampleDlg::OnCardinalSpline()
{
// TODO: Add your command handler code here
CClientDC dc(this);
//
创建Graphics对象
Graphics graphics(dc);
Point points[] =

{
Point(0, 0), Point(100, 200), Point(200, 0), Point(300, 200), Point(400, 0)
};
//
直接画线
for (int i = 0; i < 4; i++)
{
graphics.DrawLine(&Pen(Color::Blue, 3), points[i], points[i + 1]);
}
//
利用基数样条画线
graphics.DrawCurve(&Pen(Color::Red, 3), points, 5);
}


4演示了直接连线和经过基数样条平滑拟合后的线条的对比,后者的曲线(Curve)没有尖角。这个工作我们在中学的数学课上把离散的点连接成曲线时做过。


4 GDI+基数样条

持久的路径对象

GDI中,路径隶属于一个设备上下文,一旦设备环境指针超过它的生存期,路径也会被删除。利用GDI+,可以创建并维护与Graphics对象分开的GraphicsPath 对象,它不依赖于Graphics对象的生存期。

变形和矩阵对象

GDI+
提供了Matrix对象,它是一种可以使变形(旋转、平移、缩放等) 简易灵活的强大工具,Matrix对象需与要被变形的对象联合使用。对于GraphicsPath类,我们可以使用其成员函数Transform接收Matrix参数用于变形。

void CGdiexampleDlg::OnTransformationMatrix()
{
// TODO: Add your command handler code here
CClientDC dc(this);
//
创建Graphics对象
Graphics graphics(dc);
GraphicsPath path;
path.AddRectangle(Rect(250, 20, 70, 70));
graphics.DrawPath(&Pen(Color::Black, 1), &path); //
在应用变形矩阵之前绘制矩形
//
路径变形
Matrix matrix1, matrix2;

matrix1.Rotate(45.0f); //
旋转顺时针45
path.Transform(&matrix1); //
应用变形
graphics.DrawPath(&Pen(Color::Red, 3), &path);

matrix2.Scale(1.0f, 0.5f); //
转化成为平行四边形法则
path.Transform(&matrix2); //
应用变形
graphics.DrawPath(&Pen(Color::Blue, 3), &path);
}


5演示了正方形经过旋转和拉伸之后的效果:黑色的为原始图形,红色的为旋转45度之后的图形,蓝色的为经过拉伸为平行四边形后的图形。


5 GDI+变形和矩阵对象


可伸缩区域

GDI+
通过对区域(Region)的支持极大地扩展了GDI。在GDI 中,区域存储在设备坐标中,可应用于区域的唯一变形是平移。但是在GDI +中,区域存储在全局坐标(世界坐标)中,可对区域利用变形矩阵进行变形(旋转、平移、缩放等)

void CGdiexampleDlg::OnScalableRegion()
{
// TODO: Add your command handler code here
CClientDC dc(this);
//
创建Graphics对象
Graphics graphics(dc);
//
创建GraphicsPath
GraphicsPath path;
path.AddLine(100, 100, 150, 150);
path.AddLine(50, 150, 150, 150);
path.AddLine(50, 150, 100, 100);
//
创建Region
Region region(&path);
//
填充区域
graphics.FillRegion(&SolidBrush(Color::Blue), &region);
//
区域变形
Matrix matrix;
matrix.Rotate(10.0f); //
旋转顺时针20
matrix.Scale(1.0f, 0.3f); //
拉伸
region.Transform(&matrix); //
应用变形
//
填充变形后的区域
graphics.FillRegion(&SolidBrush(Color::Green), &region);
}


上述程序中以蓝色填充一个三角形区域,接着将此区域旋转和拉伸,再次显示,其效果如图6


6 GDI+区域变形

丰富的图像格式支持

GDI +
提供了ImageBitmap Metafile 类,方便用户进行图像格式的加载、操作和保存。GDI+支持的图像格式有BMPGIFJPEGEXIFPNGTIFFICONWMFEMF等,几乎涵盖了所有的常用图像格式。

void CGdiexampleDlg::OnImage()
{
// TODO: Add your command handler code here
CClientDC dc(this);
//
创建Graphics对象
Graphics graphics(dc);
Image image(L "d:\\1.jpg");
//
在矩形区域内显示jpg图像
Point destPoints1[3] =
{
Point(10, 10), Point(220, 10), Point(10, 290)
};
graphics.DrawImage(&image, destPoints1, 3);
//
在平行四边形区域内显示jpg图像
Point destPoints2[3] =
{
Point(230, 10), Point(440, 10), Point(270, 290)
};
graphics.DrawImage(&image, destPoints2, 3);
}


上述程序将D盘根目录下文件名为"1.jpg"jpg图像以矩阵和平行四边形两种方式显示,效果如图7


7 GDI+多种图像格式支持


由此我们可以看出,GDI+在图像显示和操作方面的确比GDI简单许多。回忆我们在《Visual C++DDBDIB位图编程全攻略》一文中所介绍的用GDI显示位图的方式,其与GDI+图像处理的难易程度真是有天壤之别。

Alpha
混合

Alpha
允许将两个物体混合起来显示,在3D气氛和场景渲染等方面有广泛应用。它能"雾化"图像,使得一个图像着色在另一个半透明的图像上,呈现一种朦胧美。我们知道,一个像素可用 RGB三个维度来表示,我们可以再加上第4个即:Alpha维度(channel),表征透明程度。

void CGdiexampleDlg::OnAlphaBlend()
{
// TODO: Add your command handler code here
CClientDC dc(this);
//
创建Graphics对象
Graphics graphics(dc);
//
创建ColorMatrix
ColorMatrix ClrMatrix =
{
1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.5f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f
};
//
ColorMatrix赋给ImageAttributes
ImageAttributes ImgAttr;
ImgAttr.SetColorMatrix(&ClrMatrix, ColorMatrixFlagsDefault,ColorAdjustTypeBitmap);
//
在矩形区域内显示jpg图像
Image img1(L "d:\\1.jpg");
Point destPoints1[3] =
{
Point(10, 10), Point(220, 10), Point(10, 290)
};
graphics.DrawImage(&img1, destPoints1, 3);
//Alpha
混合
Image img2(L "d:\\2.jpg");
int width, height;
width = img2.GetWidth();
height = img2.GetHeight();
graphics.DrawImage(&img2, RectF(10, 10, 210, 280), 0, 0, width, height,UnitPixel, &ImgAttr);
//
在平行四边形区域内显示jpg图像
Point destPoints2[3] =
{
Point(230, 10), Point(440, 10), Point(270, 290)
};
graphics.DrawImage(&img1, destPoints2, 3);
//Alpha
混合
graphics.DrawImage(&img2, destPoints2, 3, 0, 0, width, height, UnitPixel,&ImgAttr);
}


上述程序中将D盘根目录下文件名为"1.jpg"的图像以矩阵和平行四边形两种方式显示,然后将文件名为为"2.jpg"的图像与之进行混合,其效果如图8


8 GDI+ Alpha混合


为了能进行Alpha混合,我们需要使用ImageAttributes类和ColorMatrix矩阵,ImageAttributes可以进行颜色、灰度等调整从而达到控制图像着色方式的目的。ColorMatrixImageAttributes类大多数函数的参数,它包含了Alpha RedGreenBlue维度的值,以及另一维w,顺序为RGBaw

CGdiexampleDlg:: OnAlphaBlend()
函数中ColorMatrix的实例ClrMatrix中元素(4,4)的值为0.5,表示Alpha度的值为0.5(即半 透明)。在ColorMatrix中,元素(5,5)的值恒定为1.0。我们把ClrMatrix的元素(0,0)修改为0.0,就是使得图像2.jpg的 红色维度全不显示,再看效果,为图9。列位读者,我们以前在豪杰超级解霸中调整RGB值从而控制图像输出颜色的时候,调的就是这个东东!图9的效果很 像破旧彩色电视机,红色电子枪""了。刚大学毕业时,俺那个叫穷啊,就买了这么个电视机,还看得很爽,真是往事不堪回首!


9 GDI+中的ColorMatrix


强大的文字输出

GDI+
拥有极其强大的文字输出处理能力,输出文字的颜色、字体、填充方式都可以直接作为GraphicsDrawString成员函数的参数进行设置,其功能远胜过GDI设备上下文的TextOut函数。

void CGdiexampleDlg::OnText()
{
// TODO: Add your command handler code here
CClientDC dc(this);
//
创建Graphics对象
Graphics graphics(dc);
//
创建20"楷体"字体
FontFamily fontFamily1(L"
华文行楷"); // 定义"楷体"字样
Font font1(&fontFamily1, 20, FontStyleRegular, UnitPoint);
//
定义输出UNICODE字符串
WCHAR string[256];
wcscpy(string, L"
天极网的读者朋友,您好!");
//
以蓝色画刷和20"楷体"显示字符串
graphics.DrawString(string, (INT)wcslen(string), &font1, PointF(30, 10),&SolidBrush(Color::Blue));
//
定义字符串显示画刷
LinearGradientBrush linGrBrush(Point(30, 50), Point(100, 50), Color(255, 255,0, 0), Color(255, 0, 0, 255));
//
以线性渐变画刷和创建的20"楷体"显示字符串
graphics.DrawString(string, (INT)wcslen(string), &font1, PointF(30, 50),&linGrBrush);
//
创建20"华文行楷"字体
FontFamily fontFamily2(L"
华文行楷"); // 定义"楷体"字样
Font font2(&fontFamily2, 20, FontStyleRegular, UnitPoint);
//
以线性渐变画刷和20"华文行楷"显示字符串
graphics.DrawString(string, (INT)wcslen(string), &font2, PointF(30, 90),&linGrBrush);
//
以图像创建画刷
Image image(L"d:\\3.jpg");
TextureBrush tBrush(&image);
//
以图像画刷和20"华文行楷"显示字符串
graphics.DrawString(string, (INT)wcslen(string), &font2, PointF(30, 130),&tBrush);
//
创建25"华文中宋"字体
FontFamily fontFamily3(L"
华文中宋"); // 定义"楷体"字样
Font font3(&fontFamily2, 25, FontStyleRegular, UnitPoint);
//
以图像画刷和20"华文行楷"显示字符串
graphics.DrawString(string, (INT)wcslen(string), &font3, PointF(30, 170),&tBrush);
}


上述代码的执行效果如图10所示,字体、颜色和填充都很丰富!


10 GDI+文本输出


5.GDI
GDI+的比较

GDI+
相对GDI而言主要在编程方式上发生了巨大的改变。

GDI
的核心是设备上下文,GDI函数都依赖于设备上下文句柄,其编程方式是基于句柄的;GDI+无需时刻依赖于句柄或设备上下文,用户只需创建一个Graphics 对象,就可以用面向对象的方式调用其成员函数进行图形操作,编程方式是基于对象的

GDI
在使用设备上下文绘制线条之前,必须先调用SelectObject 以使钢笔对象和设备上下文关联。其后,在设备上下文中绘制的所有线条均使用该钢笔,直到选择另一支不同的钢笔为止。CGdiexampleDlg:: OnGdiDrawLine函数中的下列语句完成的就是这个功能:

//创建绘制正旋曲线的pen并将其选入设备上下文
CPen pen(PS_SOLID,1,RGB(255,0,0));
HGDIOBJ oldObject = dc.SelectObject(pen.GetSafeHandle());

//
创建绘制x轴的pen并将其选入设备上下文
CPen penx(PS_SOLID,1,RGB(0,0,255));
dc.SelectObject(penx.GetSafeHandle());

//
恢复原先的pen
dc.SelectObject(oldObject);


但是,在GDI+中,只需将Pen对象直接作为参数传递给Graphics类的DrawLine等方法即可,而不必使Pen对象与Graphics对象关联,例如CGdiexampleDlg::OnGdipDrawLine函数中的下列语句:

Pen myPen(Color::Red);
myPen.SetWidth(1);

graphics.DrawLine(&myPen,i,100*sin(2*(i/(rect.right/5.0))*PI),
i+1,100*sin(2*((i+1)/(rect.right/5.0))*PI));

graphics.DrawLine(&myPen,0,0,rect.right,0);


GDI
中有当前位置的概念,所以在使用GDI绘制线条前应该先使用MoveTo移动当前位置,再使用LineTo画线,例如:

//绘制正旋曲线
dc.MoveTo(0,0) ;
for(int i=0;i<rect.right;i++)
{
dc.LineTo(i,100*sin(2*(i/(rect.right/5.0))*PI));
}


GDI+中则没有当前位置的概念,画线函数中可以直接指定起点和终点,例如:

graphics.DrawLine(&myPen,0,0,rect.right,0);


6.
结论

鉴于GDI+良好的易用性和其具有的强大功能,我们建议尽快抛弃GDI编程方式,因为我们没有必要将时间浪费在无意义的重复代码的设计上。GDI+ GDI的增强,某种意义上类似于MFCWindows API的整理和封装。作为一种良好的"生产工具",它必将大大地促进开发时的"生产力"

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多