List<LinkedList<ControlPoint>> CreateContours(Glyph glyphData, out List<Triangle> triangles) { triangles = new List<Triangle>(); var contours = new List<LinkedList<ControlPoint>>(); foreach (List<ControlPoint> contour in glyphData.Contours) { if (contour.Count > 1) { var innerPoints = new LinkedList<ControlPoint>(); contours.Add(innerPoints); Triangle firstTriangle = null; for (int i = 0, a = contour.Count - 1, b = 1; i < contour.Count; a = i, b = (b + 1) % contour.Count, i++) { ControlPoint point = contour[i]; if (point.IsOnCurve) { innerPoints.AddLast(point); } else { var pointBefore = contour[a]; if (!pointBefore.IsOnCurve) { pointBefore = (pointBefore + point) / 2; innerPoints.AddLast(pointBefore); } var pointAfter = contour[b]; if (!pointAfter.IsOnCurve) pointAfter = (pointAfter + point) / 2; var triangle = new Triangle(point, pointBefore, pointAfter); if (i > 0) triangle.BNode = innerPoints.Last; else firstTriangle = triangle; triangles.Add(triangle); } } if (firstTriangle != null && firstTriangle.BNode == null) firstTriangle.BNode = contour.Last().IsOnCurve ? innerPoints.Last : innerPoints.First; } } return contours; }
Glyph[] ParseGlyfTable(Stream stream, BinaryReader reader, TableHeader glyfHeader, TableHeader locaHeader, int glyphCount, bool useLongIndexFormat) { stream.Position = locaHeader.Offset; var glyphOffsets = new long[glyphCount + 1]; Func<long> read = useLongIndexFormat ? new Func<long>(() => (long)reader.ReadUInt32()) : new Func<long>(() => reader.ReadUInt16() * 2L); for (int i = 0; i < glyphCount + 1; i++) glyphOffsets[i] = read(); var glyphs = new Glyph[glyphCount]; for (int i = 0; i < glyphCount; i++) { if (glyphOffsets[i + 1] - glyphOffsets[i] <= 0) { glyphs[i] = Glyph.Empty; } else { stream.Position = glyfHeader.Offset + glyphOffsets[i]; glyphs[i] = new Glyph(stream, reader); } } return glyphs; }
public TriangulatedGlyph(Glyph glyphData, TrueTypeFile.HorizontalMetric horizontalMetric) { indices = new List<int>(); vertices = new List<Vertex>(); advanceWidth = horizontalMetric.AdvanceWidth; leftSideBearing = horizontalMetric.LeftSideBearing; if (glyphData.IsCompound) { } else { if (glyphData.Contours.Length == 0) return; List<Triangle> triangles; List<LinkedList<ControlPoint>> contours = CreateContours(glyphData, out triangles); int count = 0; bool split; do { split = SplitTriangles(ref triangles); count++; } while (split && count < 4); foreach (var triangle in triangles) { ControlPoint p = (triangle.B + triangle.C) / 2; int windingNumber = GetWindingNumber(p, glyphData); triangle.IsInside = windingNumber == 0; if (triangle.IsInside) triangle.Contour.AddAfter(triangle.BNode, triangle.A); } PolyTree tree = ClipperHelper.Combine(contours.Select(contour => contour.ToList()).ToList()); int pointIndex = 0; var inputGeometry = new InputGeometry(); SearchTree(inputGeometry, ref pointIndex, tree); if (inputGeometry.Count > 0) { var mesh = new Mesh(); mesh.Triangulate(inputGeometry); int highestIndex = 0; var indexDict = new Dictionary<int, int>(); foreach (TriangleNet.Data.Triangle triangle in mesh.Triangles) { int[] newIndices; List<Vertex> newVertices = IndexVertices(triangle, indexDict, ref highestIndex, out newIndices); indices.AddRange(newIndices); vertices.AddRange(newVertices); } foreach (var triangle in triangles) { indices.Add(vertices.Count); vertices.Add(new Vertex((float)triangle.B.X, (float)triangle.B.Y, 0, 0, triangle.IsInside)); indices.Add(vertices.Count); vertices.Add(new Vertex((float)triangle.A.X, (float)triangle.A.Y, 0.5f, 0, triangle.IsInside)); indices.Add(vertices.Count); vertices.Add(new Vertex((float)triangle.C.X, (float)triangle.C.Y, 1, 1, triangle.IsInside)); } } } }
int GetWindingNumber(ControlPoint p, Glyph glyphData) { ControlPoint p2 = new ControlPoint(p.X + 1000, p.Y); int result = 0; foreach (var contour in glyphData.Contours) { for (int a = contour.Count - 1, b = 0; b < contour.Count; a = b, b++) { ControlPoint pa = contour[a]; ControlPoint pb = contour[b]; if (pa.Y > p.Y && pb.Y < p.Y) { if (pa.X > p.X && pb.X > p.X) { result++; } else if ((pa.X > p.X && pb.X < p.X) || (pa.X < p.X && pb.X > p.X)) { ControlPoint r = p2 - p; ControlPoint s = pb - pa; double t = ControlPoint.VectorProduct((pa - p), s) / ControlPoint.VectorProduct(r, s); double u = ControlPoint.VectorProduct((p - pa), r) / ControlPoint.VectorProduct(s, r); if (t >= 0 && u >= 0 && u <= 1) result++; } } else if (pa.Y < p.Y && pb.Y > p.Y) { if (pa.X > p.X && pb.X > p.X) { result--; } else if ((pa.X > p.X && pb.X < p.X) || (pa.X < p.X && pb.X > p.X)) { ControlPoint r = p2 - p; ControlPoint s = pb - pa; double t = ControlPoint.VectorProduct((pa - p), s) / ControlPoint.VectorProduct(r, s); double u = ControlPoint.VectorProduct((p - pa), r) / ControlPoint.VectorProduct(s, r); if (t >= 0 && u >= 0 && u <= 1) result--; } } } } return result; }