VC.NET的GDI+编程入门教程之图形

基于直线的图形

  一、等边图形

  (一)长方形和正方形

  长方形是由四条边组成的具有四个直角的几何图形,为了绘制一个长方形,可以定义围成长方形的矩形值,或定义它的位置和尺寸。为了画一个矩形围成的长方形,可以使用Graphics::DrawRectangle()方法。

 

public: void DrawRectangle(Pen *pen, Rectangle rect);


  类似的长方形可以按照如下说明:

 


图一、长方形说明图示


  定义过一个矩形变量后,可以将它传递给上述的方法,例子代码如下:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
Pen *penCurrent = new Pen(Color::Red);
Rectangle Rect(20, 20, 248, 162);
e->Graphics->DrawRectangle(penCurrent, Rect);
}


  需要注意的是,也可以在方法的括号内定义画笔或矩形对象。

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
e->Graphics->DrawRectangle(new Pen(Color::Red), Rectangle(20, 20, 248, 162));
}


  这将产生如下效果图:

 


图二、绘制的长方形效果图


  一定要记住,矩形对象的第三个参数代表的是矩形的宽度,第四个参数代表的矩形的高度,这对于那些使用过GDI编程的人来说是容易混淆的 一点。GDI+定义的矩形对象与GDI定义的矩形对象是有区别的。实际上,为了定义所要画的长方形的位置和尺寸,Graphics类提供了如下版本的 DrawRectangle()方法:

 

public: void DrawRectangle(Pen *pen, int x, int y, int width, int height);
public: void DrawRectangle(Pen *pen, float x, float y, float width, float height);


  这次,长方形对象用一个定位点和它的宽度、高度来表示。这可以用如下的Windows坐标系统进行说明。

 


图三、Windows坐标系统


  在此基础上,上述的长方形可以按照如下方法进行绘制:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 e->Graphics->DrawRectangle(new Pen(Color::Red), 20, 20, 248, 162);
}


  正方形是四个边都相等的长方形,是长方形的特例。

  (二)一系列的长方形

  DrawRectangle()方法用于绘制一个长方形,如果打算绘制很多的矩形的话,你可以向前一步地,用Graphics::DrawRectangles()方法,它有两个版本,语法如下:

 

public: void DrawRectangles(Pen *pen, Rectangle rects[]);
public: void DrawRectangles(Pen *pen, RectangleF rects[]);


  这个方法需要一个Rectangle 或 RectangleF数组。它根据数组的不同的成员值绘制不同的长方形。下面是一个例子代码:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Red);
 Rectangle Rect[] = { Rectangle(20, 20, 120, 20),
   Rectangle(20, 50, 120, 30),
   Rectangle(20, 90, 120, 40),
   Rectangle(20, 140, 120, 60) };
 e->Graphics->DrawRectangles(penCurrent, Rect);
}


  上述代码产生如下的效果:

 


图四、一系列长方形效果图

  二、线条

  (一)一条直线

  直线连接了两个点,这意味着直线有起点和终点。

 


图五、直线示意图


  起点和终点是截然不同的两个点,正是基于这一点,直线也可以用两个点(Ponit)来表示,或者用笛卡尔坐标系中的四个坐标数值表示。Graphics提供了以下重载的DrawLine()方法来绘制一条直线,:

 

public: void DrawLine(Pen *pen, Point pt1, Point pt2);
public: void DrawLine(Pen *pen, PointF pt1, PointF pt2);
public: void DrawLine(Pen *pen, int x1, int y1, int x2, int y2);
public: void DrawLine(Pen *pen, float x1, float y1, float x2, float y2);


  如果直线用自然数表示,它的起点可以用pt1表示,终点用点pt2表示,如果直线用实数绘制,它在PointF pt1处开始,在PointF pt2处结束。或者,可以用坐标(x1, y1)来表示起点,用坐标(x2, y2)表示终点。同样类型的直线可以用十进制数从点(x1, y1) 处到点 (x2, y2).处。

  下面的代码画了三条直线:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Red);
 e->Graphics->DrawLine(penCurrent, 20, 20, 205, 20);
 penCurrent = new Pen(Color::Green);
 e->Graphics->DrawLine(penCurrent, 40, 40, 225, 40);
 penCurrent = new Pen(Color::Blue);
 e->Graphics->DrawLine(penCurrent, 30, 60, 215, 60);
}

 

 


