blob: da3bee44712ecf013e7a627af9ed8d8ad9dd5acf [file] [log] [blame]
ethannicholase9709e82016-01-07 13:34:16 -08001/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Chris Dalton17dc4182020-03-25 16:18:16 -06008#ifndef GrTriangulator_DEFINED
9#define GrTriangulator_DEFINED
ethannicholase9709e82016-01-07 13:34:16 -080010
Chris Dalton57ea1fc2021-01-05 13:37:44 -070011#include "include/core/SkPath.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/core/SkPoint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/private/SkColorData.h"
Chris Dalton57ea1fc2021-01-05 13:37:44 -070014#include "src/core/SkArenaAlloc.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040015#include "src/gpu/GrColor.h"
senorblanco6599eff2016-03-10 08:38:45 -080016
Chris Daltond081dce2020-01-23 12:09:04 -070017class GrEagerVertexAllocator;
senorblanco6599eff2016-03-10 08:38:45 -080018struct SkRect;
ethannicholase9709e82016-01-07 13:34:16 -080019
Chris Dalton57ea1fc2021-01-05 13:37:44 -070020#define TRIANGULATOR_WIREFRAME 0
21
ethannicholase9709e82016-01-07 13:34:16 -080022/**
23 * Provides utility functions for converting paths to a collection of triangles.
24 */
Chris Dalton57ea1fc2021-01-05 13:37:44 -070025class GrTriangulator {
26public:
27 enum class Mode {
28 kNormal,
ethannicholase9709e82016-01-07 13:34:16 -080029
Chris Dalton57ea1fc2021-01-05 13:37:44 -070030 // Surround path edges with coverage ramps for antialiasing.
31 kEdgeAntialias,
ethannicholase9709e82016-01-07 13:34:16 -080032
Chris Dalton57ea1fc2021-01-05 13:37:44 -070033 // Triangulate only each contour's inner polygon. The inner polygons connect the endpoints
34 // of each verb. (i.e., they are the path that would result from collapsing all curves to
35 // single lines.)
36 //
37 // If the inner polygons are not simple (e.g., self intersection, double winding), then the
38 // tessellator aborts and returns 0.
39 kSimpleInnerPolygons
40 };
ethannicholase9709e82016-01-07 13:34:16 -080041
Chris Dalton57ea1fc2021-01-05 13:37:44 -070042 constexpr static size_t GetVertexStride(Mode mode) {
43 return sizeof(SkPoint) + ((Mode::kEdgeAntialias == mode) ? sizeof(float) : 0);
44 }
ethannicholase9709e82016-01-07 13:34:16 -080045
Chris Dalton57ea1fc2021-01-05 13:37:44 -070046 static int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
47 GrEagerVertexAllocator*, Mode, bool *isLinear);
ethannicholase9709e82016-01-07 13:34:16 -080048
Chris Dalton57ea1fc2021-01-05 13:37:44 -070049 struct WindingVertex {
50 SkPoint fPos;
51 int fWinding;
52 };
Chris Dalton6ccc0322020-01-29 11:38:16 -070053
Chris Dalton57ea1fc2021-01-05 13:37:44 -070054 // *DEPRECATED*: Once CCPR is removed this method will go away.
Chris Dalton6ccc0322020-01-29 11:38:16 -070055 //
Chris Dalton57ea1fc2021-01-05 13:37:44 -070056 // Triangulates a path to an array of vertices. Each triangle is represented as a set of three
57 // WindingVertex entries, each of which contains the position and winding count (which is the
58 // same for all three vertices of a triangle). The 'verts' out parameter is set to point to the
59 // resultant vertex array. CALLER IS RESPONSIBLE for deleting this buffer to avoid a memory
60 // leak!
61 static int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
62 WindingVertex** verts);
63
64 // Structs used by GrTriangulator internals.
65 struct Vertex;
66 struct VertexList;
67 struct Edge;
68 struct EdgeList;
69 struct Poly;
70 struct Comparator;
71
72private:
73 GrTriangulator(const SkPath& path, Mode mode) : fPath(path), fMode(mode) {}
74
75 // There are six stages to the basic algorithm:
76 //
77 // 1) Linearize the path contours into piecewise linear segments:
78 void pathToContours(float tolerance, const SkRect& clipBounds, VertexList* contours);
79
80 // 2) Build a mesh of edges connecting the vertices:
81 void contoursToMesh(VertexList* contours, int contourCnt, VertexList* mesh, Comparator&);
82
83 // 3) Sort the vertices in Y (and secondarily in X) (merge_sort()).
84 static void SortMesh(VertexList* vertices, const Comparator&);
85
86 // 4) Simplify the mesh by inserting new vertices at intersecting edges:
87 enum class SimplifyResult {
88 kAlreadySimple,
89 kFoundSelfIntersection,
90 kAbort
91 };
92
93 SimplifyResult simplify(VertexList* mesh, Comparator&);
94
95 // 5) Tessellate the simplified mesh into monotone polygons:
96 Poly* tessellate(const VertexList& vertices);
97
98 // 6) Triangulate the monotone polygons directly into a vertex buffer:
99 void* polysToTriangles(Poly* polys, void* data, SkPathFillType overrideFillType);
100
101 // For screenspace antialiasing, the algorithm is modified as follows:
102 //
103 // Run steps 1-5 above to produce polygons.
104 // 5b) Apply fill rules to extract boundary contours from the polygons (extract_boundaries()).
105 // 5c) Simplify boundaries to remove "pointy" vertices that cause inversions
106 // (simplify_boundary()).
107 // 5d) Displace edges by half a pixel inward and outward along their normals. Intersect to find
108 // new vertices, and set zero alpha on the exterior and one alpha on the interior. Build a
109 // new antialiased mesh from those vertices (stroke_boundary()).
110 // Run steps 3-6 above on the new mesh, and produce antialiased triangles.
111 //
112 // The vertex sorting in step (3) is a merge sort, since it plays well with the linked list
113 // of vertices (and the necessity of inserting new vertices on intersection).
114 //
115 // Stages (4) and (5) use an active edge list -- a list of all edges for which the
116 // sweep line has crossed the top vertex, but not the bottom vertex. It's sorted
117 // left-to-right based on the point where both edges are active (when both top vertices
118 // have been seen, so the "lower" top vertex of the two). If the top vertices are equal
119 // (shared), it's sorted based on the last point where both edges are active, so the
120 // "upper" bottom vertex.
121 //
122 // The most complex step is the simplification (4). It's based on the Bentley-Ottman
123 // line-sweep algorithm, but due to floating point inaccuracy, the intersection points are
124 // not exact and may violate the mesh topology or active edge list ordering. We
125 // accommodate this by adjusting the topology of the mesh and AEL to match the intersection
126 // points. This occurs in two ways:
127 //
128 // A) Intersections may cause a shortened edge to no longer be ordered with respect to its
129 // neighbouring edges at the top or bottom vertex. This is handled by merging the
130 // edges (merge_collinear_edges()).
131 // B) Intersections may cause an edge to violate the left-to-right ordering of the
132 // active edge list. This is handled by detecting potential violations and rewinding
133 // the active edge list to the vertex before they occur (rewind() during merging,
134 // rewind_if_necessary() during splitting).
135 //
136 // The tessellation steps (5) and (6) are based on "Triangulating Simple Polygons and
137 // Equivalent Problems" (Fournier and Montuno); also a line-sweep algorithm. Note that it
138 // currently uses a linked list for the active edge list, rather than a 2-3 tree as the
139 // paper describes. The 2-3 tree gives O(lg N) lookups, but insertion and removal also
140 // become O(lg N). In all the test cases, it was found that the cost of frequent O(lg N)
141 // insertions and removals was greater than the cost of infrequent O(N) lookups with the
142 // linked list implementation. With the latter, all removals are O(1), and most insertions
143 // are O(1), since we know the adjacent edge in the active edge list based on the topology.
144 // Only type 2 vertices (see paper) require the O(N) lookups, and these are much less
145 // frequent. There may be other data structures worth investigating, however.
146 //
147 // Note that the orientation of the line sweep algorithms is determined by the aspect ratio of
148 // the path bounds. When the path is taller than it is wide, we sort vertices based on
149 // increasing Y coordinate, and secondarily by increasing X coordinate. When the path is wider
150 // than it is tall, we sort by increasing X coordinate, but secondarily by *decreasing* Y
151 // coordinate. This is so that the "left" and "right" orientation in the code remains correct
152 // (edges to the left are increasing in Y; edges to the right are decreasing in Y). That is, the
153 // setting rotates 90 degrees counterclockwise, rather that transposing.
154
155 // Additional helpers and driver functions.
156 void appendPointToContour(const SkPoint& p, VertexList* contour);
157 void appendQuadraticToContour(const SkPoint[3], SkScalar toleranceSqd, VertexList* contour);
158 void generateCubicPoints(const SkPoint&, const SkPoint&, const SkPoint&, const SkPoint&,
159 SkScalar tolSqd, VertexList* contour, int pointsLeft);
160 bool splitEdge(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, Comparator&);
161 bool intersectEdgePair(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
162 Comparator&);
163 bool checkForIntersection(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
164 VertexList* mesh, Comparator&);
165 void sanitizeContours(VertexList* contours, int contourCnt);
166 bool mergeCoincidentVertices(VertexList* mesh, Comparator&);
167 void buildEdges(VertexList* contours, int contourCnt, VertexList* mesh, Comparator&);
168 Poly* contoursToPolys(VertexList* contours, int contourCnt, VertexList* outerMesh);
169 Poly* pathToPolys(float tolerance, const SkRect& clipBounds, int contourCnt,
170 VertexList* outerMesh);
171 int pathToTriangles(float tolerance, const SkRect& clipBounds, GrEagerVertexAllocator*,
172 SkPathFillType overrideFillType);
173
174 constexpr static int kArenaChunkSize = 16 * 1024;
175 SkArenaAlloc fAlloc{kArenaChunkSize};
176 const SkPath fPath;
177 const Mode fMode;
178 bool fIsLinear = false;
Chris Daltondcc8c542020-01-28 17:55:56 -0700179};
180
ethannicholase9709e82016-01-07 13:34:16 -0800181#endif