分享

GDI+ SDK参考

 whli88 2007-09-23

GDI+ SDK参考 - (翻译) [3]

线条、曲线和图形

GDI+的矢量图部分用于绘制线条、绘制曲线,绘制和填充图形。

矢量图概览

Microsoft Windows GDI+在一个坐标系统中绘制线条、矩形和其他图形。您可以选择各种不同的坐标系统,但是默认的坐标系统规定其左上角位置为起点,X轴指向右边、Y轴之指向下边。默认坐标系统的度量单位是象素(Pixel)。

电脑显示器在一个矩形点阵上创建其显示画面,这些点称为图片要素或者象素。不同的显示器其在屏幕上显示的点数不尽相同,并且通常一个独立显示器其显示的象素总数可以由用户进行调节。

在您采用GDI+绘制线条、矩形和曲线的时候,您需要提供一些关于绘制对象的关键性信息。例如,您可以通过2点确定一条线段,您可以通过一个点、高度和宽度确定一个矩形。GDI+与显示器驱动程序协同工作,来判断哪个象素应该开启用于显示线条、矩形和曲线。下面所示,显示一条从点(4, 2)到点(12, 8)的线条的象素情况。

总的来说,某些基本的构成块已经证明是最对建立二维图形非常有用。这些基本构成块,GDI+均支持,包括:

o    Lines (线条)

o    Rectangles (矩形)

o    Ellipses (椭圆)

o    Arcs (弧形)

o    Polygons (多边形)

o    Cardinal splines (基数样条)

o    Bézier splines (贝塞尔样条)

GDI+中的Graphics类提供这些方法来绘制前面列表中的对象:DrawLineDrawRectangleDrawEllipseDrawPolygonDrawArcDrawCurve (用于基数样条)DrawBezier。每个方法都已被重载;也就是说,每种方法都以不同参数列表的变体出现。例如,DrawLine方法的一个变体接受一个Pen对象的地址和四个整型值,而它的另一个变体则接受一个Pen对象地址和两个Point对象引用。

绘制线条、矩形和贝塞尔样条的方法都有其复数形式的伴随方法,这些方法在一次调用中绘制多个项目:DrawLinesDrawRectanglesDrawBeziers。同样地,DrawCurve方法也有一个伴随方法DrawClosedCurve,该函数通过连接曲线的起点和终点创建一个闭合图形。

所有的Graphics类的方法都得配合Pen对象使用。因此,为了绘制任何东西,您必须至少创建2个对象:一个Graphics 对象和一个Pen对象。Pen对象存储诸如线条宽度、颜色等绘图属性。Pen对象的地址将作为一个参数传递给绘图方法。例如,一个DrawRectangle方法的变体需要传入一个Pen对象地址和4各整数值作为参数,该方法绘制一个左上角为(20,10)、宽度为100、高度为50的矩形。

myGraphics.DrawRectangle(&myPen, 20, 10, 100, 50);

钢笔、线条和矩形

采用GDI+绘制线条需要一个Graphics对象和一个Pen对象。Graphics对象提供实际的绘图方法,而Pen对象存储线条属性,例如颜色、宽度和样式等。绘制线条只需要简单调用Graphics对象的DrawLine方法即可。Pen对象的地址作为参数之一传递给DrawLine方法。下面的例子是绘制一条从点(4, 2)到点(12, 6)的线段。

myGraphics.DrawLine(&myPen, 4, 2, 12, 6);

DrawLin是一个在Graphics类中被重载的方法,因此您可以提供几种不同的参数。例如,您可以构造2Point对象,然后将这2Point对象的引用作为参数传递给DrawLine方法。

Point myStartPoint(4, 2);
Point myEndPoint(12, 6);
myGraphics.DrawLine(&myPen, myStartPoint, myEndPoint);

您也可以在构造Pen对象时给它的属性赋值。例如,有个Pen的构造函数允许您指定颜色和宽度。下面的例子是从点(0, 0)到点(60, 30)绘制一条宽度为2的蓝色线段。

Pen myPen(Color(255, 0, 0, 255), 2);
myGraphics.DrawLine(&myPen, 0, 0, 60, 30);