图六:绘制三条直线


  (二)一系列直线

  上述的DrawLine()方法用来画一条直线,如果打算一次画一组直线的话,可以使用Graphics::DrawLines()方法,它重载了两个版本:

 

public: void DrawLines(Pen *pen, Point points[]);
public: void DrawLines(Pen *pen, PointF points[]);


  为了使用这种方法,需要使用代表笛卡尔坐标的自然数定义Point数组,或只用实数定义PointF数组,例子代码如下:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Point Coordinates[] = { Point(20, 10), Point(205, 20),
 Point(40, 40), Point(225, 60),
 Point(30, 80), Point(215, 100) };

 Pen *penCurrent = new Pen(Color::Red);
 e->Graphics->DrawLines(penCurrent, Coordinates);
}


  这将产生如下的效果:

 


图七、系列直线效果图

 

  三、多边形

  多边形是如若干个直线互联所围成的图形,换句话说,多边形有多个直线定义,除了第一根直线外,所有直线的起点都是前一根直线的终点,最后一根直线的终点是第一根直线的起点。

  为了画多边形,可以使用Graphics::Polygon()方法,它重载了两个版本:

public: void DrawPolygon(Pen *pen, Point points[]);
public: void DrawPolygon(Pen *pen, PointF points[]);

  使用这个方法时,首先声明一个Point 或 PointF类型的数组,并将它传递给函数的第二个参数。下面是一个例子的代码:

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Point Pt[] = { Point(20, 50), Point(180, 50), Point(180, 20),
  Point(230, 70), Point(180, 120), Point(180, 90),
  Point(20, 90) };
 Pen *penCurrent = new Pen(Color::Red);
 e->Graphics->DrawPolygon(penCurrent, Pt);
}

  这个将产生如下效果:


图八、多边形效果图

  基于圆的图形

  一、椭圆与圆

  连续弯曲的线条围成了椭圆。椭圆上的每一个点都相对于中心点来说都存在一个对称点,下图对它进行了说明:


图九、椭圆示意图


  因为一个椭圆可以放入到一个矩形中,所以在GDI+编程中,椭圆用它的外接矩形来定义。为了画一个椭圆,可以使用Graphics::DrawEllipse()方法,这个方法有四个版本:

public: void DrawEllipse(Pen *pen, Rectangle rect);
public: void DrawEllipse(Pen *pen, RectangleF rect);
public: void DrawEllipse(Pen *pen, int x, int y, int width, int height);
public: void DrawEllipse(Pen *pen, float x, float y, float width, float height);



图十、函数参数示意图


  这种方法参数的含义与Graphics::DrawRectangle()方法参数的含义是一样的。

  这里是一个例子:

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Red);
 e->Graphics->DrawEllipse(penCurrent, Rectangle(20, 20, 226, 144));
}



图十一、代码运行效果图


  二、饼图

  饼图是用一个起始角度和终止角度定位的椭圆的一部分,示意图如下:


图十二、饼图示意图


  为了画一个饼图,可以用Graphics::DrawPie()方法,它有下列的几个版本:

public: void DrawPie(Pen *pen, Rectangle rect,float startAngle,float sweepAngle);
public: void DrawPie(Pen *pen, RectangleF rect, float startAngle,float sweepAngle);
public: void DrawPie(Pen *pen,int x,int y,int width,int height, int startAngle, int sweepAngle);
public: void DrawPie(Pen *pen, float x, float y, float width, float height, float startAngle, float sweepAngle);


  饼图是基于椭圆的,椭圆所外接的矩形将作为rect参数传递,矩形也可以用定位点和尺寸来定义。

  对于所要绘制的椭圆的外接矩形中,可以设定一个起始角度,这个角度是按照顺时针方向从0度开始计算的(就象一个模拟钟一样)。这意味着90度在6点钟方向而不是在12点钟方向。这个开始的角度作为startAngle参数来传递。

  定义过起始角度后,还要定义饼图所覆盖的角度,这也是按照顺时针计算的。这个值使用sweepAngle参数来传递。

  下面有个例子:

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Red);
 e->Graphics->DrawPie(penCurrent, 20, 20, 200, 100, 45, 255);
}


  代码运行效果如下:


