我目前正在动态创建一个位图并使用位图中的图形对象在其上绘制一个字符串,如下所示:
System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(bmp);
graph.DrawString(text, font, brush, new System.Drawing.Point(0, 0));
这将返回一个矩形位图,其中字符串从左到右直接书写。我还想把绳子画成彩虹的形状。我怎样才能做到这一点?
我目前正在动态创建一个位图并使用位图中的图形对象在其上绘制一个字符串,如下所示:
System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(bmp);
graph.DrawString(text, font, brush, new System.Drawing.Point(0, 0));
这将返回一个矩形位图,其中字符串从左到右直接书写。我还想把绳子画成彩虹的形状。我怎样才能做到这一点?
我最近遇到了这个问题(我正在渲染要打印到 CD 上的文本),所以这是我的解决方案:
private void DrawCurvedText(Graphics graphics, string text, Point centre, float distanceFromCentreToBaseOfText, float radiansToTextCentre, Font font, Brush brush)
{
// Circumference for use later
var circleCircumference = (float)(Math.PI * 2 * distanceFromCentreToBaseOfText);
// Get the width of each character
var characterWidths = GetCharacterWidths(graphics, text, font).ToArray();
// The overall height of the string
var characterHeight = graphics.MeasureString(text, font).Height;
var textLength = characterWidths.Sum();
// The string length above is the arc length we'll use for rendering the string. Work out the starting angle required to
// centre the text across the radiansToTextCentre.
float fractionOfCircumference = textLength / circleCircumference;
float currentCharacterRadians = radiansToTextCentre + (float)(Math.PI * fractionOfCircumference);
for (int characterIndex = 0; characterIndex < text.Length; characterIndex++)
{
char @char = text[characterIndex];
// Polar to cartesian
float x = (float)(distanceFromCentreToBaseOfText * Math.Sin(currentCharacterRadians));
float y = -(float)(distanceFromCentreToBaseOfText * Math.Cos(currentCharacterRadians));
using (GraphicsPath characterPath = new GraphicsPath())
{
characterPath.AddString(@char.ToString(), font.FontFamily, (int)font.Style, font.Size, Point.Empty,
StringFormat.GenericTypographic);
var pathBounds = characterPath.GetBounds();
// Transformation matrix to move the character to the correct location.
// Note that all actions on the Matrix class are prepended, so we apply them in reverse.
var transform = new Matrix();
// Translate to the final position
transform.Translate(centre.X + x, centre.Y + y);
// Rotate the character
var rotationAngleDegrees = currentCharacterRadians * 180F / (float)Math.PI - 180F;
transform.Rotate(rotationAngleDegrees);
// Translate the character so the centre of its base is over the origin
transform.Translate(-pathBounds.Width / 2F, -characterHeight);
characterPath.Transform(transform);
// Draw the character
graphics.FillPath(brush, characterPath);
}
if (characterIndex != text.Length - 1)
{
// Move "currentCharacterRadians" on to the next character
var distanceToNextChar = (characterWidths[characterIndex] + characterWidths[characterIndex + 1]) / 2F;
float charFractionOfCircumference = distanceToNextChar / circleCircumference;
currentCharacterRadians -= charFractionOfCircumference * (float)(2F * Math.PI);
}
}
}
private IEnumerable<float> GetCharacterWidths(Graphics graphics, string text, Font font)
{
// The length of a space. Necessary because a space measured using StringFormat.GenericTypographic has no width.
// We can't use StringFormat.GenericDefault for the characters themselves, as it adds unwanted spacing.
var spaceLength = graphics.MeasureString(" ", font, Point.Empty, StringFormat.GenericDefault).Width;
return text.Select(c => c == ' ' ? spaceLength : graphics.MeasureString(c.ToString(), font, Point.Empty, StringFormat.GenericTypographic).Width);
}
我认为唯一的方法是单独渲染每个字符并使用
Graphics.RotateTransform
旋转文本。您需要自己计算旋转角度和渲染偏移。您可以使用
Graphics.MeasureCharacterRanges
获取每个字符的大小。
不幸的是,在 GDI+ 中,无法将字符串附加到路径(这是您要寻找的)。
因此,做到这一点的唯一方法是“手工”完成。这意味着将字符串拆分为字符并根据您自己的路径计算放置它们。
除非您想为此投入大量工作,否则您应该尝试找到一个库(可能完全替代 GDI+)来执行此操作或放弃您的彩虹。
使用 WPF,您可以在路径上呈现文本(有关操作方法,请参阅链接)
我需要在原始 C# 中回答这个问题,但找不到任何这样做的示例:
这个解决方案需要大量的数学来解决。总之,它需要一组点(2D 向量),将它们与基线对齐,然后围绕样条曲线弯曲这些点。代码足够快,可以实时更新并处理循环等。
为方便起见,该解决方案使用 GraphicsPath.AddString 将文本转换为矢量,并使用 GraphicsPath.PathPoints/PathTypes 作为点,但是您可以使用相同的函数弯曲任何形状甚至位图。(虽然我不建议实时制作 4k 位图)。
该代码有一个简单的 Paint 函数,后跟 Spline 类。在 Paint 方法中使用 GraphicsPath 以使代码更容易理解。ClickedPoints 是您希望文本弯曲的点数组。我使用了在鼠标事件中添加的列表,如果您事先知道这些点,请使用数组。
public void Paint(System.Drawing.Graphics g)
{
List<System.Drawing.Point> clickedPoints = new List<System.Drawing.Point>();
Additional.Math.Beziers.BezierSplineCubic2D _Spline = new Additional.Math.Beziers.BezierSplineCubic2D();
// Create the spline, exit if no points to bend around.
System.Drawing.PointF[] cd = Additional.Math.Beziers.BezierSplineCubic2D.CreateCurve(ClickedPoints.ToArray(), 0, ClickedPoints.Count, 0);
_Spline = new Additional.Math.Beziers.BezierSplineCubic2D(cd);
if (_Spline.Beziers == null || _Spline.Length == 0) return;
// Start Optional: Remove if you only want the bent object
// Draw the spline curve the text will be put onto using inbuilt GDI+ calls
g.DrawCurve(System.Drawing.Pens.Blue, clickedPoints.ToArray());
// draw the control point data for the curve
for (int i = 0; i < cd.Length; i++)
{
g.DrawRectangle(System.Drawing.Pens.Green, cd[i].X - 2F, cd[i].Y - 2F, 4F, 4F);
}
// End Optional:
// Turn the text into points that can be bent - if no points then exit:
System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
path.AddString("Lorem ipsum dolor", new System.Drawing.FontFamily("Arial"), 0, 12.0F, new System.Drawing.Point(0, 0), new System.Drawing.StringFormat() { Alignment = System.Drawing.StringAlignment.Near });
textBounds = path.GetBounds();
curvedData = (System.Drawing.PointF[])path.PathPoints.Clone();
curvedTypes = (byte[])path.PathTypes.Clone();
dataLength = curvedData.Length;
if (dataLength == 0) return;
// Bend the shape(text) around the path (Spline)
_Spline.BendShapeToSpline(textBounds, dataLength, ref curvedData, ref curvedTypes);
// draw the transformed text path
System.Drawing.Drawing2D.GraphicsPath textPath = new System.Drawing.Drawing2D.GraphicsPath(curvedData, curvedTypes);
g.DrawPath(System.Drawing.Pens.Black, textPath);
}
现在对于样条类:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Additional.Math
{
namespace Vectors
{
public struct Vector2DFloat
{
public float X;
public float Y;
public void SetXY(float x, float y)
{
X = x;
Y = y;
}
public static Vector2DFloat Lerp(Vector2DFloat v0, Vector2DFloat v1, float t)
{
return v0 + (v1 - v0) * t;
}
public Vector2DFloat(Vector2DFloat value)
{
this.X = value.X;
this.Y = value.Y;
}
public Vector2DFloat(float x, float y)
{
this.X = x;
this.Y = y;
}
public Vector2DFloat Rotate90Degrees(bool positiveRotation)
{
return positiveRotation ? new Vector2DFloat(-Y, X) : new Vector2DFloat(Y, -X);
}
public Vector2DFloat Normalize()
{
float magnitude = (float)System.Math.Sqrt(X * X + Y * Y);
return new Vector2DFloat(X / magnitude, Y / magnitude);
}
public float Distance(Vector2DFloat target)
{
return (float)System.Math.Sqrt((X - target.X) * (X - target.X) + (Y - target.Y) * (Y - target.Y));
}
public float DistanceSquared(Vector2DFloat target)
{
return (X - target.X) * (X - target.X) + (Y - target.Y) * (Y - target.Y);
}
public double DistanceTo(Vector2DFloat target)
{
return System.Math.Sqrt(System.Math.Pow(target.X - X, 2F) + System.Math.Pow(target.Y - Y, 2F));
}
public System.Drawing.PointF ToPointF()
{
return new System.Drawing.PointF(X, Y);
}
public Vector2DFloat(System.Drawing.PointF value)
{
this.X = value.X;
this.Y = value.Y;
}
public static implicit operator Vector2DFloat(System.Drawing.PointF value)
{
return new Vector2DFloat(value);
}
public static Vector2DFloat operator +(Vector2DFloat first, Vector2DFloat second)
{
return new Vector2DFloat(first.X + second.X, first.Y + second.Y);
}
public static Vector2DFloat operator -(Vector2DFloat first, Vector2DFloat second)
{
return new Vector2DFloat(first.X - second.X, first.Y - second.Y);
}
public static Vector2DFloat operator *(Vector2DFloat first, float second)
{
return new Vector2DFloat(first.X * second, first.Y * second);
}
public static Vector2DFloat operator *(float first, Vector2DFloat second)
{
return new Vector2DFloat(second.X * first, second.Y * first);
}
public static Vector2DFloat operator *(Vector2DFloat first, int second)
{
return new Vector2DFloat(first.X * second, first.Y * second);
}
public static Vector2DFloat operator *(int first, Vector2DFloat second)
{
return new Vector2DFloat(second.X * first, second.Y * first);
}
public static Vector2DFloat operator *(Vector2DFloat first, double second)
{
return new Vector2DFloat((float)(first.X * second), (float)(first.Y * second));
}
public static Vector2DFloat operator *(double first, Vector2DFloat second)
{
return new Vector2DFloat((float)(second.X * first), (float)(second.Y * first));
}
public override bool Equals(object obj)
{
return this.Equals((Vector2DFloat)obj);
}
public bool Equals(Vector2DFloat p)
{
// If parameter is null, return false.
if (p == null)
{
return false;
}
// Optimization for a common success case.
if (this == p)
{
return true;
}
// If run-time types are not exactly the same, return false.
if (this.GetType() != p.GetType())
{
return false;
}
// Return true if the fields match.
// Note that the base class is not invoked because it is
// System.Object, which defines Equals as reference equality.
return (X == p.X) && (Y == p.Y);
}
public override int GetHashCode()
{
return (int)(System.Math.Round(X + Y, 4) * 10000);
}
public static bool operator ==(Vector2DFloat first, Vector2DFloat second)
{
// Check for null on left side.
if (first == null)
{
if (second == null)
{
// null == null = true.
return true;
}
// Only the left side is null.
return false;
}
// Equals handles case of null on right side.
return first.Equals(second);
}
public static bool operator !=(Vector2DFloat first, Vector2DFloat second)
{
return !(first == second);
}
}
}
namespace Beziers
{
public struct BezierCubic2D
{
public Vectors.Vector2DFloat P0;
public Vectors.Vector2DFloat P1;
public Vectors.Vector2DFloat P2;
public Vectors.Vector2DFloat P3;
public int ArcLengthDivisionCount;
public List<float> ArcLengths { get { if (_ArcLengths.Count == 0) CalculateArcLength(); return _ArcLengths; } }
public float ArcLength { get { if (_ArcLength == 0.0F) CalculateArcLength(); return _ArcLength; } }
private Vectors.Vector2DFloat A;
private Vectors.Vector2DFloat B;
private Vectors.Vector2DFloat C;
private List<float> _ArcLengths;
private float _ArcLength;
public BezierCubic2D(Vectors.Vector2DFloat p0, Vectors.Vector2DFloat p1, Vectors.Vector2DFloat p2, Vectors.Vector2DFloat p3)
{
P0 = p0;
P1 = p1;
P2 = p2;
P3 = p3;
// vt = At^3 + Bt^2 + Ct + p0
A = P3 - 3 * P2 + 3 * P1 - P0;
B = 3 * P2 - 6 * P1 + 3 * P0;
C = 3 * P1 - 3 * P0;
ArcLengthDivisionCount = 100;
_ArcLengths = new List<float>();
_ArcLength = 0.0F;
}
public BezierCubic2D(System.Drawing.PointF p0, System.Drawing.PointF p1, System.Drawing.PointF p2, System.Drawing.PointF p3)
{
P0 = p0;
P1 = p1;
P2 = p2;
P3 = p3;
// vt = At^3 + Bt^2 + Ct + p0
A = P3 - 3 * P2 + 3 * P1 - P0;
B = 3 * P2 - 6 * P1 + 3 * P0;
C = 3 * P1 - 3 * P0;
ArcLengthDivisionCount = 100;
_ArcLengths = new List<float>();
_ArcLength = 0.0F;
}
public BezierCubic2D(float p0X, float p0Y, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y)
{
P0 = new Vectors.Vector2DFloat(p0X, p0Y);
P1 = new Vectors.Vector2DFloat(p1X, p1Y);
P2 = new Vectors.Vector2DFloat(p2X, p2Y);
P3 = new Vectors.Vector2DFloat(p3X, p3Y);
// vt = At^3 + Bt^2 + Ct + p0
A = P3 - 3 * P2 + 3 * P1 - P0;
B = 3 * P2 - 6 * P1 + 3 * P0;
C = 3 * P1 - 3 * P0;
ArcLengthDivisionCount = 100;
_ArcLengths = new List<float>();
_ArcLength = 0.0F;
}
public Vectors.Vector2DFloat PointOnCurve(float t)
{
return A * System.Math.Pow(t, 3) + B * System.Math.Pow(t, 2) + C * t + P0;
}
public Vectors.Vector2DFloat PointOnCurveGeometric(float t)
{
Vectors.Vector2DFloat p4 = Vectors.Vector2DFloat.Lerp(P0, P1, t);
Vectors.Vector2DFloat p5 = Vectors.Vector2DFloat.Lerp(P1, P2, t);
Vectors.Vector2DFloat p6 = Vectors.Vector2DFloat.Lerp(P2, P3, t);
Vectors.Vector2DFloat p7 = Vectors.Vector2DFloat.Lerp(p4, p5, t);
Vectors.Vector2DFloat p8 = Vectors.Vector2DFloat.Lerp(p5, p6, t);
return Vectors.Vector2DFloat.Lerp(p7, p8, t);
}
public Vectors.Vector2DFloat PointOnCurveTangent(float t)
{
return 3 * A * System.Math.Pow(t, 2) + 2 * B * t + C;
}
public Vectors.Vector2DFloat PointOnCurvePerpendicular(float t, bool positiveRotation)
{
return (3 * A * System.Math.Pow(t, 2) + 2 * B * t + C).Rotate90Degrees(positiveRotation).Normalize() * 10F + PointOnCurve(t);
}
public Vectors.Vector2DFloat PointOnCurvePerpendicular(float t, bool positiveRotation, float pointHeight)
{
return (3 * A * System.Math.Pow(t, 2) + 2 * B * t + C).Rotate90Degrees(positiveRotation).Normalize() * pointHeight + PointOnCurve(t);
}
public float FindTAtPointOnBezier(float u)
{
float t;
int index = _ArcLengths.BinarySearch(u);
if (index >= 0)
t = index / (float)(_ArcLengths.Count - 1);
else if (index * -1 >= _ArcLengths.Count)
t = 1;
else if (index == 0)
t = 0;
else
{
index *= -1;
float lengthBefore = _ArcLengths[index - 1];
float lengthAfter = _ArcLengths[index];
float segmentLength = lengthAfter - lengthBefore;
float segmentFraction = (u - lengthBefore) / segmentLength;
// add that fractional amount to t
t = (index + segmentFraction) / (float)(_ArcLengths.Count - 1);
}
return t;
}
private void CalculateArcLength()
{
// calculate Arc Length through successive approximation. Use the squared version as it is faster.
_ArcLength = 0.0F;
int arrayMax = ArcLengthDivisionCount + 1;
_ArcLengths = new List<float>(arrayMax)
{
0.0F
};
Vectors.Vector2DFloat prior = P0, current;
for (int i = 1; i < arrayMax; i++)
{
current = PointOnCurve(i / (float)ArcLengthDivisionCount);
_ArcLength += current.Distance(prior);
_ArcLengths.Add(_ArcLength);
prior = current;
}
}
public override bool Equals(object obj)
{
return this.Equals((BezierCubic2D)obj);
}
public bool Equals(BezierCubic2D p)
{
// If parameter is null, return false.
if (p == null)
{
return false;
}
// Optimization for a common success case.
if (this == p)
{
return true;
}
// If run-time types are not exactly the same, return false.
if (this.GetType() != p.GetType())
{
return false;
}
// Return true if the fields match.
// Note that the base class is not invoked because it is
// System.Object, which defines Equals as reference equality.
return (P0 == p.P0) && (P1 == p.P1) && (P2 == p.P2) && (P3 == p.P3);
}
public override int GetHashCode()
{
return P0.GetHashCode() + P1.GetHashCode() + P2.GetHashCode() + P3.GetHashCode() % int.MaxValue;
}
public static bool operator ==(BezierCubic2D first, BezierCubic2D second)
{
// Check for null on left side.
if (first == null)
{
if (second == null)
{
// null == null = true.
return true;
}
// Only the left side is null.
return false;
}
// Equals handles case of null on right side.
return first.Equals(second);
}
public static bool operator !=(BezierCubic2D first, BezierCubic2D second)
{
return !(first == second);
}
}
public struct BezierSplineCubic2D
{
public BezierCubic2D[] Beziers;
public BezierCubic2D this[int index] { get { return Beziers[index]; } }
public int Length { get { return Beziers.Length; } }
public List<float> ArcLengths { get { if (_ArcLengths.Count == 0) CalculateArcLength(); return _ArcLengths; } }
public float ArcLength { get { if (_ArcLength == 0.0F) CalculateArcLength(); return _ArcLength; } }
private List<float> _ArcLengths;
private float _ArcLength;
public BezierSplineCubic2D(Vectors.Vector2DFloat[] source)
{
if (source == null || source.Length < 4 || (source.Length - 4) % 3 != 0) { Beziers = null; _ArcLength = 0.0F; _ArcLengths = new List<float>(); return; }
int length = ((source.Length - 4) / 3) + 1;
Beziers = new BezierCubic2D[length];
Beziers[0] = new BezierCubic2D(source[0], source[1], source[2], source[3]);
for (int i = 1; i < length; i++)
Beziers[i] = new BezierCubic2D(source[(i * 3)], source[(i * 3) + 1], source[(i * 3) + 2], source[(i * 3) + 3]);
_ArcLength = 0.0F;
_ArcLengths = new List<float>();
}
public BezierSplineCubic2D(System.Drawing.PointF[] source)
{
if (source == null || source.Length < 4 || (source.Length - 4) % 3 != 0) { Beziers = null; _ArcLength = 0.0F; _ArcLengths = new List<float>(); return; }
int length = ((source.Length - 4) / 3) + 1;
Beziers = new BezierCubic2D[length];
Beziers[0] = new BezierCubic2D(source[0], source[1], source[2], source[3]);
for (int i = 1; i < length; i++)
Beziers[i] = new BezierCubic2D(source[(i * 3)], source[(i * 3) + 1], source[(i * 3) + 2], source[(i * 3) + 3]);
_ArcLength = 0.0F;
_ArcLengths = new List<float>();
}
public BezierSplineCubic2D(System.Drawing.Point[] source)
{
if (source == null || source.Length < 4 || (source.Length - 4) % 3 != 0) { Beziers = null; _ArcLength = 0.0F; _ArcLengths = new List<float>(); return; }
int length = ((source.Length - 4) / 3) + 1;
Beziers = new BezierCubic2D[length];
Beziers[0] = new BezierCubic2D(source[0], source[1], source[2], source[3]);
for (int i = 1; i < length; i++)
Beziers[i] = new BezierCubic2D(source[(i * 3)], source[(i * 3) + 1], source[(i * 3) + 2], source[(i * 3) + 3]);
_ArcLength = 0.0F;
_ArcLengths = new List<float>();
}
public bool FindTAtPointOnSpline(float distanceAlongSpline, out BezierCubic2D bezier, out float t)
{
// to do: cache last distance and bezier. if new distance > old then start from old bezier.
if (distanceAlongSpline > ArcLength) { bezier = Beziers[Beziers.Length - 1]; t = distanceAlongSpline / ArcLength; return false; }
if (distanceAlongSpline <= 0.0F)
{
bezier = Beziers[0];
t = 0.0F;
return true;
}
for (int i = 0; i < Beziers.Length; i++)
{
float distanceRemainingBeyondCurrentBezier = distanceAlongSpline - Beziers[i].ArcLength;
if (distanceRemainingBeyondCurrentBezier < 0.0F)
{
// t is in current bezier.
bezier = Beziers[i];
t = bezier.FindTAtPointOnBezier(distanceAlongSpline);
return true;
}
else if (distanceRemainingBeyondCurrentBezier == 0.0F)
{
// t is 1.0F. Bezier is current one.
bezier = Beziers[i];
t = 1.0F;
return true;
}
// reduce the distance by the length of the bezier.
distanceAlongSpline -= Beziers[i].ArcLength;
}
// point is outside the spline.
bezier = new BezierCubic2D();
t = 0.0F;
return false;
}
public void BendShapeToSpline(System.Drawing.RectangleF bounds, int dataLength, ref System.Drawing.PointF[] data, ref byte[] dataTypes)
{
System.Drawing.PointF pt;
// move the origin for the data to 0,0
float left = bounds.Left, height = bounds.Y + bounds.Height;
for (int i = 0; i < dataLength; i++)
{
pt = data[i];
float textX = pt.X - left;
float textY = pt.Y - height;
if (FindTAtPointOnSpline(textX, out BezierCubic2D bezier, out float t))
{
data[i] = bezier.PointOnCurvePerpendicular(t, true, textY).ToPointF();
}
else
{
// roll back all points until we reach curvedTypes[i] == 0
for (int j = i - 1; j > -1; j--)
{
if ((dataTypes[j] & 0x80) == 0x80)
{
System.Drawing.PointF[] temp1 = new System.Drawing.PointF[j + 1];
Array.Copy(data, 0, temp1, 0, j + 1);
byte[] temp2 = new byte[j + 1];
Array.Copy(dataTypes, 0, temp2, 0, j + 1);
data = temp1;
dataTypes = temp2;
break;
}
}
break;
}
}
}
private void CalculateArcLength()
{
_ArcLength = 0.0F;
_ArcLengths = new List<float>(Beziers.Length);
for (int i = 0; i < Beziers.Length; i++)
{
_ArcLength += Beziers[i].ArcLength;
_ArcLengths.Add(_ArcLength);
}
}
internal static System.Drawing.PointF[] GetCurveTangents(System.Drawing.Point[] points, int count, float tension, int curveType)
{
if (points == null)
throw new ArgumentNullException("points");
System.Drawing.PointF[] pointfs = new System.Drawing.PointF[count];
for (int p = 0; p < count; p++)
{
pointfs[p] = new System.Drawing.PointF(points[p].X, points[p].Y);
}
return GetCurveTangents(pointfs, count, tension, curveType);
}
internal static System.Drawing.PointF[] GetCurveTangents(System.Drawing.PointF[] points, int count, float tension, int curveType)
{
float coefficient = tension / 3f;
System.Drawing.PointF[] tangents = new System.Drawing.PointF[count];
if (count < 2)
return tangents;
for (int i = 0; i < count; i++)
{
int r = i + 1;
int s = i - 1;
if (r >= count)
r = count - 1;
if (curveType == 0) // 0 == CurveType.Open
{
if (s < 0)
s = 0;
}
else // 1 == CurveType.Closed, end point jumps to start point
{
if (s < 0)
s += count;
}
tangents[i].X += (coefficient * (points[r].X - points[s].X));
tangents[i].Y += (coefficient * (points[r].Y - points[s].Y));
}
return tangents;
}
internal static System.Drawing.PointF[] CreateCurve(System.Drawing.Point[] points, int offset, int length, int curveType)
{
if (points == null)
throw new ArgumentNullException("points");
System.Drawing.PointF[] pointfs = new System.Drawing.PointF[length];
for (int p = 0; p < length; p++)
{
pointfs[p] = new System.Drawing.PointF(points[p].X, points[p].Y);
}
System.Drawing.PointF[] tangents = GetCurveTangents(pointfs, length, 0.5F, 0);
return CreateCurve(pointfs, tangents, offset, length, curveType);
}
internal static System.Drawing.PointF[] CreateCurve(System.Drawing.Point[] points, System.Drawing.PointF[] tangents, int offset, int length, int curveType)
{
if (points == null)
throw new ArgumentNullException("points");
System.Drawing.PointF[] pointfs = new System.Drawing.PointF[length];
for (int p = 0; p < length; p++)
{
pointfs[p] = new System.Drawing.PointF(points[p].X, points[p].Y);
}
return CreateCurve(pointfs, tangents, offset, length, curveType);
}
internal static System.Drawing.PointF[] CreateCurve(System.Drawing.PointF[] points, System.Drawing.PointF[] tangents, int offset, int length, int curveType)
{
List<System.Drawing.PointF> curve = new List<System.Drawing.PointF>();
int i;
Append(curve, points[offset].X, points[offset].Y, true);
for (i = offset; i < offset + length - 1; i++)
{
int j = i + 1;
float x1 = points[i].X + tangents[i].X;
float y1 = points[i].Y + tangents[i].Y;
float x2 = points[j].X - tangents[j].X;
float y2 = points[j].Y - tangents[j].Y;
float x3 = points[j].X;
float y3 = points[j].Y;
AppendBezier(curve, x1, y1, x2, y2, x3, y3, false);
}
return curve.ToArray<System.Drawing.PointF>();
}
internal static void Append(List<System.Drawing.PointF> points, float x, float y, bool compress)
{
System.Drawing.PointF pt = System.Drawing.PointF.Empty;
/* in some case we're allowed to compress identical points */
if (compress && (points.Count > 0))
{
/* points (X, Y) must be identical */
System.Drawing.PointF lastPoint = points[points.Count - 1];
if ((lastPoint.X == x) && (lastPoint.Y == y))
{
return;
}
}
pt.X = x;
pt.Y = y;
points.Add(pt);
}
internal static void AppendBezier(List<System.Drawing.PointF> points, float x1, float y1, float x2, float y2, float x3, float y3, bool isReverseWindingOnFill)
{
if (isReverseWindingOnFill)
{
Append(points, y1, x1, false);
Append(points, y2, x2, false);
Append(points, y3, x3, false);
}
else
{
Append(points, x1, y1, false);
Append(points, x2, y2, false);
Append(points, x3, y3, false);
}
}
}
}
}