blob: 93805099ea62235206082afc3c7b440182206452 [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 Dalton75887a22021-01-06 00:23:13 -070020#define TRIANGULATOR_LOGGING 0
Chris Dalton57ea1fc2021-01-05 13:37:44 -070021#define TRIANGULATOR_WIREFRAME 0
22
ethannicholase9709e82016-01-07 13:34:16 -080023/**
24 * Provides utility functions for converting paths to a collection of triangles.
25 */
Chris Dalton57ea1fc2021-01-05 13:37:44 -070026class GrTriangulator {
27public:
Chris Dalton854ee852021-01-05 15:12:59 -070028 static int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
29 GrEagerVertexAllocator* vertexAllocator, bool* isLinear) {
30 GrTriangulator triangulator(path);
31 int count = triangulator.pathToTriangles(tolerance, clipBounds, vertexAllocator,
32 path.getFillType());
33 *isLinear = triangulator.fIsLinear;
34 return count;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070035 }
ethannicholase9709e82016-01-07 13:34:16 -080036
Chris Dalton854ee852021-01-05 15:12:59 -070037 static int PathToAATriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
38 GrEagerVertexAllocator* vertexAllocator, bool* isLinear) {
39 GrTriangulator triangulator(path);
40 triangulator.fRoundVerticesToQuarterPixel = true;
41 triangulator.fEmitCoverage = true;
42 int count = triangulator.pathToTriangles(tolerance, clipBounds, vertexAllocator,
43 SkPathFillType::kWinding);
44 *isLinear = triangulator.fIsLinear;
45 return count;
46 }
47
48 static int TriangulateSimpleInnerPolygons(const SkPath& path,
49 GrEagerVertexAllocator* vertexAllocator,
50 bool *isLinear) {
51 GrTriangulator triangulator(path);
52 triangulator.fCullCollinearVertices = false;
53 triangulator.fSimpleInnerPolygons = true;
54 int count = triangulator.pathToTriangles(0, SkRect::MakeEmpty(), vertexAllocator,
55 path.getFillType());
56 *isLinear = triangulator.fIsLinear;
57 return count;
58 }
ethannicholase9709e82016-01-07 13:34:16 -080059
Chris Dalton57ea1fc2021-01-05 13:37:44 -070060 struct WindingVertex {
61 SkPoint fPos;
62 int fWinding;
63 };
Chris Dalton6ccc0322020-01-29 11:38:16 -070064
Chris Dalton57ea1fc2021-01-05 13:37:44 -070065 // *DEPRECATED*: Once CCPR is removed this method will go away.
Chris Dalton6ccc0322020-01-29 11:38:16 -070066 //
Chris Dalton57ea1fc2021-01-05 13:37:44 -070067 // Triangulates a path to an array of vertices. Each triangle is represented as a set of three
68 // WindingVertex entries, each of which contains the position and winding count (which is the
69 // same for all three vertices of a triangle). The 'verts' out parameter is set to point to the
70 // resultant vertex array. CALLER IS RESPONSIBLE for deleting this buffer to avoid a memory
71 // leak!
72 static int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
73 WindingVertex** verts);
74
75 // Structs used by GrTriangulator internals.
Chris Dalton75887a22021-01-06 00:23:13 -070076 enum class Side : bool;
77 enum class EdgeType : int;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070078 struct Vertex;
79 struct VertexList;
Chris Dalton75887a22021-01-06 00:23:13 -070080 struct Line;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070081 struct Edge;
82 struct EdgeList;
83 struct Poly;
Chris Dalton75887a22021-01-06 00:23:13 -070084 struct MonotonePoly;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070085 struct Comparator;
86
87private:
Chris Dalton854ee852021-01-05 15:12:59 -070088 GrTriangulator(const SkPath& path) : fPath(path) {}
Chris Dalton57ea1fc2021-01-05 13:37:44 -070089
90 // There are six stages to the basic algorithm:
91 //
92 // 1) Linearize the path contours into piecewise linear segments:
93 void pathToContours(float tolerance, const SkRect& clipBounds, VertexList* contours);
94
95 // 2) Build a mesh of edges connecting the vertices:
Chris Dalton75887a22021-01-06 00:23:13 -070096 void contoursToMesh(VertexList* contours, int contourCnt, VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -070097
98 // 3) Sort the vertices in Y (and secondarily in X) (merge_sort()).
99 static void SortMesh(VertexList* vertices, const Comparator&);
100
101 // 4) Simplify the mesh by inserting new vertices at intersecting edges:
102 enum class SimplifyResult {
103 kAlreadySimple,
104 kFoundSelfIntersection,
105 kAbort
106 };
107
Chris Dalton75887a22021-01-06 00:23:13 -0700108 SimplifyResult simplify(VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700109
110 // 5) Tessellate the simplified mesh into monotone polygons:
111 Poly* tessellate(const VertexList& vertices);
112
113 // 6) Triangulate the monotone polygons directly into a vertex buffer:
114 void* polysToTriangles(Poly* polys, void* data, SkPathFillType overrideFillType);
115
116 // For screenspace antialiasing, the algorithm is modified as follows:
117 //
118 // Run steps 1-5 above to produce polygons.
119 // 5b) Apply fill rules to extract boundary contours from the polygons (extract_boundaries()).
120 // 5c) Simplify boundaries to remove "pointy" vertices that cause inversions
121 // (simplify_boundary()).
122 // 5d) Displace edges by half a pixel inward and outward along their normals. Intersect to find
123 // new vertices, and set zero alpha on the exterior and one alpha on the interior. Build a
124 // new antialiased mesh from those vertices (stroke_boundary()).
125 // Run steps 3-6 above on the new mesh, and produce antialiased triangles.
126 //
127 // The vertex sorting in step (3) is a merge sort, since it plays well with the linked list
128 // of vertices (and the necessity of inserting new vertices on intersection).
129 //
130 // Stages (4) and (5) use an active edge list -- a list of all edges for which the
131 // sweep line has crossed the top vertex, but not the bottom vertex. It's sorted
132 // left-to-right based on the point where both edges are active (when both top vertices
133 // have been seen, so the "lower" top vertex of the two). If the top vertices are equal
134 // (shared), it's sorted based on the last point where both edges are active, so the
135 // "upper" bottom vertex.
136 //
137 // The most complex step is the simplification (4). It's based on the Bentley-Ottman
138 // line-sweep algorithm, but due to floating point inaccuracy, the intersection points are
139 // not exact and may violate the mesh topology or active edge list ordering. We
140 // accommodate this by adjusting the topology of the mesh and AEL to match the intersection
141 // points. This occurs in two ways:
142 //
143 // A) Intersections may cause a shortened edge to no longer be ordered with respect to its
144 // neighbouring edges at the top or bottom vertex. This is handled by merging the
145 // edges (merge_collinear_edges()).
146 // B) Intersections may cause an edge to violate the left-to-right ordering of the
147 // active edge list. This is handled by detecting potential violations and rewinding
148 // the active edge list to the vertex before they occur (rewind() during merging,
149 // rewind_if_necessary() during splitting).
150 //
151 // The tessellation steps (5) and (6) are based on "Triangulating Simple Polygons and
152 // Equivalent Problems" (Fournier and Montuno); also a line-sweep algorithm. Note that it
153 // currently uses a linked list for the active edge list, rather than a 2-3 tree as the
154 // paper describes. The 2-3 tree gives O(lg N) lookups, but insertion and removal also
155 // become O(lg N). In all the test cases, it was found that the cost of frequent O(lg N)
156 // insertions and removals was greater than the cost of infrequent O(N) lookups with the
157 // linked list implementation. With the latter, all removals are O(1), and most insertions
158 // are O(1), since we know the adjacent edge in the active edge list based on the topology.
159 // Only type 2 vertices (see paper) require the O(N) lookups, and these are much less
160 // frequent. There may be other data structures worth investigating, however.
161 //
162 // Note that the orientation of the line sweep algorithms is determined by the aspect ratio of
163 // the path bounds. When the path is taller than it is wide, we sort vertices based on
164 // increasing Y coordinate, and secondarily by increasing X coordinate. When the path is wider
165 // than it is tall, we sort by increasing X coordinate, but secondarily by *decreasing* Y
166 // coordinate. This is so that the "left" and "right" orientation in the code remains correct
167 // (edges to the left are increasing in Y; edges to the right are decreasing in Y). That is, the
168 // setting rotates 90 degrees counterclockwise, rather that transposing.
169
170 // Additional helpers and driver functions.
Chris Dalton75887a22021-01-06 00:23:13 -0700171 void* emitMonotonePoly(const MonotonePoly* monotonePoly, void* vertexData);
172 void* emitTriangle(const Vertex* prev, const Vertex* curr, const Vertex* next, int winding,
173 void* vertexData) const;
174 void* emitPoly(const Poly* poly, void* vertexData);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700175 void appendPointToContour(const SkPoint& p, VertexList* contour);
176 void appendQuadraticToContour(const SkPoint[3], SkScalar toleranceSqd, VertexList* contour);
177 void generateCubicPoints(const SkPoint&, const SkPoint&, const SkPoint&, const SkPoint&,
178 SkScalar tolSqd, VertexList* contour, int pointsLeft);
Chris Dalton75887a22021-01-06 00:23:13 -0700179 bool splitEdge(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current,
180 const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700181 bool intersectEdgePair(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
Chris Dalton75887a22021-01-06 00:23:13 -0700182 const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700183 bool checkForIntersection(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
Chris Dalton75887a22021-01-06 00:23:13 -0700184 VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700185 void sanitizeContours(VertexList* contours, int contourCnt);
Chris Dalton75887a22021-01-06 00:23:13 -0700186 bool mergeCoincidentVertices(VertexList* mesh, const Comparator&);
187 void buildEdges(VertexList* contours, int contourCnt, VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700188 Poly* contoursToPolys(VertexList* contours, int contourCnt, VertexList* outerMesh);
189 Poly* pathToPolys(float tolerance, const SkRect& clipBounds, int contourCnt,
190 VertexList* outerMesh);
191 int pathToTriangles(float tolerance, const SkRect& clipBounds, GrEagerVertexAllocator*,
192 SkPathFillType overrideFillType);
193
194 constexpr static int kArenaChunkSize = 16 * 1024;
195 SkArenaAlloc fAlloc{kArenaChunkSize};
196 const SkPath fPath;
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700197 bool fIsLinear = false;
Chris Dalton854ee852021-01-05 15:12:59 -0700198
199 // Flags.
200 bool fRoundVerticesToQuarterPixel = false;
201 bool fEmitCoverage = false;
202 bool fCullCollinearVertices = true;
203 bool fSimpleInnerPolygons = false;
Chris Daltondcc8c542020-01-28 17:55:56 -0700204};
205
Chris Dalton75887a22021-01-06 00:23:13 -0700206enum class GrTriangulator::Side : bool {
207 kLeft,
208 kRight
209};
210
211enum class GrTriangulator::EdgeType : int {
212 kInner,
213 kOuter,
214 kConnector
215};
216
217// Vertices are used in three ways: first, the path contours are converted into a
218// circularly-linked list of Vertices for each contour. After edge construction, the same Vertices
219// are re-ordered by the merge sort according to the sweep_lt comparator (usually, increasing
220// in Y) using the same fPrev/fNext pointers that were used for the contours, to avoid
221// reallocation. Finally, MonotonePolys are built containing a circularly-linked list of
222// Vertices. (Currently, those Vertices are newly-allocated for the MonotonePolys, since
223// an individual Vertex from the path mesh may belong to multiple
224// MonotonePolys, so the original Vertices cannot be re-used.
225struct GrTriangulator::Vertex {
226 Vertex(const SkPoint& point, uint8_t alpha)
227 : fPoint(point)
228 , fAlpha(alpha)
229#if TRIANGULATOR_LOGGING
230 , fID (-1.0f)
231#endif
232 {}
233 SkPoint fPoint; // Vertex position
234 Vertex* fPrev{}; // Linked list of contours, then Y-sorted vertices.
235 Vertex* fNext{}; // "
236 Edge* fFirstEdgeAbove{}; // Linked list of edges above this vertex.
237 Edge* fLastEdgeAbove{}; // "
238 Edge* fFirstEdgeBelow{}; // Linked list of edges below this vertex.
239 Edge* fLastEdgeBelow{}; // "
240 Edge* fLeftEnclosingEdge{}; // Nearest edge in the AEL left of this vertex.
241 Edge* fRightEnclosingEdge{}; // Nearest edge in the AEL right of this vertex.
242 Vertex* fPartner{}; // Corresponding inner or outer vertex (for AA).
243 uint8_t fAlpha;
244 bool fSynthetic{}; // Is this a synthetic vertex?
245#if TRIANGULATOR_LOGGING
246 float fID; // Identifier used for logging.
247#endif
248 bool isConnected() const { return fFirstEdgeAbove || fFirstEdgeBelow; }
249 void insertEdgeAbove(Edge*, const Comparator& c);
250 void insertEdgeBelow(Edge*, const Comparator& c);
251};
252
253struct GrTriangulator::VertexList {
254 VertexList() = default;
255 VertexList(Vertex* head, Vertex* tail) : fHead(head), fTail(tail) {}
256 Vertex* fHead{};
257 Vertex* fTail{};
258 void insert(Vertex* v, Vertex* prev, Vertex* next);
259 void append(Vertex* v) { this->insert(v, fTail, nullptr); }
260 void append(const VertexList& list) {
261 if (!list.fHead) {
262 return;
263 }
264 if (fTail) {
265 fTail->fNext = list.fHead;
266 list.fHead->fPrev = fTail;
267 } else {
268 fHead = list.fHead;
269 }
270 fTail = list.fTail;
271 }
272 void prepend(Vertex* v) { this->insert(v, nullptr, fHead); }
273 void remove(Vertex* v);
274 void close() {
275 if (fHead && fTail) {
276 fTail->fNext = fHead;
277 fHead->fPrev = fTail;
278 }
279 }
280#if TRIANGULATOR_LOGGING
281 void dump();
282#endif
283};
284
285// A line equation in implicit form. fA * x + fB * y + fC = 0, for all points (x, y) on the line.
286struct GrTriangulator::Line {
287 Line(double a, double b, double c) : fA(a), fB(b), fC(c) {}
288 Line(Vertex* p, Vertex* q) : Line(p->fPoint, q->fPoint) {}
289 Line(const SkPoint& p, const SkPoint& q)
290 : fA(static_cast<double>(q.fY) - p.fY) // a = dY
291 , fB(static_cast<double>(p.fX) - q.fX) // b = -dX
292 , fC(static_cast<double>(p.fY) * q.fX - // c = cross(q, p)
293 static_cast<double>(p.fX) * q.fY) {
294 }
295 double dist(const SkPoint& p) const { return fA * p.fX + fB * p.fY + fC; }
296 Line operator*(double v) const { return Line(fA * v, fB * v, fC * v); }
297 double magSq() const { return fA * fA + fB * fB; }
298 void normalize();
299 bool nearParallel(const Line& o) const {
300 return fabs(o.fA - fA) < 0.00001 && fabs(o.fB - fB) < 0.00001;
301 }
302 // Compute the intersection of two (infinite) Lines.
303 bool intersect(const Line& other, SkPoint* point) const;
304 double fA, fB, fC;
305};
306
307// An Edge joins a top Vertex to a bottom Vertex. Edge ordering for the list of "edges above" and
308// "edge below" a vertex as well as for the active edge list is handled by isLeftOf()/isRightOf().
309// Note that an Edge will give occasionally dist() != 0 for its own endpoints (because floating
310// point). For speed, that case is only tested by the callers that require it (e.g.,
311// rewind_if_necessary()). Edges also handle checking for intersection with other edges.
312// Currently, this converts the edges to the parametric form, in order to avoid doing a division
313// until an intersection has been confirmed. This is slightly slower in the "found" case, but
314// a lot faster in the "not found" case.
315//
316// The coefficients of the line equation stored in double precision to avoid catastrophic
317// cancellation in the isLeftOf() and isRightOf() checks. Using doubles ensures that the result is
318// correct in float, since it's a polynomial of degree 2. The intersect() function, being
319// degree 5, is still subject to catastrophic cancellation. We deal with that by assuming its
320// output may be incorrect, and adjusting the mesh topology to match (see comment at the top of
321// this file).
322struct GrTriangulator::Edge {
323 Edge(Vertex* top, Vertex* bottom, int winding, EdgeType type)
324 : fWinding(winding), fTop(top), fBottom(bottom), fType(type), fLine(top, bottom) {}
325 int fWinding; // 1 == edge goes downward; -1 = edge goes upward.
326 Vertex* fTop; // The top vertex in vertex-sort-order (sweep_lt).
327 Vertex* fBottom; // The bottom vertex in vertex-sort-order.
328 EdgeType fType;
329 Edge* fLeft{}; // The linked list of edges in the active edge list.
330 Edge* fRight{}; // "
331 Edge* fPrevEdgeAbove{}; // The linked list of edges in the bottom Vertex's "edges above".
332 Edge* fNextEdgeAbove{}; // "
333 Edge* fPrevEdgeBelow{}; // The linked list of edges in the top Vertex's "edges below".
334 Edge* fNextEdgeBelow{}; // "
335 Poly* fLeftPoly{}; // The Poly to the left of this edge, if any.
336 Poly* fRightPoly{}; // The Poly to the right of this edge, if any.
337 Edge* fLeftPolyPrev{};
338 Edge* fLeftPolyNext{};
339 Edge* fRightPolyPrev{};
340 Edge* fRightPolyNext{};
341 bool fUsedInLeftPoly{};
342 bool fUsedInRightPoly{};
343 Line fLine;
344 void disconnect();
345 double dist(const SkPoint& p) const { return fLine.dist(p); }
346 bool isRightOf(Vertex* v) const { return fLine.dist(v->fPoint) < 0.0; }
347 bool isLeftOf(Vertex* v) const { return fLine.dist(v->fPoint) > 0.0; }
348 void recompute() { fLine = Line(fTop, fBottom); }
349 bool intersect(const Edge& other, SkPoint* p, uint8_t* alpha = nullptr) const;
350};
351
352struct GrTriangulator::EdgeList {
353 Edge* fHead{};
354 Edge* fTail{};
355 void insert(Edge* edge, Edge* prev);
356 void insert(Edge* edge, Edge* prev, Edge* next);
357 void append(Edge* e) {
358 this->insert(e, fTail, nullptr);
359 }
360 void remove(Edge* edge);
361 void removeAll() {
362 while (fHead) {
363 this->remove(fHead);
364 }
365 }
366 void close() {
367 if (fHead && fTail) {
368 fTail->fRight = fHead;
369 fHead->fLeft = fTail;
370 }
371 }
372 bool contains(Edge* edge) const {
373 return edge->fLeft || edge->fRight || fHead == edge;
374 }
375};
376
377struct GrTriangulator::Poly {
378 Poly(Vertex* v, int winding) : fFirstVertex(v), fWinding(winding) {
379#if TRIANGULATOR_LOGGING
380 static int gID = 0;
381 fID = gID++;
382 TESS_LOG("*** created Poly %d\n", fID);
383#endif
384 }
385 Poly* addEdge(Edge*, Side, SkArenaAlloc&);
386 Vertex* lastVertex() const;
387 Vertex* fFirstVertex;
388 int fWinding;
389 MonotonePoly* fHead{};
390 MonotonePoly* fTail{};
391 Poly* fNext{};
392 Poly* fPartner{};
393 int fCount{};
394#if TRIANGULATOR_LOGGING
395 int fID;
396#endif
397};
398
399struct GrTriangulator::MonotonePoly {
400 MonotonePoly(Edge* edge, Side side, int winding) : fSide(side), fWinding(winding) {
401 this->addEdge(edge);
402 }
403 Side fSide;
404 Edge* fFirstEdge{};
405 Edge* fLastEdge{};
406 MonotonePoly* fPrev{};
407 MonotonePoly* fNext{};
408 int fWinding;
409 void addEdge(Edge* edge);
410 void* emitTriangle(Vertex* prev, Vertex* curr, Vertex* next, bool emitCoverage,
411 void* data) const;
412};
413
414struct GrTriangulator::Comparator {
415 enum class Direction { kVertical, kHorizontal };
416 Comparator(Direction direction) : fDirection(direction) {}
417 bool sweep_lt(const SkPoint& a, const SkPoint& b) const;
418 Direction fDirection;
419};
420
ethannicholase9709e82016-01-07 13:34:16 -0800421#endif