图十三、饼图效果

  三、弧线

  弧线是椭圆的一部分,这意味着弧线是一个非封闭的椭圆。尽管饼图是一个封闭的图形,但弧线不是。它仅仅是定义椭圆的边线部分。因为弧线必须与椭圆一致,它被定义为适应外接矩形,这可以用下图来说明:


图十四、弧线示意图


  为了支持弧线,Graphics 类提供了DrawArc()方法,这个方法定义了四个版本:

public: void DrawArc(Pen *pen, Rectangle rect, float startAngle, float sweepAngle);
public: void DrawArc(Pen *pen, RectangleF rect, float startAngle, float sweepAngle);
public: void DrawArc(Pen *pen, int x, int y, int width, int height, int startAngle, int sweepAngle);
public: void DrawArc(Pen *pen, float x, float y, float width, float height, float startAngle, float sweepAngle);


   含有弧线的椭圆必须在Rectangle或 RectangleF矩形内进行绘制,也可以用外接矩形的坐标、尺寸来定义椭圆,弧线必须与外接矩形相匹配外,还必须定义起始角度(起始点与X轴的顺时针 角度)。弧线还要定义从起始点顺时针所扫过的角度,这两个值与Graphics::Pie()方法中的值含义一样 ,下面是例子代码:

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Red);
 e->Graphics->DrawArc(penCurrent, 20, 20, 200, 150, 225, 200);
}



图十五、程序效果图

 

  基于曲线的图形

  一、曲线

   曲线是连接两点或多点的线条,如果仅仅涉及两个点,线条将把它们连接在一起,但这个线条不是直的。如果有三个点A、B、C,这条线将从A点开始,穿过B 点,结束于C点。如果多于三个点,这条线将起始与第一个点,依次穿过中间的若干点,结束于最后的点。曲线中的点不必排成一条直线,实际上,绘制曲线的整体 思想是用非直的线条连接不在一条直线上的点,下图的C 1、C2、C3可以说明这一点:

 


图十六、曲线示意图


  曲线C1包括两个点,曲线C2包括3个点,曲线C3包括4个点。

  两点间的部分称为线段。这也意味着曲线可以由包含 的线段的个数来进行区分。如果一个曲线仅仅由两个点构成,这意味着它只有一个线段,连接着第一、第二个点,如果一个曲线包括三个点,它有两个线段,第一个 线段跨越第一、第二个点,第二个线段跨越第二、第三个点。综上所述,曲线的线段数等于点数减去一。

  GDI+中使用Graphics::DrawCurve()方法画曲线,在画一个曲线时,必须说明所涉及到的点数,这意味着首先要声明一个Point 或PointF数组,正是考虑到这一点,Graphics类定义的DrawCurve()的方法如下:

 

public: void DrawCurve(Pen *pen, Point points[]);
public: void DrawCurve(Pen *pen, PointF points[]);


  这个版本的方法使用了Point 或 PointF值作为参数,数组的成员数由你规定,这里有一个例子使用四个点来绘制一个含有三个线段的曲线:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
Pen *penCurrent = new Pen(Color::Blue);
Point pt[] = { Point( 40, 42), Point(188, 246),
Point(484, 192), Point(350, 48) };
e->Graphics->DrawCurve(penCurrent, pt);
}


  它的效果图如下所示:

 


图十七、曲线效果图


  正如你所见到的,当画一个曲线时,一个弯曲的线条穿越起始点和终点之间的中间点,为了使线条是非直的,编译器使用一个称为张力的值来弯曲线条,这个张力值可以通过定义弯曲系数的形式来修改,为了这么做,使用下列版本的DrawCurve()方法:

 

public: void DrawCurve(Pen *pen, Point points[], float tension);
public: void DrawCurve(Pen *pen, PointF points[], float tension);


  弯曲程度由张力参数决定,它可以是大于等于0.00的十进制数,如果该值等于0.00,将画一个直线。下面是一个例子:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Blue);
 Point pt[] = { Point(40, 42), Point(188, 246),Point(484, 192), Point(350, 48) };
 e->Graphics->DrawCurve(penCurrent, pt, 0.00F);
}

 

 