Pen对象同样有自己的属性,例如虚线样式,您可以用来指定线条特征。例如,下面的例子用于绘制一条从点(100, 50)到点(300, 80)的虚线。

myPen.SetDashStyle(DashStyleDash);
myGraphics.DrawLine(&myPen, 100, 50, 300, 80);

您可以使用Pen对象的多种方法来设置更多的线条属性。SetStartCapSetEndCap方法指定线条末端的表现形式;线帽可以为扁平、方块、圆角、三角或者用户定义形状。SetLineJoin方法允许您设置线条间的联接方式是斜接(有明显边角)、斜切、圆或者裁剪。下图显示的是具有不同的端点类型和连接方式的线条。

绘制矩形的方法和画线的方法相似。绘制一个矩形,您需要一个Graphics对象和一个Pen对象。Graphics对象提供一个DrawRectangle方法,而Pen对象存储输入线条宽度和颜色等属性。Pen对象的地址作为参数之一传递给DrawRectangle方法。下面的例子是绘制一个左上角为(100,50)、宽度为80、高度为40的矩形。

myGraphics.DrawRectangle(&myPen, 100, 50, 80, 40);

DrawRectangle是一个在Graphics类中被重载的方法,因此也有几种不同传递参数的方式。例如,您可以先构造一个Rect对象然后将Rect对象的引用作为参数之一传递给DrawRectangle方法。

Rect myRect(100, 50, 80, 40);
myGraphics.DrawRectangle(&myPen, myRect);

Rect对象的某些方法可以控制和收集矩形的有关信息。例如,InflateOffset方法可以改变矩形的尺寸和位置。IntersectsWith方法告诉您一个矩形是否和另一个矩形交叉了,Contains方法则告诉您指定的点是否在一个矩形内部。

椭圆和弧

一个椭圆由它的外接矩形来描述。下图显示了一个椭圆和它的外接矩形。

绘制一个椭圆,您需要一个Graphics对象和一个Pen对象。Graphics对象提供DrawEllipse方法,Pen对象存储诸如线条宽度和颜色等信息。Pen对象的地址作为参数之一传递给DrawEllipse方法。传递给DrawEllipse方法其余的参数指定其外接矩形。下面的例子将绘制一个椭圆;其外接矩形宽度为160、高度为80,左上角位置为(100,50)

myGraphics.DrawEllipse(&myPen, 100, 50, 160, 80);

DrawEllipse是一个在Graphics类中被重载的方法,因此也有几种不同传递参数的方式。例如,您可以先构造一个Rect对象然后将Rect对象的引用作为参数之一传递给DrawEllipse方法。

Rect myRect(100, 50, 160, 80);
myGraphics.DrawEllipse(&myPen, myRect);

弧是椭圆的一部分。绘制弧形,您需要调用Graphics类的DrawArc方法。DrawArc方法的参数和DrawEllipse方法的参数一样,除此之外还需要提供起始角和扫描角。下面的例子绘制一条弧形,其起始角为30度,扫描角为180度。

myGraphics.DrawArc(&myPen, 100, 50, 160, 80, 30, 180);

下图同时显示了弧形、椭圆与外接矩形。

多边形

多边形是由3个或3个以上的直边所组成的闭合图形。例如,三角形就是3条边所组成,矩形由4条边组成,五角星由5条边所组成。下图显示了几种不同的多边形。

绘制多边形,您需要一个Graphics对象,一个Pen对象,以及一个Point(或者PointF)对象数组。Graphics对象提供DrawPolygon方法,Pen对象存储多边形的线条宽度和颜色等信息,Point对象数组则存储相连直线的点集。Pen对象的地址和Point对象数组作为参数传递给DrawPolygon方法。下面的例子将绘制一个3条边的多边形。注意在myPointArray中只有3个点:(0, 0) (50, 30) (30, 60)DrawPolygon方法自动绘制从(30, 60)返回点(0, 0)的线条使该多边形闭合。

Point myPointArray[] =
   {Point(0, 0), Point(50, 30), Point(30, 60)};
myGraphics.DrawPolygon(&myPen, myPointArray, 3);

下图显示了该多边形。

基数样条

