PathPointType浅析(C#)
// 摘要:
// 使用指定的 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到底有什么秘密呢?
// 指定 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。
先给出一段测试函数,以后代码要用到:
//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]);
}
}
在按钮事件中引用下面的测试函数:
{
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)。
看一下这段测试代码:
{
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方法可以抽取任意两个标记间的路径。看一下例子:
{
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++,不过原理都一样。
不小心在哪点了隐藏,害得文章有一段不能显示,搞了半天,郁闷。睡觉!