图十八、代码运行效果图


  这意味着,如果想画一个真正的曲线,或者是不传递张力参数,使用这个方法的第一个版本,或者是传递的张力的参数值大于0.00。这里有个例子:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Blue);
 Point pt[] = { Point(40, 42), Point(188, 246),Point(484, 192), Point(350, 48) };
 e->Graphics->DrawCurve(penCurrent, pt, 2.15F);
}


  这将产生如下效果:

 


图十九、代码运行效果图


  DrawCurve()方法的两个版本准许在开始的第一个点绘制曲线,下面的例子使用了5个点,产生了4个线段:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Blue);
 PointF pt[] = { PointF(20.00F, 322.00F), PointF(124, 24),PointF(214, 242), PointF(275, 28),
 PointF(380.00F, 322.00F) };
 e->Graphics->DrawCurve(penCurrent, pt);
}


  效果如图所示:

 


图二十、代码运行效果图


  如果需要的话,可以随意在任意一个点开始曲线,为了支持这一点,Graphics类提供了以下版本的DrawCurve()方法:

 

public: void DrawCurve(Pen *pen, PointF[] points, int offset, int numberOfSegments);


  offset参数用来规定在开始绘制之前跳需要跃过的点数,首先需要决定的就是offset参数必须设置为0或者更高的数,如果将这个 参数设置为0,曲线将从第一个点开始绘制。如果这个参数设置为1,第一个点将不包含在曲线之内。这意味着曲线从第二个点开始绘制,以此类推。如果设置为 2,第一和第二个点都不在曲线内,曲线从第三个点开始。

  通过offset参数规定曲线的起点后,然后就需要设定曲线的段数,这个段数必须要小于可用的段数,它等于所有段落数减去offset值。下面是一个例子:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Blue);
 PointF pt[] = { PointF(20.00F, 322.00F), PointF(124, 24),PointF(214, 242), PointF(275, 28),
PointF(380.00F, 322.00F) };
 e->Graphics->DrawCurve(penCurrent, pt, 1, 2);
}


  效果图如下:

 


图二十一、代码运行效果图


  再一次,编译器在绘制曲线时需要申请张力,如果愿意使用一个直线段或使用一个不同于默认值的张力的话,可以使用下列版本的Graphics::DrawCurve()方法:

 

public: void DrawCurve(Pen *pen, Point[] points, int offset, int numberOfSegments, float tension);
public: void DrawCurve(Pen *pen, PointF[] points, int offset, int numberOfSegments, float tension);


  这次,你可以传递0值给张力,以此来得到直线,代码如下:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Blue);
 PointF pt[] = { PointF(20.00F, 322.00F), PointF(124, 24),PointF(214, 242), PointF(275, 28),PointF(380.00F, 322.00F) };
 e->Graphics->DrawCurve(penCurrent, pt, 0, 4, 0);
}


  效果如图:

 


图二十二、代码运行效果图


  也可以向张力参数传递任何正值,这有个例子:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Blue);
 PointF pt[] = { PointF(20.00F, 322.00F), PointF(124, 24), PointF(214, 242), PointF(275, 28),
PointF(380.00F, 322.00F) };
 e->Graphics->DrawCurve(penCurrent, pt, 1, 3, 1.750F);
}


  效果如图:

 


图二十三、代码运行效果图

  二、贝赛尔曲线

  贝赛尔曲线是用四个点(不必在一条直线上)绘制的连续曲线,它可以用下图来说明:

 


图二十四、贝赛尔曲线


  为了绘制这个线条(使用四个点),编译器将从第一点到第四个点画一条曲线,但是它并不经过第二、第三个点,而只是通过弯曲曲线来使中间的侧边各自接近于第二、第三个点。例如,上述的贝赛尔曲线使用了如下的四个点进行绘制:

 


图二十五、贝赛尔曲线绘制说明图


  为了绘制贝赛尔曲线,Graphics类提供了DrawBezier()方法,它重载了以下版本:

 

public: void DrawBezier(Pen *pen, Point pt1, Point pt2, Point pt3, Point pt4);
public: void DrawBezier(Pen *pen, PointF pt1, PointF pt2, PointF pt3, PointF pt4);
public: void DrawBezier(Pen *pen, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4);


  在此基础上,绘制贝赛尔曲线时可以使用四个Point 或PointF值,也可以使用四个点的坐标值。下面有一个例子:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Blue);
 Point pt1 = Point(20, 12), pt2 = Point(88, 246), pt3 = Point(364, 192), pt4 = Point(250, 48);
 e->Graphics->DrawBezier(penCurrent, pt1, pt2, pt3, pt4);
}


  效果图如下:

 


