blob: f0a7a3e762c161f180af447d7c728e1a24f3be36 [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 Dalton5045de32021-01-07 19:09:01 -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);
Chris Dalton93c2d812021-01-11 19:51:59 -070031 int count = triangulator.pathToTriangles(tolerance, clipBounds, vertexAllocator);
Chris Dalton854ee852021-01-05 15:12:59 -070032 *isLinear = triangulator.fIsLinear;
33 return count;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070034 }
ethannicholase9709e82016-01-07 13:34:16 -080035
Chris Dalton854ee852021-01-05 15:12:59 -070036 static int TriangulateSimpleInnerPolygons(const SkPath& path,
37 GrEagerVertexAllocator* vertexAllocator,
38 bool *isLinear) {
39 GrTriangulator triangulator(path);
40 triangulator.fCullCollinearVertices = false;
41 triangulator.fSimpleInnerPolygons = true;
Chris Dalton93c2d812021-01-11 19:51:59 -070042 int count = triangulator.pathToTriangles(0, SkRect::MakeEmpty(), vertexAllocator);
Chris Dalton854ee852021-01-05 15:12:59 -070043 *isLinear = triangulator.fIsLinear;
44 return count;
45 }
ethannicholase9709e82016-01-07 13:34:16 -080046
Chris Dalton57ea1fc2021-01-05 13:37:44 -070047 struct WindingVertex {
48 SkPoint fPos;
49 int fWinding;
50 };
Chris Dalton6ccc0322020-01-29 11:38:16 -070051
Chris Dalton57ea1fc2021-01-05 13:37:44 -070052 // *DEPRECATED*: Once CCPR is removed this method will go away.
Chris Dalton6ccc0322020-01-29 11:38:16 -070053 //
Chris Dalton57ea1fc2021-01-05 13:37:44 -070054 // Triangulates a path to an array of vertices. Each triangle is represented as a set of three
55 // WindingVertex entries, each of which contains the position and winding count (which is the
56 // same for all three vertices of a triangle). The 'verts' out parameter is set to point to the
57 // resultant vertex array. CALLER IS RESPONSIBLE for deleting this buffer to avoid a memory
58 // leak!
59 static int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
60 WindingVertex** verts);
61
Chris Dalton17ce8c52021-01-07 18:08:46 -070062 // Enums used by GrTriangulator internals.
63 typedef enum { kLeft_Side, kRight_Side } Side;
64 enum class EdgeType { kInner, kOuter, kConnector };
65
Chris Dalton57ea1fc2021-01-05 13:37:44 -070066 // Structs used by GrTriangulator internals.
67 struct Vertex;
68 struct VertexList;
Chris Dalton17ce8c52021-01-07 18:08:46 -070069 struct Line;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070070 struct Edge;
71 struct EdgeList;
Chris Dalton17ce8c52021-01-07 18:08:46 -070072 struct MonotonePoly;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070073 struct Poly;
74 struct Comparator;
75
Chris Dalton7cf3add2021-01-11 18:33:28 -070076protected:
Chris Dalton854ee852021-01-05 15:12:59 -070077 GrTriangulator(const SkPath& path) : fPath(path) {}
Chris Dalton7cf3add2021-01-11 18:33:28 -070078 virtual ~GrTriangulator() {}
Chris Dalton57ea1fc2021-01-05 13:37:44 -070079
80 // There are six stages to the basic algorithm:
81 //
82 // 1) Linearize the path contours into piecewise linear segments:
83 void pathToContours(float tolerance, const SkRect& clipBounds, VertexList* contours);
84
85 // 2) Build a mesh of edges connecting the vertices:
Chris Dalton811dc6a2021-01-07 16:40:32 -070086 void contoursToMesh(VertexList* contours, int contourCnt, VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -070087
88 // 3) Sort the vertices in Y (and secondarily in X) (merge_sort()).
89 static void SortMesh(VertexList* vertices, const Comparator&);
90
91 // 4) Simplify the mesh by inserting new vertices at intersecting edges:
92 enum class SimplifyResult {
93 kAlreadySimple,
94 kFoundSelfIntersection,
95 kAbort
96 };
97
Chris Dalton811dc6a2021-01-07 16:40:32 -070098 SimplifyResult simplify(VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -070099
100 // 5) Tessellate the simplified mesh into monotone polygons:
Chris Dalton93c2d812021-01-11 19:51:59 -0700101 virtual Poly* tessellate(const VertexList& vertices, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700102
103 // 6) Triangulate the monotone polygons directly into a vertex buffer:
Chris Dalton93c2d812021-01-11 19:51:59 -0700104 virtual int64_t countPoints(Poly* polys) const {
105 return this->countPointsImpl(polys, fPath.getFillType());
106 }
107 virtual void* polysToTriangles(Poly* polys, void* data) {
108 return this->polysToTrianglesImpl(polys, data, fPath.getFillType());
109 }
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700110
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700111 // The vertex sorting in step (3) is a merge sort, since it plays well with the linked list
112 // of vertices (and the necessity of inserting new vertices on intersection).
113 //
114 // Stages (4) and (5) use an active edge list -- a list of all edges for which the
115 // sweep line has crossed the top vertex, but not the bottom vertex. It's sorted
116 // left-to-right based on the point where both edges are active (when both top vertices
117 // have been seen, so the "lower" top vertex of the two). If the top vertices are equal
118 // (shared), it's sorted based on the last point where both edges are active, so the
119 // "upper" bottom vertex.
120 //
121 // The most complex step is the simplification (4). It's based on the Bentley-Ottman
122 // line-sweep algorithm, but due to floating point inaccuracy, the intersection points are
123 // not exact and may violate the mesh topology or active edge list ordering. We
124 // accommodate this by adjusting the topology of the mesh and AEL to match the intersection
125 // points. This occurs in two ways:
126 //
127 // A) Intersections may cause a shortened edge to no longer be ordered with respect to its
128 // neighbouring edges at the top or bottom vertex. This is handled by merging the
129 // edges (merge_collinear_edges()).
130 // B) Intersections may cause an edge to violate the left-to-right ordering of the
131 // active edge list. This is handled by detecting potential violations and rewinding
132 // the active edge list to the vertex before they occur (rewind() during merging,
133 // rewind_if_necessary() during splitting).
134 //
135 // The tessellation steps (5) and (6) are based on "Triangulating Simple Polygons and
136 // Equivalent Problems" (Fournier and Montuno); also a line-sweep algorithm. Note that it
137 // currently uses a linked list for the active edge list, rather than a 2-3 tree as the
138 // paper describes. The 2-3 tree gives O(lg N) lookups, but insertion and removal also
139 // become O(lg N). In all the test cases, it was found that the cost of frequent O(lg N)
140 // insertions and removals was greater than the cost of infrequent O(N) lookups with the
141 // linked list implementation. With the latter, all removals are O(1), and most insertions
142 // are O(1), since we know the adjacent edge in the active edge list based on the topology.
143 // Only type 2 vertices (see paper) require the O(N) lookups, and these are much less
144 // frequent. There may be other data structures worth investigating, however.
145 //
146 // Note that the orientation of the line sweep algorithms is determined by the aspect ratio of
147 // the path bounds. When the path is taller than it is wide, we sort vertices based on
148 // increasing Y coordinate, and secondarily by increasing X coordinate. When the path is wider
149 // than it is tall, we sort by increasing X coordinate, but secondarily by *decreasing* Y
150 // coordinate. This is so that the "left" and "right" orientation in the code remains correct
151 // (edges to the left are increasing in Y; edges to the right are decreasing in Y). That is, the
152 // setting rotates 90 degrees counterclockwise, rather that transposing.
153
154 // Additional helpers and driver functions.
Chris Dalton9a4904f2021-01-07 19:10:14 -0700155 void* emitMonotonePoly(const MonotonePoly*, void* data);
156 void* emitTriangle(Vertex* prev, Vertex* curr, Vertex* next, int winding, void* data) const;
157 void* emitPoly(const Poly*, void *data);
Chris Dalton7cf3add2021-01-11 18:33:28 -0700158 Poly* makePoly(Poly** head, Vertex* v, int winding);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700159 void appendPointToContour(const SkPoint& p, VertexList* contour);
160 void appendQuadraticToContour(const SkPoint[3], SkScalar toleranceSqd, VertexList* contour);
161 void generateCubicPoints(const SkPoint&, const SkPoint&, const SkPoint&, const SkPoint&,
162 SkScalar tolSqd, VertexList* contour, int pointsLeft);
Chris Dalton7cf3add2021-01-11 18:33:28 -0700163 Edge* makeEdge(Vertex* prev, Vertex* next, EdgeType type, const Comparator&);
164 Edge* makeConnectingEdge(Vertex* prev, Vertex* next, EdgeType, const Comparator&,
165 int windingScale = 1);
Chris Dalton811dc6a2021-01-07 16:40:32 -0700166 bool splitEdge(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current,
167 const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700168 bool intersectEdgePair(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
Chris Dalton811dc6a2021-01-07 16:40:32 -0700169 const Comparator&);
Chris Dalton7cf3add2021-01-11 18:33:28 -0700170 Vertex* makeSortedVertex(const SkPoint&, uint8_t alpha, VertexList* mesh, Vertex* reference,
171 const Comparator&);
172 void computeBisector(Edge* edge1, Edge* edge2, Vertex*);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700173 bool checkForIntersection(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
Chris Dalton811dc6a2021-01-07 16:40:32 -0700174 VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700175 void sanitizeContours(VertexList* contours, int contourCnt);
Chris Dalton811dc6a2021-01-07 16:40:32 -0700176 bool mergeCoincidentVertices(VertexList* mesh, const Comparator&);
177 void buildEdges(VertexList* contours, int contourCnt, VertexList* mesh, const Comparator&);
Chris Dalton93c2d812021-01-11 19:51:59 -0700178 Poly* contoursToPolys(VertexList* contours, int contourCnt);
179 Poly* pathToPolys(float tolerance, const SkRect& clipBounds, int contourCnt);
180 int64_t countPointsImpl(Poly* polys, SkPathFillType overrideFillType) const;
181 void* polysToTrianglesImpl(Poly* polys, void* data, SkPathFillType overrideFillType);
182 int pathToTriangles(float tolerance, const SkRect& clipBounds, GrEagerVertexAllocator*);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700183
184 constexpr static int kArenaChunkSize = 16 * 1024;
185 SkArenaAlloc fAlloc{kArenaChunkSize};
186 const SkPath fPath;
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700187 bool fIsLinear = false;
Chris Dalton854ee852021-01-05 15:12:59 -0700188
189 // Flags.
190 bool fRoundVerticesToQuarterPixel = false;
191 bool fEmitCoverage = false;
192 bool fCullCollinearVertices = true;
193 bool fSimpleInnerPolygons = false;
Chris Daltondcc8c542020-01-28 17:55:56 -0700194};
195
Chris Dalton5045de32021-01-07 19:09:01 -0700196/**
197 * Vertices are used in three ways: first, the path contours are converted into a
198 * circularly-linked list of Vertices for each contour. After edge construction, the same Vertices
199 * are re-ordered by the merge sort according to the sweep_lt comparator (usually, increasing
200 * in Y) using the same fPrev/fNext pointers that were used for the contours, to avoid
201 * reallocation. Finally, MonotonePolys are built containing a circularly-linked list of
202 * Vertices. (Currently, those Vertices are newly-allocated for the MonotonePolys, since
203 * an individual Vertex from the path mesh may belong to multiple
204 * MonotonePolys, so the original Vertices cannot be re-used.
205 */
206
207struct GrTriangulator::Vertex {
208 Vertex(const SkPoint& point, uint8_t alpha)
209 : fPoint(point), fPrev(nullptr), fNext(nullptr)
210 , fFirstEdgeAbove(nullptr), fLastEdgeAbove(nullptr)
211 , fFirstEdgeBelow(nullptr), fLastEdgeBelow(nullptr)
212 , fLeftEnclosingEdge(nullptr), fRightEnclosingEdge(nullptr)
213 , fPartner(nullptr)
214 , fAlpha(alpha)
215 , fSynthetic(false)
216#if TRIANGULATOR_LOGGING
217 , fID (-1.0f)
218#endif
219 {}
220 SkPoint fPoint; // Vertex position
221 Vertex* fPrev; // Linked list of contours, then Y-sorted vertices.
222 Vertex* fNext; // "
223 Edge* fFirstEdgeAbove; // Linked list of edges above this vertex.
224 Edge* fLastEdgeAbove; // "
225 Edge* fFirstEdgeBelow; // Linked list of edges below this vertex.
226 Edge* fLastEdgeBelow; // "
227 Edge* fLeftEnclosingEdge; // Nearest edge in the AEL left of this vertex.
228 Edge* fRightEnclosingEdge; // Nearest edge in the AEL right of this vertex.
229 Vertex* fPartner; // Corresponding inner or outer vertex (for AA).
230 uint8_t fAlpha;
231 bool fSynthetic; // Is this a synthetic vertex?
232#if TRIANGULATOR_LOGGING
233 float fID; // Identifier used for logging.
234#endif
Chris Dalton24472af2021-01-11 20:05:00 -0700235 bool isConnected() const { return this->fFirstEdgeAbove || this->fFirstEdgeBelow; }
Chris Dalton5045de32021-01-07 19:09:01 -0700236};
237
238struct GrTriangulator::VertexList {
239 VertexList() : fHead(nullptr), fTail(nullptr) {}
240 VertexList(Vertex* head, Vertex* tail) : fHead(head), fTail(tail) {}
241 Vertex* fHead;
242 Vertex* fTail;
243 void insert(Vertex* v, Vertex* prev, Vertex* next);
244 void append(Vertex* v) { insert(v, fTail, nullptr); }
245 void append(const VertexList& list) {
246 if (!list.fHead) {
247 return;
248 }
249 if (fTail) {
250 fTail->fNext = list.fHead;
251 list.fHead->fPrev = fTail;
252 } else {
253 fHead = list.fHead;
254 }
255 fTail = list.fTail;
256 }
257 void prepend(Vertex* v) { insert(v, nullptr, fHead); }
258 void remove(Vertex* v);
259 void close() {
260 if (fHead && fTail) {
261 fTail->fNext = fHead;
262 fHead->fPrev = fTail;
263 }
264 }
Chris Dalton24472af2021-01-11 20:05:00 -0700265#if TRIANGULATOR_LOGGING
266 void dump();
267#endif
Chris Dalton5045de32021-01-07 19:09:01 -0700268};
269
270// A line equation in implicit form. fA * x + fB * y + fC = 0, for all points (x, y) on the line.
271struct GrTriangulator::Line {
272 Line(double a, double b, double c) : fA(a), fB(b), fC(c) {}
273 Line(Vertex* p, Vertex* q) : Line(p->fPoint, q->fPoint) {}
274 Line(const SkPoint& p, const SkPoint& q)
275 : fA(static_cast<double>(q.fY) - p.fY) // a = dY
276 , fB(static_cast<double>(p.fX) - q.fX) // b = -dX
277 , fC(static_cast<double>(p.fY) * q.fX - // c = cross(q, p)
278 static_cast<double>(p.fX) * q.fY) {}
279 double dist(const SkPoint& p) const { return fA * p.fX + fB * p.fY + fC; }
280 Line operator*(double v) const { return Line(fA * v, fB * v, fC * v); }
281 double magSq() const { return fA * fA + fB * fB; }
282 void normalize() {
283 double len = sqrt(this->magSq());
284 if (len == 0.0) {
285 return;
286 }
287 double scale = 1.0f / len;
288 fA *= scale;
289 fB *= scale;
290 fC *= scale;
291 }
292 bool nearParallel(const Line& o) const {
293 return fabs(o.fA - fA) < 0.00001 && fabs(o.fB - fB) < 0.00001;
294 }
295
296 // Compute the intersection of two (infinite) Lines.
297 bool intersect(const Line& other, SkPoint* point) const;
298 double fA, fB, fC;
299};
300
301/**
302 * An Edge joins a top Vertex to a bottom Vertex. Edge ordering for the list of "edges above" and
303 * "edge below" a vertex as well as for the active edge list is handled by isLeftOf()/isRightOf().
304 * Note that an Edge will give occasionally dist() != 0 for its own endpoints (because floating
305 * point). For speed, that case is only tested by the callers that require it (e.g.,
306 * rewind_if_necessary()). Edges also handle checking for intersection with other edges.
307 * Currently, this converts the edges to the parametric form, in order to avoid doing a division
308 * until an intersection has been confirmed. This is slightly slower in the "found" case, but
309 * a lot faster in the "not found" case.
310 *
311 * The coefficients of the line equation stored in double precision to avoid catastrophic
312 * cancellation in the isLeftOf() and isRightOf() checks. Using doubles ensures that the result is
313 * correct in float, since it's a polynomial of degree 2. The intersect() function, being
314 * degree 5, is still subject to catastrophic cancellation. We deal with that by assuming its
315 * output may be incorrect, and adjusting the mesh topology to match (see comment at the top of
316 * this file).
317 */
318
319struct GrTriangulator::Edge {
320 Edge(Vertex* top, Vertex* bottom, int winding, EdgeType type)
321 : fWinding(winding)
322 , fTop(top)
323 , fBottom(bottom)
324 , fType(type)
325 , fLeft(nullptr)
326 , fRight(nullptr)
327 , fPrevEdgeAbove(nullptr)
328 , fNextEdgeAbove(nullptr)
329 , fPrevEdgeBelow(nullptr)
330 , fNextEdgeBelow(nullptr)
331 , fLeftPoly(nullptr)
332 , fRightPoly(nullptr)
333 , fLeftPolyPrev(nullptr)
334 , fLeftPolyNext(nullptr)
335 , fRightPolyPrev(nullptr)
336 , fRightPolyNext(nullptr)
337 , fUsedInLeftPoly(false)
338 , fUsedInRightPoly(false)
339 , fLine(top, bottom) {
340 }
341 int fWinding; // 1 == edge goes downward; -1 = edge goes upward.
342 Vertex* fTop; // The top vertex in vertex-sort-order (sweep_lt).
343 Vertex* fBottom; // The bottom vertex in vertex-sort-order.
344 EdgeType fType;
345 Edge* fLeft; // The linked list of edges in the active edge list.
346 Edge* fRight; // "
347 Edge* fPrevEdgeAbove; // The linked list of edges in the bottom Vertex's "edges above".
348 Edge* fNextEdgeAbove; // "
349 Edge* fPrevEdgeBelow; // The linked list of edges in the top Vertex's "edges below".
350 Edge* fNextEdgeBelow; // "
351 Poly* fLeftPoly; // The Poly to the left of this edge, if any.
352 Poly* fRightPoly; // The Poly to the right of this edge, if any.
353 Edge* fLeftPolyPrev;
354 Edge* fLeftPolyNext;
355 Edge* fRightPolyPrev;
356 Edge* fRightPolyNext;
357 bool fUsedInLeftPoly;
358 bool fUsedInRightPoly;
359 Line fLine;
360 double dist(const SkPoint& p) const { return fLine.dist(p); }
361 bool isRightOf(Vertex* v) const { return fLine.dist(v->fPoint) < 0.0; }
362 bool isLeftOf(Vertex* v) const { return fLine.dist(v->fPoint) > 0.0; }
363 void recompute() { fLine = Line(fTop, fBottom); }
Chris Dalton24472af2021-01-11 20:05:00 -0700364 void insertAbove(Vertex*, const Comparator&);
365 void insertBelow(Vertex*, const Comparator&);
366 void disconnect();
Chris Dalton5045de32021-01-07 19:09:01 -0700367 bool intersect(const Edge& other, SkPoint* p, uint8_t* alpha = nullptr) const;
368};
369
370struct GrTriangulator::EdgeList {
371 EdgeList() : fHead(nullptr), fTail(nullptr) {}
372 Edge* fHead;
373 Edge* fTail;
374 void insert(Edge* edge, Edge* prev, Edge* next);
Chris Dalton24472af2021-01-11 20:05:00 -0700375 void insert(Edge* edge, Edge* prev);
Chris Dalton5045de32021-01-07 19:09:01 -0700376 void append(Edge* e) { insert(e, fTail, nullptr); }
377 void remove(Edge* edge);
378 void removeAll() {
379 while (fHead) {
380 this->remove(fHead);
381 }
382 }
383 void close() {
384 if (fHead && fTail) {
385 fTail->fRight = fHead;
386 fHead->fLeft = fTail;
387 }
388 }
389 bool contains(Edge* edge) const { return edge->fLeft || edge->fRight || fHead == edge; }
390};
391
392struct GrTriangulator::MonotonePoly {
393 MonotonePoly(Edge* edge, Side side, int winding)
394 : fSide(side)
395 , fFirstEdge(nullptr)
396 , fLastEdge(nullptr)
397 , fPrev(nullptr)
398 , fNext(nullptr)
399 , fWinding(winding) {
400 this->addEdge(edge);
401 }
402 Side fSide;
403 Edge* fFirstEdge;
404 Edge* fLastEdge;
405 MonotonePoly* fPrev;
406 MonotonePoly* fNext;
407 int fWinding;
408 void addEdge(Edge*);
409 void* emit(bool emitCoverage, void* data);
410 void* emitTriangle(Vertex* prev, Vertex* curr, Vertex* next, bool emitCoverage,
411 void* data) const;
412};
413
414struct GrTriangulator::Poly {
415 Poly(Vertex* v, int winding)
416 : fFirstVertex(v)
417 , fWinding(winding)
418 , fHead(nullptr)
419 , fTail(nullptr)
420 , fNext(nullptr)
421 , fPartner(nullptr)
422 , fCount(0)
423 {
424#if TRIANGULATOR_LOGGING
425 static int gID = 0;
426 fID = gID++;
427 TESS_LOG("*** created Poly %d\n", fID);
428#endif
429 }
430 Poly* addEdge(Edge* e, Side side, SkArenaAlloc& alloc);
431 void* emit(bool emitCoverage, void *data);
432 Vertex* lastVertex() const { return fTail ? fTail->fLastEdge->fBottom : fFirstVertex; }
433 Vertex* fFirstVertex;
434 int fWinding;
435 MonotonePoly* fHead;
436 MonotonePoly* fTail;
437 Poly* fNext;
438 Poly* fPartner;
439 int fCount;
440#if TRIANGULATOR_LOGGING
441 int fID;
442#endif
443};
444
445struct GrTriangulator::Comparator {
446 enum class Direction { kVertical, kHorizontal };
447 Comparator(Direction direction) : fDirection(direction) {}
448 bool sweep_lt(const SkPoint& a, const SkPoint& b) const;
449 Direction fDirection;
450};
451
Chris Dalton7cf3add2021-01-11 18:33:28 -0700452// Triangulates the given path in device space with a mesh of alpha ramps for antialiasing.
453class GrAATriangulator : public GrTriangulator {
454public:
455 static int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
456 GrEagerVertexAllocator* vertexAllocator) {
457 GrAATriangulator aaTriangulator(path);
458 aaTriangulator.fRoundVerticesToQuarterPixel = true;
459 aaTriangulator.fEmitCoverage = true;
Chris Dalton93c2d812021-01-11 19:51:59 -0700460 return aaTriangulator.pathToTriangles(tolerance, clipBounds, vertexAllocator);
Chris Dalton7cf3add2021-01-11 18:33:28 -0700461 }
462
463 // Structs used by GrAATriangulator internals.
464 struct SSEdge;
465 struct EventList;
466 struct Event {
467 Event(SSEdge* edge, const SkPoint& point, uint8_t alpha)
468 : fEdge(edge), fPoint(point), fAlpha(alpha) {}
469 SSEdge* fEdge;
470 SkPoint fPoint;
471 uint8_t fAlpha;
472 void apply(VertexList* mesh, const Comparator&, EventList* events, GrAATriangulator*);
473 };
474 struct EventComparator {
475 enum class Op { kLessThan, kGreaterThan };
476 EventComparator(Op op) : fOp(op) {}
477 bool operator() (Event* const &e1, Event* const &e2) {
478 return fOp == Op::kLessThan ? e1->fAlpha < e2->fAlpha
479 : e1->fAlpha > e2->fAlpha;
480 }
481 Op fOp;
482 };
483
484private:
485 GrAATriangulator(const SkPath& path) : GrTriangulator(path) {}
486
487 // For screenspace antialiasing, the algorithm is modified as follows:
488 //
489 // Run steps 1-5 above to produce polygons.
490 // 5b) Apply fill rules to extract boundary contours from the polygons:
491 void extractBoundary(EdgeList* boundary, Edge* e);
Chris Dalton93c2d812021-01-11 19:51:59 -0700492 void extractBoundaries(const VertexList& inMesh, VertexList* innerVertices, const Comparator&);
Chris Dalton7cf3add2021-01-11 18:33:28 -0700493
494 // 5c) Simplify boundaries to remove "pointy" vertices that cause inversions:
495 void simplifyBoundary(EdgeList* boundary, const Comparator&);
496
497 // 5d) Displace edges by half a pixel inward and outward along their normals. Intersect to find
498 // new vertices, and set zero alpha on the exterior and one alpha on the interior. Build a
499 // new antialiased mesh from those vertices:
Chris Dalton93c2d812021-01-11 19:51:59 -0700500 void strokeBoundary(EdgeList* boundary, VertexList* innerMesh, const Comparator&);
Chris Dalton7cf3add2021-01-11 18:33:28 -0700501
502 // Run steps 3-6 above on the new mesh, and produce antialiased triangles.
Chris Dalton93c2d812021-01-11 19:51:59 -0700503 Poly* tessellate(const VertexList& mesh, const Comparator&) override;
504 int64_t countPoints(Poly* polys) const override;
505 void* polysToTriangles(Poly* polys, void* data) override;
Chris Dalton7cf3add2021-01-11 18:33:28 -0700506
507 // Additional helpers and driver functions.
508 void makeEvent(SSEdge*, EventList* events);
509 void makeEvent(SSEdge*, Vertex* v, SSEdge* other, Vertex* dest, EventList* events,
510 const Comparator&);
511 void connectPartners(VertexList* mesh, const Comparator&);
512 void removeNonBoundaryEdges(const VertexList& mesh);
513 void connectSSEdge(Vertex* v, Vertex* dest, const Comparator&);
514 bool collapseOverlapRegions(VertexList* mesh, const Comparator&, EventComparator comp);
Chris Dalton93c2d812021-01-11 19:51:59 -0700515
516 VertexList fOuterMesh;
Chris Dalton7cf3add2021-01-11 18:33:28 -0700517};
518
ethannicholase9709e82016-01-07 13:34:16 -0800519#endif