blob: b90bcc0f1e33f7f9a492ed548363fa76077a5d1c [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 Dalton8a42b092021-01-21 22:09:26 -070028 constexpr static int kArenaDefaultChunkSize = 16 * 1024;
29
Chris Dalton854ee852021-01-05 15:12:59 -070030 static int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
31 GrEagerVertexAllocator* vertexAllocator, bool* isLinear) {
Chris Dalton8a42b092021-01-21 22:09:26 -070032 SkArenaAlloc alloc(kArenaDefaultChunkSize);
33 GrTriangulator triangulator(path, &alloc);
Chris Dalton0879da02021-01-29 13:57:25 -070034 Poly* polys = triangulator.pathToPolys(tolerance, clipBounds, isLinear);
35 int count = triangulator.polysToTriangles(polys, vertexAllocator);
Chris Dalton854ee852021-01-05 15:12:59 -070036 return count;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070037 }
ethannicholase9709e82016-01-07 13:34:16 -080038
Chris Dalton57ea1fc2021-01-05 13:37:44 -070039 struct WindingVertex {
40 SkPoint fPos;
41 int fWinding;
42 };
Chris Dalton6ccc0322020-01-29 11:38:16 -070043
Chris Dalton57ea1fc2021-01-05 13:37:44 -070044 // *DEPRECATED*: Once CCPR is removed this method will go away.
Chris Dalton6ccc0322020-01-29 11:38:16 -070045 //
Chris Dalton57ea1fc2021-01-05 13:37:44 -070046 // Triangulates a path to an array of vertices. Each triangle is represented as a set of three
47 // WindingVertex entries, each of which contains the position and winding count (which is the
48 // same for all three vertices of a triangle). The 'verts' out parameter is set to point to the
49 // resultant vertex array. CALLER IS RESPONSIBLE for deleting this buffer to avoid a memory
50 // leak!
51 static int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
52 WindingVertex** verts);
53
Chris Dalton17ce8c52021-01-07 18:08:46 -070054 // Enums used by GrTriangulator internals.
55 typedef enum { kLeft_Side, kRight_Side } Side;
56 enum class EdgeType { kInner, kOuter, kConnector };
57
Chris Dalton57ea1fc2021-01-05 13:37:44 -070058 // Structs used by GrTriangulator internals.
59 struct Vertex;
60 struct VertexList;
Chris Dalton17ce8c52021-01-07 18:08:46 -070061 struct Line;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070062 struct Edge;
63 struct EdgeList;
Chris Dalton17ce8c52021-01-07 18:08:46 -070064 struct MonotonePoly;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070065 struct Poly;
66 struct Comparator;
67
Chris Dalton7cf3add2021-01-11 18:33:28 -070068protected:
Chris Dalton8a42b092021-01-21 22:09:26 -070069 GrTriangulator(const SkPath& path, SkArenaAlloc* alloc) : fPath(path), fAlloc(alloc) {}
Chris Dalton7cf3add2021-01-11 18:33:28 -070070 virtual ~GrTriangulator() {}
Chris Dalton57ea1fc2021-01-05 13:37:44 -070071
72 // There are six stages to the basic algorithm:
73 //
74 // 1) Linearize the path contours into piecewise linear segments:
Chris Dalton37d16f12021-01-20 18:32:48 -070075 void pathToContours(float tolerance, const SkRect& clipBounds, VertexList* contours,
Chris Dalton330cfa42021-01-27 18:24:48 -070076 bool* isLinear) const;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070077
78 // 2) Build a mesh of edges connecting the vertices:
Chris Dalton0879da02021-01-29 13:57:25 -070079 void contoursToMesh(VertexList* contours, int contourCnt, VertexList* mesh,
80 const Comparator&) const;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070081
Chris Dalton68b8bd52021-01-14 10:48:02 -070082 // 3) Sort the vertices in Y (and secondarily in X):
Chris Dalton47114db2021-01-06 00:35:20 -070083 static void SortedMerge(VertexList* front, VertexList* back, VertexList* result,
84 const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -070085 static void SortMesh(VertexList* vertices, const Comparator&);
86
87 // 4) Simplify the mesh by inserting new vertices at intersecting edges:
Chris Daltonebb37e72021-01-27 17:59:45 -070088 enum class SimplifyResult : bool {
Chris Dalton57ea1fc2021-01-05 13:37:44 -070089 kAlreadySimple,
Chris Daltonebb37e72021-01-27 17:59:45 -070090 kFoundSelfIntersection
Chris Dalton57ea1fc2021-01-05 13:37:44 -070091 };
92
Chris Dalton0879da02021-01-29 13:57:25 -070093 SimplifyResult simplify(VertexList* mesh, const Comparator&) const;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070094
95 // 5) Tessellate the simplified mesh into monotone polygons:
Chris Dalton330cfa42021-01-27 18:24:48 -070096 virtual Poly* tessellate(const VertexList& vertices, const Comparator&) const;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070097
98 // 6) Triangulate the monotone polygons directly into a vertex buffer:
Chris Dalton0879da02021-01-29 13:57:25 -070099 void* polysToTriangles(Poly* polys, void* data, SkPathFillType overrideFillType) const;
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700100
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700101 // The vertex sorting in step (3) is a merge sort, since it plays well with the linked list
102 // of vertices (and the necessity of inserting new vertices on intersection).
103 //
104 // Stages (4) and (5) use an active edge list -- a list of all edges for which the
105 // sweep line has crossed the top vertex, but not the bottom vertex. It's sorted
106 // left-to-right based on the point where both edges are active (when both top vertices
107 // have been seen, so the "lower" top vertex of the two). If the top vertices are equal
108 // (shared), it's sorted based on the last point where both edges are active, so the
109 // "upper" bottom vertex.
110 //
111 // The most complex step is the simplification (4). It's based on the Bentley-Ottman
112 // line-sweep algorithm, but due to floating point inaccuracy, the intersection points are
113 // not exact and may violate the mesh topology or active edge list ordering. We
114 // accommodate this by adjusting the topology of the mesh and AEL to match the intersection
115 // points. This occurs in two ways:
116 //
117 // A) Intersections may cause a shortened edge to no longer be ordered with respect to its
118 // neighbouring edges at the top or bottom vertex. This is handled by merging the
Chris Dalton68b8bd52021-01-14 10:48:02 -0700119 // edges (mergeCollinearVertices()).
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700120 // B) Intersections may cause an edge to violate the left-to-right ordering of the
121 // active edge list. This is handled by detecting potential violations and rewinding
122 // the active edge list to the vertex before they occur (rewind() during merging,
123 // rewind_if_necessary() during splitting).
124 //
125 // The tessellation steps (5) and (6) are based on "Triangulating Simple Polygons and
126 // Equivalent Problems" (Fournier and Montuno); also a line-sweep algorithm. Note that it
127 // currently uses a linked list for the active edge list, rather than a 2-3 tree as the
128 // paper describes. The 2-3 tree gives O(lg N) lookups, but insertion and removal also
129 // become O(lg N). In all the test cases, it was found that the cost of frequent O(lg N)
130 // insertions and removals was greater than the cost of infrequent O(N) lookups with the
131 // linked list implementation. With the latter, all removals are O(1), and most insertions
132 // are O(1), since we know the adjacent edge in the active edge list based on the topology.
133 // Only type 2 vertices (see paper) require the O(N) lookups, and these are much less
134 // frequent. There may be other data structures worth investigating, however.
135 //
136 // Note that the orientation of the line sweep algorithms is determined by the aspect ratio of
137 // the path bounds. When the path is taller than it is wide, we sort vertices based on
138 // increasing Y coordinate, and secondarily by increasing X coordinate. When the path is wider
139 // than it is tall, we sort by increasing X coordinate, but secondarily by *decreasing* Y
140 // coordinate. This is so that the "left" and "right" orientation in the code remains correct
141 // (edges to the left are increasing in Y; edges to the right are decreasing in Y). That is, the
142 // setting rotates 90 degrees counterclockwise, rather that transposing.
143
144 // Additional helpers and driver functions.
Chris Dalton0879da02021-01-29 13:57:25 -0700145 void* emitMonotonePoly(const MonotonePoly*, void* data) const;
146 void* emitTriangle(Vertex* prev, Vertex* curr, Vertex* next, int winding, void* data) const;
147 void* emitPoly(const Poly*, void *data) const;
Chris Dalton330cfa42021-01-27 18:24:48 -0700148 Poly* makePoly(Poly** head, Vertex* v, int winding) const;
149 void appendPointToContour(const SkPoint& p, VertexList* contour) const;
150 void appendQuadraticToContour(const SkPoint[3], SkScalar toleranceSqd,
151 VertexList* contour) const;
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700152 void generateCubicPoints(const SkPoint&, const SkPoint&, const SkPoint&, const SkPoint&,
Chris Dalton330cfa42021-01-27 18:24:48 -0700153 SkScalar tolSqd, VertexList* contour, int pointsLeft) const;
154 bool applyFillType(int winding) const;
155 Edge* makeEdge(Vertex* prev, Vertex* next, EdgeType type, const Comparator&) const;
Chris Dalton0879da02021-01-29 13:57:25 -0700156 void setTop(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current,
157 const Comparator&) const;
Chris Dalton68b8bd52021-01-14 10:48:02 -0700158 void setBottom(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current,
Chris Dalton0879da02021-01-29 13:57:25 -0700159 const Comparator&) const;
Chris Dalton68b8bd52021-01-14 10:48:02 -0700160 void mergeEdgesAbove(Edge* edge, Edge* other, EdgeList* activeEdges, Vertex** current,
Chris Dalton0879da02021-01-29 13:57:25 -0700161 const Comparator&) const;
Chris Dalton68b8bd52021-01-14 10:48:02 -0700162 void mergeEdgesBelow(Edge* edge, Edge* other, EdgeList* activeEdges, Vertex** current,
Chris Dalton0879da02021-01-29 13:57:25 -0700163 const Comparator&) const;
Chris Dalton7cf3add2021-01-11 18:33:28 -0700164 Edge* makeConnectingEdge(Vertex* prev, Vertex* next, EdgeType, const Comparator&,
Chris Dalton0879da02021-01-29 13:57:25 -0700165 int windingScale = 1) const;
166 void mergeVertices(Vertex* src, Vertex* dst, VertexList* mesh, const Comparator&) const;
Chris Dalton47114db2021-01-06 00:35:20 -0700167 static void FindEnclosingEdges(Vertex* v, EdgeList* edges, Edge** left, Edge** right);
Chris Dalton68b8bd52021-01-14 10:48:02 -0700168 void mergeCollinearEdges(Edge* edge, EdgeList* activeEdges, Vertex** current,
Chris Dalton0879da02021-01-29 13:57:25 -0700169 const Comparator&) const;
Chris Dalton811dc6a2021-01-07 16:40:32 -0700170 bool splitEdge(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current,
Chris Dalton0879da02021-01-29 13:57:25 -0700171 const Comparator&) const;
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700172 bool intersectEdgePair(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
Chris Dalton0879da02021-01-29 13:57:25 -0700173 const Comparator&) const;
Chris Dalton7cf3add2021-01-11 18:33:28 -0700174 Vertex* makeSortedVertex(const SkPoint&, uint8_t alpha, VertexList* mesh, Vertex* reference,
Chris Dalton330cfa42021-01-27 18:24:48 -0700175 const Comparator&) const;
176 void computeBisector(Edge* edge1, Edge* edge2, Vertex*) const;
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700177 bool checkForIntersection(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
Chris Dalton0879da02021-01-29 13:57:25 -0700178 VertexList* mesh, const Comparator&) const;
Chris Dalton330cfa42021-01-27 18:24:48 -0700179 void sanitizeContours(VertexList* contours, int contourCnt) const;
Chris Dalton0879da02021-01-29 13:57:25 -0700180 bool mergeCoincidentVertices(VertexList* mesh, const Comparator&) const;
181 void buildEdges(VertexList* contours, int contourCnt, VertexList* mesh,
182 const Comparator&) const;
183 Poly* contoursToPolys(VertexList* contours, int contourCnt) const;
184 Poly* pathToPolys(float tolerance, const SkRect& clipBounds,
Chris Dalton330cfa42021-01-27 18:24:48 -0700185 bool* isLinear) const;
Chris Daltond5384792021-01-20 15:43:24 -0700186 static int64_t CountPoints(Poly* polys, SkPathFillType overrideFillType);
Chris Dalton0879da02021-01-29 13:57:25 -0700187 int polysToTriangles(Poly*, GrEagerVertexAllocator*) const;
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700188
Chris Dalton330cfa42021-01-27 18:24:48 -0700189 // FIXME: fPath should be plumbed through function parameters instead.
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700190 const SkPath fPath;
Chris Dalton8a42b092021-01-21 22:09:26 -0700191 SkArenaAlloc* const fAlloc;
Chris Dalton854ee852021-01-05 15:12:59 -0700192
Chris Dalton68b8bd52021-01-14 10:48:02 -0700193 // Internal control knobs.
Chris Dalton854ee852021-01-05 15:12:59 -0700194 bool fRoundVerticesToQuarterPixel = false;
195 bool fEmitCoverage = false;
Chris Dalton0879da02021-01-29 13:57:25 -0700196 bool fPreserveCollinearVertices = false;
197 bool fCollectBreadcrumbTriangles = false;
198
199 // The breadcrumb triangles serve as a glue that erases T-junctions between a path's outer
200 // curves and its inner polygon triangulation. Drawing a path's outer curves, breadcrumb
201 // triangles, and inner polygon triangulation all together into the stencil buffer has the same
202 // identical rasterized effect as stenciling a classic Redbook fan.
203 //
204 // The breadcrumb triangles track all the edge splits that led from the original inner polygon
205 // edges to the final triangulation. Every time an edge splits, we emit a razor-thin breadcrumb
206 // triangle consisting of the edge's original endpoints and the split point. (We also add
207 // supplemental breadcrumb triangles to areas where abs(winding) > 1.)
208 //
209 // a
210 // /
211 // /
212 // /
213 // x <- Edge splits at x. New breadcrumb triangle is: [a, b, x].
214 // /
215 // /
216 // b
217 //
218 // The opposite-direction shared edges between the triangulation and breadcrumb triangles should
219 // all cancel out, leaving just the set of edges from the original polygon.
220 class BreadcrumbTriangleList {
221 public:
222 struct Node {
223 Node(SkPoint a, SkPoint b, SkPoint c) : fPts{a, b, c} {}
224 SkPoint fPts[3];
225 Node* fNext = nullptr;
226 };
227 const Node* head() const { return fHead; }
228 int count() const { return fCount; }
229
230 void append(SkArenaAlloc* alloc, SkPoint a, SkPoint b, SkPoint c, int winding) {
231 if (a == b || a == c || b == c || winding == 0) {
232 return;
233 }
234 if (winding < 0) {
235 std::swap(a, b);
236 winding = -winding;
237 }
238 for (int i = 0; i < winding; ++i) {
239 SkASSERT(fTail && !(*fTail));
240 *fTail = alloc->make<Node>(a, b, c);
241 fTail = &(*fTail)->fNext;
242 }
243 fCount += winding;
244 }
245
246 void concat(BreadcrumbTriangleList&& list) {
247 SkASSERT(fTail && !(*fTail));
248 if (list.fHead) {
249 *fTail = list.fHead;
250 fTail = list.fTail;
251 fCount += list.fCount;
252 list.fHead = nullptr;
253 list.fTail = &list.fHead;
254 list.fCount = 0;
255 }
256 }
257
258 private:
259 Node* fHead = nullptr;
260 Node** fTail = &fHead;
261 int fCount = 0;
262 };
263
264 mutable BreadcrumbTriangleList fBreadcrumbList;
Chris Daltondcc8c542020-01-28 17:55:56 -0700265};
266
Chris Dalton5045de32021-01-07 19:09:01 -0700267/**
268 * Vertices are used in three ways: first, the path contours are converted into a
269 * circularly-linked list of Vertices for each contour. After edge construction, the same Vertices
270 * are re-ordered by the merge sort according to the sweep_lt comparator (usually, increasing
271 * in Y) using the same fPrev/fNext pointers that were used for the contours, to avoid
272 * reallocation. Finally, MonotonePolys are built containing a circularly-linked list of
273 * Vertices. (Currently, those Vertices are newly-allocated for the MonotonePolys, since
274 * an individual Vertex from the path mesh may belong to multiple
275 * MonotonePolys, so the original Vertices cannot be re-used.
276 */
277
278struct GrTriangulator::Vertex {
279 Vertex(const SkPoint& point, uint8_t alpha)
280 : fPoint(point), fPrev(nullptr), fNext(nullptr)
281 , fFirstEdgeAbove(nullptr), fLastEdgeAbove(nullptr)
282 , fFirstEdgeBelow(nullptr), fLastEdgeBelow(nullptr)
283 , fLeftEnclosingEdge(nullptr), fRightEnclosingEdge(nullptr)
284 , fPartner(nullptr)
285 , fAlpha(alpha)
286 , fSynthetic(false)
287#if TRIANGULATOR_LOGGING
288 , fID (-1.0f)
289#endif
290 {}
291 SkPoint fPoint; // Vertex position
292 Vertex* fPrev; // Linked list of contours, then Y-sorted vertices.
293 Vertex* fNext; // "
294 Edge* fFirstEdgeAbove; // Linked list of edges above this vertex.
295 Edge* fLastEdgeAbove; // "
296 Edge* fFirstEdgeBelow; // Linked list of edges below this vertex.
297 Edge* fLastEdgeBelow; // "
298 Edge* fLeftEnclosingEdge; // Nearest edge in the AEL left of this vertex.
299 Edge* fRightEnclosingEdge; // Nearest edge in the AEL right of this vertex.
300 Vertex* fPartner; // Corresponding inner or outer vertex (for AA).
301 uint8_t fAlpha;
302 bool fSynthetic; // Is this a synthetic vertex?
303#if TRIANGULATOR_LOGGING
304 float fID; // Identifier used for logging.
305#endif
Chris Dalton24472af2021-01-11 20:05:00 -0700306 bool isConnected() const { return this->fFirstEdgeAbove || this->fFirstEdgeBelow; }
Chris Dalton5045de32021-01-07 19:09:01 -0700307};
308
309struct GrTriangulator::VertexList {
310 VertexList() : fHead(nullptr), fTail(nullptr) {}
311 VertexList(Vertex* head, Vertex* tail) : fHead(head), fTail(tail) {}
312 Vertex* fHead;
313 Vertex* fTail;
314 void insert(Vertex* v, Vertex* prev, Vertex* next);
315 void append(Vertex* v) { insert(v, fTail, nullptr); }
316 void append(const VertexList& list) {
317 if (!list.fHead) {
318 return;
319 }
320 if (fTail) {
321 fTail->fNext = list.fHead;
322 list.fHead->fPrev = fTail;
323 } else {
324 fHead = list.fHead;
325 }
326 fTail = list.fTail;
327 }
328 void prepend(Vertex* v) { insert(v, nullptr, fHead); }
329 void remove(Vertex* v);
330 void close() {
331 if (fHead && fTail) {
332 fTail->fNext = fHead;
333 fHead->fPrev = fTail;
334 }
335 }
Chris Dalton24472af2021-01-11 20:05:00 -0700336#if TRIANGULATOR_LOGGING
Chris Dalton330cfa42021-01-27 18:24:48 -0700337 void dump() const;
Chris Dalton24472af2021-01-11 20:05:00 -0700338#endif
Chris Dalton5045de32021-01-07 19:09:01 -0700339};
340
341// A line equation in implicit form. fA * x + fB * y + fC = 0, for all points (x, y) on the line.
342struct GrTriangulator::Line {
343 Line(double a, double b, double c) : fA(a), fB(b), fC(c) {}
344 Line(Vertex* p, Vertex* q) : Line(p->fPoint, q->fPoint) {}
345 Line(const SkPoint& p, const SkPoint& q)
346 : fA(static_cast<double>(q.fY) - p.fY) // a = dY
347 , fB(static_cast<double>(p.fX) - q.fX) // b = -dX
348 , fC(static_cast<double>(p.fY) * q.fX - // c = cross(q, p)
349 static_cast<double>(p.fX) * q.fY) {}
350 double dist(const SkPoint& p) const { return fA * p.fX + fB * p.fY + fC; }
351 Line operator*(double v) const { return Line(fA * v, fB * v, fC * v); }
352 double magSq() const { return fA * fA + fB * fB; }
353 void normalize() {
354 double len = sqrt(this->magSq());
355 if (len == 0.0) {
356 return;
357 }
358 double scale = 1.0f / len;
359 fA *= scale;
360 fB *= scale;
361 fC *= scale;
362 }
363 bool nearParallel(const Line& o) const {
364 return fabs(o.fA - fA) < 0.00001 && fabs(o.fB - fB) < 0.00001;
365 }
366
367 // Compute the intersection of two (infinite) Lines.
368 bool intersect(const Line& other, SkPoint* point) const;
369 double fA, fB, fC;
370};
371
372/**
373 * An Edge joins a top Vertex to a bottom Vertex. Edge ordering for the list of "edges above" and
374 * "edge below" a vertex as well as for the active edge list is handled by isLeftOf()/isRightOf().
375 * Note that an Edge will give occasionally dist() != 0 for its own endpoints (because floating
376 * point). For speed, that case is only tested by the callers that require it (e.g.,
377 * rewind_if_necessary()). Edges also handle checking for intersection with other edges.
378 * Currently, this converts the edges to the parametric form, in order to avoid doing a division
379 * until an intersection has been confirmed. This is slightly slower in the "found" case, but
380 * a lot faster in the "not found" case.
381 *
382 * The coefficients of the line equation stored in double precision to avoid catastrophic
383 * cancellation in the isLeftOf() and isRightOf() checks. Using doubles ensures that the result is
384 * correct in float, since it's a polynomial of degree 2. The intersect() function, being
385 * degree 5, is still subject to catastrophic cancellation. We deal with that by assuming its
386 * output may be incorrect, and adjusting the mesh topology to match (see comment at the top of
387 * this file).
388 */
389
390struct GrTriangulator::Edge {
391 Edge(Vertex* top, Vertex* bottom, int winding, EdgeType type)
392 : fWinding(winding)
393 , fTop(top)
394 , fBottom(bottom)
395 , fType(type)
396 , fLeft(nullptr)
397 , fRight(nullptr)
398 , fPrevEdgeAbove(nullptr)
399 , fNextEdgeAbove(nullptr)
400 , fPrevEdgeBelow(nullptr)
401 , fNextEdgeBelow(nullptr)
402 , fLeftPoly(nullptr)
403 , fRightPoly(nullptr)
404 , fLeftPolyPrev(nullptr)
405 , fLeftPolyNext(nullptr)
406 , fRightPolyPrev(nullptr)
407 , fRightPolyNext(nullptr)
408 , fUsedInLeftPoly(false)
409 , fUsedInRightPoly(false)
410 , fLine(top, bottom) {
411 }
412 int fWinding; // 1 == edge goes downward; -1 = edge goes upward.
413 Vertex* fTop; // The top vertex in vertex-sort-order (sweep_lt).
414 Vertex* fBottom; // The bottom vertex in vertex-sort-order.
415 EdgeType fType;
416 Edge* fLeft; // The linked list of edges in the active edge list.
417 Edge* fRight; // "
418 Edge* fPrevEdgeAbove; // The linked list of edges in the bottom Vertex's "edges above".
419 Edge* fNextEdgeAbove; // "
420 Edge* fPrevEdgeBelow; // The linked list of edges in the top Vertex's "edges below".
421 Edge* fNextEdgeBelow; // "
422 Poly* fLeftPoly; // The Poly to the left of this edge, if any.
423 Poly* fRightPoly; // The Poly to the right of this edge, if any.
424 Edge* fLeftPolyPrev;
425 Edge* fLeftPolyNext;
426 Edge* fRightPolyPrev;
427 Edge* fRightPolyNext;
428 bool fUsedInLeftPoly;
429 bool fUsedInRightPoly;
430 Line fLine;
431 double dist(const SkPoint& p) const { return fLine.dist(p); }
432 bool isRightOf(Vertex* v) const { return fLine.dist(v->fPoint) < 0.0; }
433 bool isLeftOf(Vertex* v) const { return fLine.dist(v->fPoint) > 0.0; }
434 void recompute() { fLine = Line(fTop, fBottom); }
Chris Dalton24472af2021-01-11 20:05:00 -0700435 void insertAbove(Vertex*, const Comparator&);
436 void insertBelow(Vertex*, const Comparator&);
437 void disconnect();
Chris Dalton5045de32021-01-07 19:09:01 -0700438 bool intersect(const Edge& other, SkPoint* p, uint8_t* alpha = nullptr) const;
439};
440
441struct GrTriangulator::EdgeList {
442 EdgeList() : fHead(nullptr), fTail(nullptr) {}
443 Edge* fHead;
444 Edge* fTail;
445 void insert(Edge* edge, Edge* prev, Edge* next);
Chris Dalton24472af2021-01-11 20:05:00 -0700446 void insert(Edge* edge, Edge* prev);
Chris Dalton5045de32021-01-07 19:09:01 -0700447 void append(Edge* e) { insert(e, fTail, nullptr); }
448 void remove(Edge* edge);
449 void removeAll() {
450 while (fHead) {
451 this->remove(fHead);
452 }
453 }
454 void close() {
455 if (fHead && fTail) {
456 fTail->fRight = fHead;
457 fHead->fLeft = fTail;
458 }
459 }
460 bool contains(Edge* edge) const { return edge->fLeft || edge->fRight || fHead == edge; }
461};
462
463struct GrTriangulator::MonotonePoly {
464 MonotonePoly(Edge* edge, Side side, int winding)
465 : fSide(side)
466 , fFirstEdge(nullptr)
467 , fLastEdge(nullptr)
468 , fPrev(nullptr)
469 , fNext(nullptr)
470 , fWinding(winding) {
471 this->addEdge(edge);
472 }
473 Side fSide;
474 Edge* fFirstEdge;
475 Edge* fLastEdge;
476 MonotonePoly* fPrev;
477 MonotonePoly* fNext;
478 int fWinding;
479 void addEdge(Edge*);
Chris Dalton5045de32021-01-07 19:09:01 -0700480};
481
482struct GrTriangulator::Poly {
483 Poly(Vertex* v, int winding)
484 : fFirstVertex(v)
485 , fWinding(winding)
486 , fHead(nullptr)
487 , fTail(nullptr)
488 , fNext(nullptr)
489 , fPartner(nullptr)
490 , fCount(0)
491 {
492#if TRIANGULATOR_LOGGING
493 static int gID = 0;
494 fID = gID++;
495 TESS_LOG("*** created Poly %d\n", fID);
496#endif
497 }
Chris Dalton8a42b092021-01-21 22:09:26 -0700498 Poly* addEdge(Edge* e, Side side, SkArenaAlloc* alloc);
Chris Dalton5045de32021-01-07 19:09:01 -0700499 Vertex* lastVertex() const { return fTail ? fTail->fLastEdge->fBottom : fFirstVertex; }
500 Vertex* fFirstVertex;
501 int fWinding;
502 MonotonePoly* fHead;
503 MonotonePoly* fTail;
504 Poly* fNext;
505 Poly* fPartner;
506 int fCount;
507#if TRIANGULATOR_LOGGING
508 int fID;
509#endif
510};
511
512struct GrTriangulator::Comparator {
513 enum class Direction { kVertical, kHorizontal };
514 Comparator(Direction direction) : fDirection(direction) {}
515 bool sweep_lt(const SkPoint& a, const SkPoint& b) const;
516 Direction fDirection;
517};
518
ethannicholase9709e82016-01-07 13:34:16 -0800519#endif