图二十六、贝赛尔曲线效果图


  三、一系列贝赛尔曲线

  Graphics::DrawBezier()方法用来绘制一条贝赛尔曲线,如果想绘制一系列贝赛尔曲线,可以用Graphics::DrawBeziers()方法,它重载了两个版本:

 

public: void DrawBeziers(Pen *pen, Point points[]);
public: void DrawBeziers(Pen *pen, PointF points[]);


  DrawBeziers()方法需要一个Point 或 PointF数组值。当仅仅处理四个点时,DrawBeziers() 方法与 DrawBezier()很相似。区别是DrawBezier()处理的是四个Point 或 PointF的值,DrawBeziers()处理的是Point 或 PointF数组值。使用DrawBeziers()方法可以绘制出与上面曲线一样的效果,代码如下:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Blue);
 Point pt[] = { Point(20, 12), Point(88, 246), Point(364, 192), Point(250, 48) };
 e->Graphics->DrawBeziers(penCurrent, pt);
}


  使用DrawBeziers()方法的一个典型特点是它允许使用7个Point或PointF值,这里有一个例子:

 

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Blue);
 Point pt[] = { Point( 10, 5), Point(340, 60), Point(320, 148), Point(150, 120), Point(24, 220), Point(250, 150), Point(304, 240) };
 e->Graphics->DrawBeziers(penCurrent, pt);
}


  效果图如下:

 


图二十七、代码运行效果图

  四、封闭曲线

  使用 DrawLines()、DrawBezier()或 DrawBeziers()方法将得到一条或一系列有起点有终点的直线或曲线,GDI+也允许绘制一系列的线段,但是最后的线段的终点和第一条线段的起点 是连接在一起的,形成了一个封闭的图形。为了绘制这种图形,可以使用Graphics::DrawClosedCurve()方法,该方法重载了四个版 本,其中的两个版本语法如下:

public: void DrawClosedCurve(Pen *pen, Point points[]);
public: void DrawClosedCurve(Pen *pen, PointF points[]);


  这两个版本非常容易使用,它们允许你提供4个Point 或PointF值的数组,在执行过程中,每一种版本都将绘制一条曲线,穿过每一个点,并连接终点和起点,这里有个例子:

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 Pen *penCurrent = new Pen(Color::Blue);
 Point pt[] = { Point(40, 42), Point(188, 246), Point(484, 192), Point(350, 48) };
 e->Graphics->DrawClosedCurve(penCurrent, pt);
}


  代码运行效果如下图:


图二十八、代码运行效果图


  上两个版本用来绘制线条,但对线条进行弯曲处理以使图形看起来平滑,如果需要的话,可以画直线来连接各点,而不必进行弯曲处理。基于这种假定,上面的形状将显示如下:


图二十九、直线连接的封闭图形


  为了绘制这种类型的图形,需要使用ClosedCurve()方法,可以使用以下版本:

public: void DrawClosedCurve(Pen *pen, Point points[], float tension, FillMode fillmode);
public: void DrawClosedCurve(Pen *pen, PointF points[], float tension, FillMode fillmode);


   这些版本可以规定张力大小及填充模式,张力系数使你可以定义曲线的实际形状,如果这个值是零的话,各个点将用直线进行互联。填充模式因数用来定义内部的 曲线如何来填充,这由FillMode枚举来进行控制,它定义在System::Drawing::Drawing2D命名空间中。枚举FillMode 有两个值,Alternate 和Winding,这里有一个例子:

private: System::Void Form1_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
 using namespace System::Drawing::Drawing2D;
 Pen *penCurrent = new Pen(Color::Red);
 Point pt[] = { Point(40, 42), Point(188, 246), Point(484, 192), Point(350, 48) };
 e->Graphics->DrawClosedCurve(penCurrent, pt, 0.75F, FillMode::Winding);
}


  这将产生影响如下效果图:


图三十、代码运行效果图
posted @ 2015-05-18 09:32  lurenzhao  阅读(682)  评论(0编辑  收藏  举报