基数样条是一组单个曲线按照一定的顺序连接而成的一条较大曲线。样条由一个点数组和一个张力参数描述。由于基数样条平滑地穿过数组中的每一个点;在曲线的密度上不会不出现锐角和突变。下图显示一组点和穿过它们每个点的基数样条。

物理样条是一小片木头或者其他柔性材质做成的。在数学样条诞生之前,设计人员采用物理样条来绘制曲线。它们将样条置于纸上然后定位一系列锚点,然后用铅笔沿着样条绘制曲线。给出的一系列点可能产生不同的曲线,这取决于物理样条的性质。例如,与一个极其易弯曲的样条相比,有较高的抗弯能力的一个样条将生产一条不同的曲线。

数学样条的公式基于柔性杆的特性, 因此数学样条生产的曲线类似于曾经由物理样条生产的曲线。正如物理样条通过给定的一组点时在不同的张力下的将生成一条不同的曲线一样, 数学样条在张力参数不同的时候也将生成不同的曲线。下图显示了通过相同一组点集的4条基数样条。每条样条都标注了它的张力参数。注意张力系数为0的情况下相当于无限的物理张力,迫使曲线走点之间的最短路径(直线)。张力系数为1表示没有物理张力,此时样条采用最小弯程。如果张力系数大于1,此时的样条看起来就像被压扁的弹簧,被迫经过更长的路径。

需要注意的是,以上4条样条在顶点处都拥有相同的切线。切线表示从一个起始点沿曲线指向下一个点间的直线。同样的,终点共享的切线表示从终点开始的沿曲线曲线指向前一个点。

绘制一条基数样条,您需要一个Graphics对象,一个Pen对象和一个Point对象数组。Graphics对象提供DrawCurve方法用于绘制基数样条,Pen对象存储诸如线条宽度和颜色等信息,Point对象数组存储曲线经过的点集。下面的例子将绘制一条基数样条,它穿过myPointArray点集。第三个参数是张力参数。

myGraphics.DrawCurve(&myPen, myPointArray, 3, 1.5f);

贝塞尔样条

贝塞尔样条是由4个点所确定的曲线:2个端点(p1p2)和2个控制点(c1c2)。曲线始于p1终于p2。曲线并不经过控制点,但是控制点扮演了磁铁的角色,将曲线往某个方向拉从而影响了曲线的走向。下图显示了贝塞尔样条和它的顶点以及控制点。

注意,曲线从p1开始向控制点c1移动。P1位置的切线是从p1c1。同时请注意,终点p2处的切线是从c2p2的。

绘制贝塞尔样条,您需要一个Graphics对象和一个Pen对象。Graphics对象提供DrawBezier方法,而Pen对象存储诸如线条宽度和颜色等信息。Pen对象的地址作为参数之一传递给DrawBezier方法。DrawBezier方法余下的参数传入顶点和控制点。下面的例子将绘制一条贝塞尔样条,它的起点为(0,0),控制点为(40,20)(80,150),终点为(100,10)

myGraphics.DrawBezier(&myPen, 0, 0, 40, 20, 80, 150, 100, 10);

下图显示该曲线、控制点和两条切线。

贝塞尔样条最初由Pierre Bézier在汽车工业设计中发明。它被证明对于多种类型的计算机辅助设计非常有用,同时还用于定义字体轮廓。贝塞尔样条可以产生多种形状,下图罗列了其中一些:

路径

路径由线条、矩形以及简单曲线等组合而成。回顾矢量图形概念部分,以下基本构成块被证明对于绘制图象非常有用。

l   Lines (线条)

l   Rectangles (矩形)

l   Ellipses (椭圆)

l   Arcs (弧线)

l   Polygons (多边形)

l   Cardinal splines (基数样条)

l   Bézier splines (贝塞尔样条)

GDI+中,GraphicsPath对象允许您将这些基本组成部分组合成一个单独的单位。整个一组线条、矩形、多边形和曲线可以通过Graphcis类的DrawPath方法一次性绘制。下图显示的是一条有线条、弧线、贝塞尔样条以及基数样条所组成的路径。

