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