GraphicsPath路径是由点来定义的,路径的点信息包括点的位置信息和点的类型信息两部分。在GDI+中点的类型是由枚举PathPointType定义的,本文就PathPointType的各成员进行简要分析,欢迎大家一起探讨!


path画点 ANdroid path points_数组

path画点 ANdroid path points_Line_02

GraphicsPath构造函数 
        //
        // 摘要:
        //     使用指定的 System.Drawing.Drawing2D.PathPointType 和 System.Drawing.Point 数组初始化
        //     System.Drawing.Drawing2D.GraphicsPath 类的新实例。
        //
        // 参数:
        //   pts:
        //     System.Drawing.Point 结构的数组,它定义组成此 System.Drawing.Drawing2D.GraphicsPath 的点的坐标。
        //
        //   types:
        //     System.Drawing.Drawing2D.PathPointType 枚举元素的数组,它指定 pts 数组中各相应点的类型。
        public GraphicsPath(Point[] pts, byte[] types);这是GraphicsPath的构造函数,其中types就是PathPointType类型的数组,那么PathPointType到底有什么秘密呢?

path画点 ANdroid path points_数组

path画点 ANdroid path points_Line_02

PathPointType定义 
    // 摘要:
    //     指定 System.Drawing.Drawing2D.GraphicsPath 对象中点的类型。
    public enum PathPointType
    {
        // 摘要:
        //     System.Drawing.Drawing2D.GraphicsPath 对象的起始点。
        Start = 0,
        //
        // 摘要:
        //     连线线段。
        Line = 1,
        //
        // 摘要:
        //     立体贝塞尔曲线。
        Bezier3 = 3,
        //
        // 摘要:
        //     默认贝塞尔曲线。
        Bezier = 3,
        //
        // 摘要:
        //     遮盖点。
        PathTypeMask = 7,
        //
        // 摘要:
        //     对应线段为虚线。
        DashMode = 16,
        //
        // 摘要:
        //     路径标记。
        PathMarker = 32,
        //
        // 摘要:
        //     子路径的终结点。
        CloseSubpath = 128,
    }

1、PathPointType.Start

子路径的起始点。任何GraphicsPath中的点数组的第一个点的类型都为PathPointType.Start,即使你把它赋值为PathPointType.Line或PathPointType.Bezier也会被改为PathPointType.Start。

先给出一段测试函数,以后代码要用到:

path画点 ANdroid path points_数组

path画点 ANdroid path points_Line_02

测试GraphicsPath代码

//别忘记添加引用:
//using System.Drawing;
//using System.Drawing.Drawing2D;
private void Draw(GraphicsPath gp)
{
    GraphicsPath g = this.CreateGraphics();
    g.DrawPath(Pens.Red, gp);
    int pl = gp.PathData.Points.Length;
    for (int i = 0; i < pl; i++)
    {
        g.DrawString(gp.PathData.Types[i].ToString(), this.Font, Brushes.Blue, gp.PathData.Points[i]);
    }
}

path画点 ANdroid path points_数组

path画点 ANdroid path points_Line_02

代码

private void TestPathPointTypeStart()
{
    Point[] ps = new Point[] { new Point(0, 0), new Point(0, 300), new Point(500, 300), new Point(500, 0) };
    byte[] ts = new byte[] { (byte)PathPointType.Line, (byte)PathPointType.Bezier, (byte)PathPointType.Bezier, (byte)PathPointType.Bezier };
    GraphicsPath gp = new GraphicsPath(ps, ts);
    Draw(gp);
}

 运行后你会发现第一个点的类型不是1而是0,而第一个点最终作为Bezier曲线的端点渲染,这是有第二个点的类型决定的。

2、PathPointType.Line

不需要多解释,标记该点为直线的端点。

3、PathPointType.Bezier

标记该点为Bezier曲线的端点或控制点。一段Bezier曲线有4个点,如果描述多段Bezier曲线需要3N+1个点。如果连续的类型标记为PathPointType.Bezier的点的个数不为3N+1的话是无法建立GraphicsPath的!

前三种类型是用来定义路径的,路径的形状是由前三种类型决定的,每个点的类型必须是这三种之一!

后面几种则是用来标记的,他们不会影响路径的形状。

4、PathPointType.PathTypeMask