GraphicsPath类提供如下方法用于创建一系列对象:AddLineAddRectangleAddEllipseAddArcAddPolygonAddCurve (用于基数样条)以及AddBezier。它们中的每个方法都已被重载;也就是说,您可以传入不同的参数列表。例如,AddLine方法的变体之一需要传入4个整形值,而另外一个变体则需要传入2Point对象。

添加线条、矩形和贝塞尔样条的方法都有其复数形式的伴随方法,这些方法在一次调用中添加多个项目:AddLinesAddRectanglesAddBeziers。同样地,AddCurve方法也有一个伴随方法AddClosedCurve,该函数通过连接曲线的起点和终点添加一个闭合曲线。

绘制一条路径,您需要一个Graphcis对象,一个Pen对象和一个GraphicsPath对象。Graphics对象提供方法。Pen对象存储诸如线条宽度和颜色等信息。GraphicsPath对象存储线条、矩形和曲线序列用以构成一条路径。Pend对象和GracphisPath对象的地址作为参数传给DrawPath方法。下面的例子将绘制一条路径,它由一根线条、一个椭圆和一条贝塞尔样条组成。

myGraphicsPath.AddLine(0, 0, 30, 20);
myGraphicsPath.AddEllipse(20, 20, 20, 40);
myGraphicsPath.AddBezier(30, 60, 70, 60, 50, 30, 100, 10);
myGraphics.DrawPath(&myPen, &myGraphicsPath);

下图所示为该路径:

除了添加线条、矩形和曲线到路径外,您还可以添加路径到路径。这允许您将已有路径组合为更大更复杂的路径。下面的代码将graphicsPath1graphicsPath2加入到myGraphicsPath中。AddPath方法的第二个参数用于指定新增路径是否与已有路径相连。

myGraphicsPath.AddPath(&graphicsPath1, FALSE);
myGraphicsPath.AddPath(&graphicsPath2, TRUE);

另外还有2个项目您可以加入路径中:字符串和饼图。饼图是椭圆的一部分。下面的例子将创建一个由弧线、基数样条、字符串和饼图组成的路径。

myGraphicsPath.AddArc(0, 0, 30, 20, -90, 180);
myGraphicsPath.AddCurve(myPointArray, 3);
myGraphicsPath.AddString(L"a string in a path", 18, &myFontFamily, 
   0, 24, myPointF, &myStringFormat);
myGraphicsPath.AddPie(230, 10, 40, 40, 40, 110);
myGraphics.DrawPath(&myPen, &myGraphicsPath);

下图所示为这个路径。注意这个路径并没有连接起来;弧线、基数样条、字符串和饼图都是相离的。

画刷和填充图形

一个闭合图形比如矩形和椭圆包含一个边框和内部区域。边框是由Pen对象绘制,而内部区域由Brush对象进行填充。Microsoft Windows GDI+提供几种画刷类用于填充闭合图形的内部区域:SolidBrushHatchBrushTextureBrushLinearGradientBrushPathGradientBrush。所有这些类都继承于Brush类。下图显示了一个由纯色画刷填充的矩形和一个由阴影画刷填充的椭圆。

·         纯色画刷

填充一个闭合图形,您需要一个Graphics对象和一个Brush对象。Graphics对象提供方法,比如FillRectangle FillEllipse,而Brush对象存储诸如颜色和图案等填充属性。Brush对象的地址作为参数之一传递给填充方法。下面的例子将用实行红色填充一个椭圆。

SolidBrush mySolidBrush(Color(255, 255, 0, 0));
myGraphics.FillEllipse(&mySolidBrush, 0, 0, 60, 40);

注意上例中,画刷采用的是SolidBrush类型,它继承于Brush

·         阴影画刷

当您需要填充一个阴影画刷时,您需要指定其前景色、背景色和阴影样式。前景色就是阴影的颜色。

HatchBrush myHatchBrush(
   HatchStyleVertical, 
   Color(255, 0, 0, 255),
   Color(255, 0, 255, 0));

GDI+提供了超过50种阴影样式,这些样式在HatchStyle中定义。下面显示的3种阴影分别是水平、正向对角线和十字交叉阴影。

·         纹理画刷

通过纹理画刷,您可以使用存储于位图中的纹理来填充图形。例如,假设下面的图形存储在磁盘文件MyTexture.bmp中。

