1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrTriangulator_DEFINED
#define GrTriangulator_DEFINED
#include "include/core/SkPath.h"
#include "include/core/SkPoint.h"
#include "src/core/SkArenaAlloc.h"
namespace pk {
struct SkRect;
#define TRIANGULATOR_LOGGING 0
#define TRIANGULATOR_WIREFRAME 0
/**
* Provides utility functions for converting paths to a collection of triangles.
*/
class GrTriangulator {
public:
constexpr static int kArenaDefaultChunkSize = 16 * 1024;
static int PathToTriangles(const SkPath& path,
SkScalar tolerance,
const SkRect& clipBounds,
std::vector<float>* vertex,
bool* isLinear) {
if (!path.isFinite()) {
return 0;
}
SkArenaAlloc alloc(kArenaDefaultChunkSize);
GrTriangulator triangulator(path, &alloc);
auto [polys, success] = triangulator.pathToPolys(tolerance, clipBounds, isLinear);
if (!success) {
return 0;
}
return triangulator.polysToTriangles(polys, vertex);
}
// Enums used by GrTriangulator internals.
typedef enum { kLeft_Side, kRight_Side } Side;
enum class EdgeType { kInner, kOuter, kConnector };
// Structs used by GrTriangulator internals.
struct Vertex;
struct VertexList;
struct Line;
struct Edge;
struct EdgeList;
struct MonotonePoly;
struct Poly;
struct Comparator;
protected:
GrTriangulator(const SkPath& path, SkArenaAlloc* alloc) : fPath(path), fAlloc(alloc) {}
virtual ~GrTriangulator() {}
// There are six stages to the basic algorithm:
//
// 1) Linearize the path contours into piecewise linear segments:
void pathToContours(float tolerance,
const SkRect& clipBounds,
VertexList* contours,
bool* isLinear) const;
// 2) Build a mesh of edges connecting the vertices:
void contoursToMesh(VertexList* contours, int contourCnt, VertexList* mesh, const Comparator&);
// 3) Sort the vertices in Y (and secondarily in X):
static void SortedMerge(VertexList* front,
VertexList* back,
VertexList* result,
const Comparator&);
static void SortMesh(VertexList* vertices, const Comparator&);
// 4) Simplify the mesh by inserting new vertices at intersecting edges:
enum class SimplifyResult {kFailed, kAlreadySimple, kFoundSelfIntersection };
enum class BoolFail { kFalse, kTrue, kFail };
SimplifyResult simplify(VertexList* mesh, const Comparator&);
// 5) Tessellate the simplified mesh into monotone polygons:
virtual std::tuple<Poly*, bool> tessellate(const VertexList& vertices, const Comparator&);
// 6) Triangulate the monotone polygons directly into a vertex buffer:
void polysToTriangles(Poly* polys,
std::vector<float>* data,
SkPathFillType overrideFillType) const;
// The vertex sorting in step (3) is a merge sort, since it plays well with the linked list
// of vertices (and the necessity of inserting new vertices on intersection).
//
// Stages (4) and (5) use an active edge list -- a list of all edges for which the
// sweep line has crossed the top vertex, but not the bottom vertex. It's sorted
// left-to-right based on the point where both edges are active (when both top vertices
// have been seen, so the "lower" top vertex of the two). If the top vertices are equal
// (shared), it's sorted based on the last point where both edges are active, so the
// "upper" bottom vertex.
//
// The most complex step is the simplification (4). It's based on the Bentley-Ottman
// line-sweep algorithm, but due to floating point inaccuracy, the intersection points are
// not exact and may violate the mesh topology or active edge list ordering. We
// accommodate this by adjusting the topology of the mesh and AEL to match the intersection
// points. This occurs in two ways:
//
// A) Intersections may cause a shortened edge to no longer be ordered with respect to its
// neighbouring edges at the top or bottom vertex. This is handled by merging the
// edges (mergeCollinearVertices()).
// B) Intersections may cause an edge to violate the left-to-right ordering of the
// active edge list. This is handled by detecting potential violations and rewinding
// the active edge list to the vertex before they occur (rewind() during merging,
// rewind_if_necessary() during splitting).
//
// The tessellation steps (5) and (6) are based on "Triangulating Simple Polygons and
// Equivalent Problems" (Fournier and Montuno); also a line-sweep algorithm. Note that it
// currently uses a linked list for the active edge list, rather than a 2-3 tree as the
// paper describes. The 2-3 tree gives O(lg N) lookups, but insertion and removal also
// become O(lg N). In all the test cases, it was found that the cost of frequent O(lg N)
// insertions and removals was greater than the cost of infrequent O(N) lookups with the
// linked list implementation. With the latter, all removals are O(1), and most insertions
// are O(1), since we know the adjacent edge in the active edge list based on the topology.
// Only type 2 vertices (see paper) require the O(N) lookups, and these are much less
// frequent. There may be other data structures worth investigating, however.
//
// Note that the orientation of the line sweep algorithms is determined by the aspect ratio of
// the path bounds. When the path is taller than it is wide, we sort vertices based on
// increasing Y coordinate, and secondarily by increasing X coordinate. When the path is wider
// than it is tall, we sort by increasing X coordinate, but secondarily by *decreasing* Y
// coordinate. This is so that the "left" and "right" orientation in the code remains correct
// (edges to the left are increasing in Y; edges to the right are decreasing in Y). That is, the
// setting rotates 90 degrees counterclockwise, rather that transposing.
// Additional helpers and driver functions.
void emitMonotonePoly(const MonotonePoly*, std::vector<float>* data) const;
void emitTriangle(
Vertex* prev, Vertex* curr, Vertex* next, int winding, std::vector<float>* data) const;
void emitPoly(const Poly*, std::vector<float>* data) const;
Poly* makePoly(Poly** head, Vertex* v, int winding) const;
void appendPointToContour(const SkPoint& p, VertexList* contour) const;
void appendQuadraticToContour(const SkPoint[3],
SkScalar toleranceSqd,
VertexList* contour) const;
void generateCubicPoints(const SkPoint&,
const SkPoint&,
const SkPoint&,
const SkPoint&,
SkScalar tolSqd,
VertexList* contour,
int pointsLeft) const;
bool applyFillType(int winding) const;
Edge* allocateEdge(Vertex* top, Vertex* bottom, int winding, EdgeType type);
Edge* makeEdge(Vertex* prev, Vertex* next, EdgeType type, const Comparator&);
bool setTop(Edge* edge,
Vertex* v,
EdgeList* activeEdges,
Vertex** current,
const Comparator&) const;
bool setBottom(Edge* edge,
Vertex* v,
EdgeList* activeEdges,
Vertex** current,
const Comparator&) const;
bool mergeEdgesAbove(Edge* edge,
Edge* other,
EdgeList* activeEdges,
Vertex** current,
const Comparator&) const;
bool mergeEdgesBelow(Edge* edge,
Edge* other,
EdgeList* activeEdges,
Vertex** current,
const Comparator&) const;
Edge* makeConnectingEdge(
Vertex* prev, Vertex* next, EdgeType, const Comparator&, int windingScale = 1);
void mergeVertices(Vertex* src, Vertex* dst, VertexList* mesh, const Comparator&) const;
static void FindEnclosingEdges(Vertex* v, EdgeList* edges, Edge** left, Edge** right);
bool mergeCollinearEdges(Edge* edge,
EdgeList* activeEdges,
Vertex** current,
const Comparator&) const;
BoolFail splitEdge(
Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, const Comparator&);
BoolFail intersectEdgePair(
Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current, const Comparator&);
Vertex* makeSortedVertex(const SkPoint&,
uint8_t alpha,
VertexList* mesh,
Vertex* reference,
const Comparator&) const;
void computeBisector(Edge* edge1, Edge* edge2, Vertex*) const;
BoolFail checkForIntersection(Edge* left,
Edge* right,
EdgeList* activeEdges,
Vertex** current,
VertexList* mesh,
const Comparator&);
void sanitizeContours(VertexList* contours, int contourCnt) const;
bool mergeCoincidentVertices(VertexList* mesh, const Comparator&) const;
void buildEdges(VertexList* contours, int contourCnt, VertexList* mesh, const Comparator&);
std::tuple<Poly*, bool> contoursToPolys(VertexList* contours, int contourCnt);
std::tuple<Poly*, bool> pathToPolys(float tolerance, const SkRect& clipBounds, bool* isLinear);
static int64_t CountPoints(Poly* polys, SkPathFillType overrideFillType);
int polysToTriangles(Poly*, std::vector<float>*) const;
// FIXME: fPath should be plumbed through function parameters instead.
const SkPath fPath;
SkArenaAlloc* const fAlloc;
int fNumEdges = 0;
// Internal control knobs.
bool fRoundVerticesToQuarterPixel = false;
bool fEmitCoverage = false;
bool fPreserveCollinearVertices = false;
bool fCollectBreadcrumbTriangles = false;
// The breadcrumb triangles serve as a glue that erases T-junctions between a path's outer
// curves and its inner polygon triangulation. Drawing a path's outer curves, breadcrumb
// triangles, and inner polygon triangulation all together into the stencil buffer has the same
// identical rasterized effect as stenciling a classic Redbook fan.
//
// The breadcrumb triangles track all the edge splits that led from the original inner polygon
// edges to the final triangulation. Every time an edge splits, we emit a razor-thin breadcrumb
// triangle consisting of the edge's original endpoints and the split point. (We also add
// supplemental breadcrumb triangles to areas where abs(winding) > 1.)
//
// a
// /
// /
// /
// x <- Edge splits at x. New breadcrumb triangle is: [a, b, x].
// /
// /
// b
//
// The opposite-direction shared edges between the triangulation and breadcrumb triangles should
// all cancel out, leaving just the set of edges from the original polygon.
class BreadcrumbTriangleList {
public:
struct Node {
Node(SkPoint a, SkPoint b, SkPoint c) : fPts{a, b, c} {}
SkPoint fPts[3];
Node* fNext = nullptr;
};
const Node* head() const { return fHead; }
int count() const { return fCount; }
void append(SkArenaAlloc* alloc, SkPoint a, SkPoint b, SkPoint c, int winding) {
if (a == b || a == c || b == c || winding == 0) {
return;
}
if (winding < 0) {
std::swap(a, b);
winding = -winding;
}
for (int i = 0; i < winding; ++i) {
*fTail = alloc->make<Node>(a, b, c);
fTail = &(*fTail)->fNext;
}
fCount += winding;
}
void concat(BreadcrumbTriangleList&& list) {
if (list.fHead) {
*fTail = list.fHead;
fTail = list.fTail;
fCount += list.fCount;
list.fHead = nullptr;
list.fTail = &list.fHead;
list.fCount = 0;
}
}
private:
Node* fHead = nullptr;
Node** fTail = &fHead;
int fCount = 0;
};
mutable BreadcrumbTriangleList fBreadcrumbList;
};
/**
* Vertices are used in three ways: first, the path contours are converted into a
* circularly-linked list of Vertices for each contour. After edge construction, the same Vertices
* are re-ordered by the merge sort according to the sweep_lt comparator (usually, increasing
* in Y) using the same fPrev/fNext pointers that were used for the contours, to avoid
* reallocation. Finally, MonotonePolys are built containing a circularly-linked list of
* Vertices. (Currently, those Vertices are newly-allocated for the MonotonePolys, since
* an individual Vertex from the path mesh may belong to multiple
* MonotonePolys, so the original Vertices cannot be re-used.
*/
struct GrTriangulator::Vertex {
Vertex(const SkPoint& point, uint8_t alpha)
: fPoint(point)
, fPrev(nullptr)
, fNext(nullptr)
, fFirstEdgeAbove(nullptr)
, fLastEdgeAbove(nullptr)
, fFirstEdgeBelow(nullptr)
, fLastEdgeBelow(nullptr)
, fLeftEnclosingEdge(nullptr)
, fRightEnclosingEdge(nullptr)
, fPartner(nullptr)
, fAlpha(alpha)
, fSynthetic(false)
#if TRIANGULATOR_LOGGING
, fID(-1.0f)
#endif
{
}
SkPoint fPoint; // Vertex position
Vertex* fPrev; // Linked list of contours, then Y-sorted vertices.
Vertex* fNext; // "
Edge* fFirstEdgeAbove; // Linked list of edges above this vertex.
Edge* fLastEdgeAbove; // "
Edge* fFirstEdgeBelow; // Linked list of edges below this vertex.
Edge* fLastEdgeBelow; // "
Edge* fLeftEnclosingEdge; // Nearest edge in the AEL left of this vertex.
Edge* fRightEnclosingEdge; // Nearest edge in the AEL right of this vertex.
Vertex* fPartner; // Corresponding inner or outer vertex (for AA).
uint8_t fAlpha;
bool fSynthetic; // Is this a synthetic vertex?
#if TRIANGULATOR_LOGGING
float fID; // Identifier used for logging.
#endif
bool isConnected() const { return this->fFirstEdgeAbove || this->fFirstEdgeBelow; }
};
struct GrTriangulator::VertexList {
VertexList() : fHead(nullptr), fTail(nullptr) {}
VertexList(Vertex* head, Vertex* tail) : fHead(head), fTail(tail) {}
Vertex* fHead;
Vertex* fTail;
void insert(Vertex* v, Vertex* prev, Vertex* next);
void append(Vertex* v) { insert(v, fTail, nullptr); }
void append(const VertexList& list) {
if (!list.fHead) {
return;
}
if (fTail) {
fTail->fNext = list.fHead;
list.fHead->fPrev = fTail;
} else {
fHead = list.fHead;
}
fTail = list.fTail;
}
void prepend(Vertex* v) { insert(v, nullptr, fHead); }
void remove(Vertex* v);
void close() {
if (fHead && fTail) {
fTail->fNext = fHead;
fHead->fPrev = fTail;
}
}
#if TRIANGULATOR_LOGGING
void dump() const;
#endif
};
// A line equation in implicit form. fA * x + fB * y + fC = 0, for all points (x, y) on the line.
struct GrTriangulator::Line {
Line(double a, double b, double c) : fA(a), fB(b), fC(c) {}
Line(Vertex* p, Vertex* q) : Line(p->fPoint, q->fPoint) {}
Line(const SkPoint& p, const SkPoint& q)
: fA(static_cast<double>(q.fY) - p.fY) // a = dY
, fB(static_cast<double>(p.fX) - q.fX) // b = -dX
, fC(static_cast<double>(p.fY) * q.fX - // c = cross(q, p)
static_cast<double>(p.fX) * q.fY) {}
double dist(const SkPoint& p) const { return fA * p.fX + fB * p.fY + fC; }
Line operator*(double v) const { return Line(fA * v, fB * v, fC * v); }
double magSq() const { return fA * fA + fB * fB; }
void normalize() {
double len = sqrt(this->magSq());
if (len == 0.0) {
return;
}
double scale = 1.0f / len;
fA *= scale;
fB *= scale;
fC *= scale;
}
bool nearParallel(const Line& o) const {
return fabs(o.fA - fA) < 0.00001 && fabs(o.fB - fB) < 0.00001;
}
// Compute the intersection of two (infinite) Lines.
bool intersect(const Line& other, SkPoint* point) const;
double fA, fB, fC;
};
/**
* An Edge joins a top Vertex to a bottom Vertex. Edge ordering for the list of "edges above" and
* "edge below" a vertex as well as for the active edge list is handled by isLeftOf()/isRightOf().
* Note that an Edge will give occasionally dist() != 0 for its own endpoints (because floating
* point). For speed, that case is only tested by the callers that require it (e.g.,
* rewind_if_necessary()). Edges also handle checking for intersection with other edges.
* Currently, this converts the edges to the parametric form, in order to avoid doing a division
* until an intersection has been confirmed. This is slightly slower in the "found" case, but
* a lot faster in the "not found" case.
*
* The coefficients of the line equation stored in double precision to avoid catastrophic
* cancellation in the isLeftOf() and isRightOf() checks. Using doubles ensures that the result is
* correct in float, since it's a polynomial of degree 2. The intersect() function, being
* degree 5, is still subject to catastrophic cancellation. We deal with that by assuming its
* output may be incorrect, and adjusting the mesh topology to match (see comment at the top of
* this file).
*/
struct GrTriangulator::Edge {
Edge(Vertex* top, Vertex* bottom, int winding, EdgeType type)
: fWinding(winding)
, fTop(top)
, fBottom(bottom)
, fType(type)
, fLeft(nullptr)
, fRight(nullptr)
, fPrevEdgeAbove(nullptr)
, fNextEdgeAbove(nullptr)
, fPrevEdgeBelow(nullptr)
, fNextEdgeBelow(nullptr)
, fLeftPoly(nullptr)
, fRightPoly(nullptr)
, fLeftPolyPrev(nullptr)
, fLeftPolyNext(nullptr)
, fRightPolyPrev(nullptr)
, fRightPolyNext(nullptr)
, fUsedInLeftPoly(false)
, fUsedInRightPoly(false)
, fLine(top, bottom) {}
int fWinding; // 1 == edge goes downward; -1 = edge goes upward.
Vertex* fTop; // The top vertex in vertex-sort-order (sweep_lt).
Vertex* fBottom; // The bottom vertex in vertex-sort-order.
EdgeType fType;
Edge* fLeft; // The linked list of edges in the active edge list.
Edge* fRight; // "
Edge* fPrevEdgeAbove; // The linked list of edges in the bottom Vertex's "edges above".
Edge* fNextEdgeAbove; // "
Edge* fPrevEdgeBelow; // The linked list of edges in the top Vertex's "edges below".
Edge* fNextEdgeBelow; // "
Poly* fLeftPoly; // The Poly to the left of this edge, if any.
Poly* fRightPoly; // The Poly to the right of this edge, if any.
Edge* fLeftPolyPrev;
Edge* fLeftPolyNext;
Edge* fRightPolyPrev;
Edge* fRightPolyNext;
bool fUsedInLeftPoly;
bool fUsedInRightPoly;
Line fLine;
double dist(const SkPoint& p) const {
// Coerce points coincident with the vertices to have dist = 0, since converting from
// a double intersection point back to float storage might construct a point that's no
// longer on the ideal line.
return (p == fTop->fPoint || p == fBottom->fPoint) ? 0.0 : fLine.dist(p);
}
bool isRightOf(Vertex* v) const { return this->dist(v->fPoint) < 0.0; }
bool isLeftOf(Vertex* v) const { return this->dist(v->fPoint) > 0.0; }
void recompute() { fLine = Line(fTop, fBottom); }
void insertAbove(Vertex*, const Comparator&);
void insertBelow(Vertex*, const Comparator&);
void disconnect();
bool intersect(const Edge& other, SkPoint* p, uint8_t* alpha = nullptr) const;
};
struct GrTriangulator::EdgeList {
EdgeList() : fHead(nullptr), fTail(nullptr) {}
Edge* fHead;
Edge* fTail;
void insert(Edge* edge, Edge* prev, Edge* next);
bool insert(Edge* edge, Edge* prev);
void append(Edge* e) { insert(e, fTail, nullptr); }
bool remove(Edge* edge);
void removeAll() {
while (fHead) {
this->remove(fHead);
}
}
void close() {
if (fHead && fTail) {
fTail->fRight = fHead;
fHead->fLeft = fTail;
}
}
bool contains(Edge* edge) const { return edge->fLeft || edge->fRight || fHead == edge; }
};
struct GrTriangulator::MonotonePoly {
MonotonePoly(Edge* edge, Side side, int winding)
: fSide(side)
, fFirstEdge(nullptr)
, fLastEdge(nullptr)
, fPrev(nullptr)
, fNext(nullptr)
, fWinding(winding) {
this->addEdge(edge);
}
Side fSide;
Edge* fFirstEdge;
Edge* fLastEdge;
MonotonePoly* fPrev;
MonotonePoly* fNext;
int fWinding;
void addEdge(Edge*);
};
struct GrTriangulator::Poly {
Poly(Vertex* v, int winding);
Poly* addEdge(Edge* e, Side side, SkArenaAlloc* alloc);
Vertex* lastVertex() const { return fTail ? fTail->fLastEdge->fBottom : fFirstVertex; }
Vertex* fFirstVertex;
int fWinding;
MonotonePoly* fHead;
MonotonePoly* fTail;
Poly* fNext;
Poly* fPartner;
int fCount;
#if TRIANGULATOR_LOGGING
int fID;
#endif
};
struct GrTriangulator::Comparator {
enum class Direction { kVertical, kHorizontal };
Comparator(Direction direction) : fDirection(direction) {}
bool sweep_lt(const SkPoint& a, const SkPoint& b) const;
Direction fDirection;
};
} // namespace pk
#endif