遮盖点。该值不应该用来定义GraphicsPath,它其实是个掩码,对于任何一个PathPointType类型的变量,将它与PathPointType.PathTypeMask进行"&"操作得到的就是该点形状属性(PathPointType.Start、PathPointType.Line或PathPointType.Bezier)。

看一下这段测试代码:

path画点 ANdroid path points_数组

path画点 ANdroid path points_Line_02

测试PathTypeMask

private void TestPathPointTypePathTypeMask()
{
    GraphicsPath gp = new GraphicsPath();
    gp.AddString("火", this.Font.FontFamily, 0, 512, new Point(0, 0), StringFormat.GenericDefault);
    Draw(gp);
    string s = "";
    foreach (byte b in gp.PathTypes)
    {
        s += b & (byte)PathPointType.PathTypeMask;
        s += ",";
    }
    MessageBox.Show(s);
}

 窗体上显示的是点的实际类型值,你可以发现有131这样的值,它是PathPointType.Bezier与PathPointType.CloseSubpath的和,表示该点是Bezier曲线而且还是子路径的结束点。而弹出的对话框上显示的是进行"&"操作后得到的。

5、PathPointType.DashMode

标记对应线段为虚线。必须和前三个类型一起使用,如PathPointType.Bezier|PathPointType.DashMode;如果单独使用PathPointType.DashMode运行将不会有结果。但在实际使用时,GDI+并不会将对应线段渲染为虚线,我觉得该标记只是个摆设,建议大家忽视。

6、PathPointType.PathMarker

路径标记点。同样必须和前三个类型一起使用。使用GraphicsPathIterator类的NextMarker方法可以抽取任意两个标记间的路径。看一下例子:

path画点 ANdroid path points_数组

path画点 ANdroid path points_Line_02

PathMarker示例

private void TestGraphicsPathIterator()
{
    GraphicsPath gp = new GraphicsPath();
    gp.AddRectangle(new Rectangle(50, 50, 300, 300));
    gp.AddLines(new Point[] { new Point(100, 100), new Point(500, 100), new Point(200, 300) });
    gp.SetMarkers();
    gp.AddCurve(new Point[] { new Point(100, 100), new Point(60, 200), new Point(200, 360) });
    gp.CloseFigure();
    gp.AddEllipse(new Rectangle(0, 0, 100, 100));
    gp.SetMarkers();
    gp.AddLine(new Point(90, 100), new Point(300, 270));
    Draw(gp);
    //GraphicsPathIterator gpi = new GraphicsPathIterator(gp);
    //int start = 0;
    //int end = 0;
    //int count = 0;
    //count = gpi.NextMarker(out start, out end);//这行代码就是识别PathMarker的,抽取当前PathMarker到下一个PathMarker之间的路径。执行一次表示抽取第一个点到首次用PathMarker标识的点之间的路径。你可以在执行一次试试!
    //PointF[] points = new PointF[count];
    //byte[] types = new byte[count];
    //gpi.CopyData(ref points, ref types, start, end);
    //GraphicsPath gp2 = new GraphicsPath(points, types);
    //this.CreateGraphics().Clear(this.BackColor);
    //Draw(gp2);
}

 首先,运行后显示所有的路径。

然后把注释去掉,执行所有的语句,运行后显示一部分路径。

最后把count = gpi.NextMarker(out start, out end);执行两次(复制一行就行!),运行后显示另一部分路径。

解释一下上述代码:

添加一个矩形—>添加一组线段—>设置标记点—>添加一段曲线—>封闭曲线—>添加一个椭圆—>设置标记点—>添加一直线

这是完整的建立Path的过程,Path会认为把第一个点是标记点,所以执行一次NextMarker方法,将抽取矩形和一组线段,再执行一次NextMarker方法,将抽取封闭曲线和椭圆。

7、PathPointType.CloseSubpath

前面已经提过,标记子路径的结束点。如果一个路径有多个子路径,每个子路径的最后一点要用CloseSubpath标识,当然它也必须和前三种类型一起使用。使用GraphicsPathIterator类的NextSubpath方法可以抽取任意一个子路径,使用方法类似NextMarker,这里不再给出代码了。


本文就当入门,相信大家已经对PathPointType有了一定的了解,其实GraphicsPathIterator是一个很有用的类,它封装了很多对PathPointType处理的方法,建议大家花时间研究一下!

推荐参考书籍《精通GDI+编程》,示例代码是C++,不过原理都一样。


不小心在哪点了隐藏,害得文章有一段不能显示,搞了半天,郁闷。睡觉!