下面的例子将创建一个由MyTexture.bmp中存储的图片反复填充得到的椭圆。

Image myImage(L"MyTexture.bmp");
TextureBrush myTextureBrush(&myImage);
myGraphics.FillEllipse(&myTextureBrush, 0, 0, 100, 50);

下图所示为填充结果:

·         渐变画刷

您可以采用渐变画刷填充一个图形,使得该图形可以从一个部分到另一部分由一种颜色渐变为其他颜色。例如,一个水平渐变画刷将使得从左至右颜色渐变。下面的例子将采用水平渐变画刷填充一个椭圆,从左至右颜色由蓝色逐渐变为绿色。

LinearGradientBrush myLinearGradientBrush(
   myRect,
   Color(255, 0, 0, 255),
   Color(255, 0, 255, 0),
   LinearGradientModeHorizontal);
myGraphics.FillEllipse(&myLinearGradientBrush, myRect); 

下图所示为填充后的椭圆:

路径渐变画刷允许您设置从中心向边界渐变得画刷。

路径渐变画刷非常灵活。下图中用于填充三角形的渐变画刷中心为红色,向每个顶点渐变为3个不同的颜色。

开放与闭合曲线

下面显示了两个曲线:一个开放,一个闭合。

闭合曲线因为有内部区域因而可以被画刷填充。GDI+中的Graphcis类提供如下方法用于填充闭合图形和曲线:FillRectangleFillEllipseFillPieFillPolygonFillClosedCurveFillPathFillRegion。任何时候在您调用这些方法的其中之一时,您必须将一个指定类型的画刷(SolidBrushHatchBrushTextureBrushLinearGradientBrush或者PathGradientBrush)地址作为参数之一传入。

FillPie方法伴随着DrawArc方法。正如DrawArc方法绘制椭圆边界的一部分,FillPie方法填充椭圆内部区域的一部分。下面的例子将绘制一段弧线,然后填充该椭圆内部的相应区域。

myGraphics.FillPie(&mySolidBrush, 0, 0, 140, 70, 0, 120);
myGraphics.DrawArc(&myPen, 0, 0, 140, 70, 0, 120);

下图所示为这条弧线和填充的饼图。

FillClosedCurve方法伴随DrawClosedCurve方法。这两个方法都将终点自动连接起点从而使曲线闭合。下面的例子将绘制一条穿过(0, 0)(60, 20)(40, 50)的曲线。然后该曲线通过连接(40, 50)和起点(0, 0)自动闭合,然后采用实行画刷对该区域进行填充。

Point myPointArray[] =
   {Point(10, 10), Point(60, 20),Point(40, 50)};
myGraphics.DrawClosedCurve(&myPen, myPointArray, 3);
myGraphics.FillClosedCurve(&mySolidBrush, myPointArray, 3, FillModeAlternate)

一条路径可以包含多个图形(子路径)。FillPath方法填充每个图形的内部区域。如果一个图形不是闭合的,那么FillPath方法将假设它是闭合的然后进行填充。下面的例子将填充一个由弧线、基数样条、字符串和饼图组成的路径。

myGraphics.FillPath(&mySolidBrush, &myGraphicsPath);
myGraphics.DrawPath(&myPen, &myGraphicsPath);

下图为该路径采用纯色画刷填充前后的样子。注意,采用DrawPath方法时,字符串描出轮廓,但是没有填充。而FillPath方法将字符串各字符内部进行了着色。

区域

区域指的是显示表面的一部分。区域可以简单(单个矩形)也可以复杂(由一个由多边形和闭合曲线组成)。下图显示了2个区域:一个由矩形构成,另一个由路径构成。

通常区域用来裁剪 (Clipping) 和进行点击测试 (Hit Testing)。裁剪包括限制对显示区域的某个特定区域进行绘制,通常该区域为必须进行更新的部分。点击测试包括检查并判断当按下鼠标按钮时,光标是否位于屏幕的某个特定区域中。

您可以从矩形或路径建立区域。您也可以通过组合现有的区域来建立复杂的区域。Region 类提供下列用来组合区域的方法:IntersectUnionXorExclude Complement

两个区域的交集是指隶属于这两个区域的所有点的组合。联集是指属于其中一个或两个区域的所有点的组合。区域的补码 (Complement) 是指所有区域以外的点。下图将显示前图所说明的两个区域的交集和联集。

套用到区域组的 Xor 方法会产生一个区域,其中含有隶属于其中一个区域 (而非两个) 的所有点。套用到区域组合的 Exclude 方法会产生一个区域,其中含有第一个区域中但位于第二个区域以外的所有点。下图将显示因套用 Xor Exclude 方法到本主题一开始所说明的两个区域时,所产生的区域。

若要绘制区域,您需要Graphics对象、Brush对象和Region对象。Graphics对象提供FillRegion方法,而Brush对象则是储存填充的特性,例如色彩或图样。下列范例将纯色填入区域中:

myGraphics.FillRegion(&mySolidBrush, &myRegion);

裁剪

裁剪是指限制对特定区域进行绘制。下图将显示 "Hello" 字符串被裁剪为心形的区域。

区域可从路径建立,而路径中可包含字符串的字型外框,因此您可以使用外框文字来进行裁剪。下图将显示裁减为文字字符串内景的一组同心椭圆。

若要使用裁剪来进行绘制,请建立Graphics对象,调用其SetClip方法,然后调用相同 Graphics 对象的绘图方法。下例将绘制一条被矩形区域裁剪的直线:

Region myRegion(Rect(20, 30, 100, 50));
myGraphics.DrawRectangle(&myPen, 20, 30, 100, 50);  
myGraphics.SetClip(&myRegion, CombineModeReplace);
myGraphics.DrawLine(&myPen, 0, 0, 200, 200);

下图显示了一个矩形区域裁剪的直线。

路径平直化

对象存储一系列的线条和贝塞尔样条。您可以加入多种类型的曲线(椭圆、弧线、基数样条)到路径中,但是其中每条曲线在存储为路径之前都将转化为贝塞尔样条。路径平直化处理表示将路径中的每条贝塞尔样条转化为一系列的直线。

若要平直化一条路径,需要调用GraphicsPath对象的Flatten方法。Flatten方法有一个平直度的参数,表示平直化后的路径与原始路径的最大距离。下图显示了一条路径平直化处理前后的情形:

线条和曲线的抗锯齿功能 

使用 GDI+ 绘制线条时,您必须提供线条的起始点和结束点,但不必提供该线条的个别像素相关信息。GDI+ 是与显示驱动程序软件搭配使用,来决定必须开启哪些像素,才可以在特定显示装置上显示该线条。

举这条从点 (4, 2) 到点 (16, 10) 的直色红线作为范例说明。假设坐标系统的原点在左上角,而度量单位为像素。同时我们也假设 X 轴指向右方,而 Y 轴则向下延伸。下图将显示在多重色彩背景上绘制的红色线条的放大画面。

用来呈现线条的红色像素是不透明的。这条直线的像素全部都是不透明的。此类线条绘制方式所产生的线条会出现锯齿形的外观,看起来有点像阶梯。这种将线条绘制成阶梯状的技术称为锯齿 (Aliasing);这种阶梯为理论线条的别名。

另一种更为复杂的绘制线条技术是同时使用透明的像素和不透明的像素。像素将设为纯红色或红色与背景色彩的混色,但是这需要视像素与线条接近的程度。此类绘制方式称为反锯齿 (Antialiasing),可产生肉眼感觉更为平滑的线条。下图将显示某些像素将与背景混色以产生反锯齿线条。

反锯齿 (亦称为平滑化) 也可套用至曲线。下图将显示平滑椭圆形的放大画面。

下图将显示同一个椭圆形的实际大小,一个未使用反锯齿功能,另一个则使用反锯齿功能。

若要绘制使用平滑的线条和曲线,请先建立一个 Graphics 类对象,并传递SmoothingModeAntiAlias给它的SetSmoothingMode方法。然后,调用Graphics 类的相同的绘图方法。

myGraphics.SetSmoothingMode(SmoothingModeAntiAlias);
myGraphics.DrawLine(&myPen, 0, 0, 12, 8);

SmoothingModeAntiAliasSmoothingMode枚举中的元素之一。

已发表 2007年2月6日 21:40 作者 wqw


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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多