Coverage Report

Created: 2021-08-22 09:07

/src/skia/src/gpu/geometry/GrTriangulator.h
Line
Count
Source (jump to first uncovered line)
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
1.37M
#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
30
    static int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
31
1.27k
                               GrEagerVertexAllocator* vertexAllocator, bool* isLinear) {
32
1.27k
        SkArenaAlloc alloc(kArenaDefaultChunkSize);
33
1.27k
        GrTriangulator triangulator(path, &alloc);
34
1.27k
        Poly* polys = triangulator.pathToPolys(tolerance, clipBounds, isLinear);
35
1.27k
        int count = triangulator.polysToTriangles(polys, vertexAllocator);
36
1.27k
        return count;
37
1.27k
    }
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:
54
10.0k
    GrTriangulator(const SkPath& path, SkArenaAlloc* alloc) : fPath(path), fAlloc(alloc) {}
55
10.0k
    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 {
208
0
            Node(SkPoint a, SkPoint b, SkPoint c) : fPts{a, b, c} {}
209
            SkPoint fPts[3];
210
            Node* fNext = nullptr;
211
        };
212
0
        const Node* head() const { return fHead; }
213
0
        int count() const { return fCount; }
214
215
0
        void append(SkArenaAlloc* alloc, SkPoint a, SkPoint b, SkPoint c, int winding) {
216
0
            if (a == b || a == c || b == c || winding == 0) {
217
0
                return;
218
0
            }
219
0
            if (winding < 0) {
220
0
                std::swap(a, b);
221
0
                winding = -winding;
222
0
            }
223
0
            for (int i = 0; i < winding; ++i) {
224
0
                SkASSERT(fTail && !(*fTail));
225
0
                *fTail = alloc->make<Node>(a, b, c);
226
0
                fTail = &(*fTail)->fNext;
227
0
            }
228
0
            fCount += winding;
229
0
        }
Unexecuted instantiation: GrTriangulator::BreadcrumbTriangleList::append(SkArenaAlloc*, SkPoint, SkPoint, SkPoint, int)
Unexecuted instantiation: GrTriangulator::BreadcrumbTriangleList::append(SkArenaAlloc*, SkPoint, SkPoint, SkPoint, int)
230
231
0
        void concat(BreadcrumbTriangleList&& list) {
232
0
            SkASSERT(fTail && !(*fTail));
233
0
            if (list.fHead) {
234
0
                *fTail = list.fHead;
235
0
                fTail = list.fTail;
236
0
                fCount += list.fCount;
237
0
                list.fHead = nullptr;
238
0
                list.fTail = &list.fHead;
239
0
                list.fCount = 0;
240
0
            }
241
0
        }
Unexecuted instantiation: GrTriangulator::BreadcrumbTriangleList::concat(GrTriangulator::BreadcrumbTriangleList&&)
Unexecuted instantiation: GrTriangulator::BreadcrumbTriangleList::concat(GrTriangulator::BreadcrumbTriangleList&&)
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 {
264
  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
20.0M
    {}
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
291
28.8M
    bool isConnected() const { return this->fFirstEdgeAbove || this->fFirstEdgeBelow; }
292
};
293
294
struct GrTriangulator::VertexList {
295
4.69M
    VertexList() : fHead(nullptr), fTail(nullptr) {}
296
30.4M
    VertexList(Vertex* head, Vertex* tail) : fHead(head), fTail(tail) {}
297
    Vertex* fHead;
298
    Vertex* fTail;
299
    void insert(Vertex* v, Vertex* prev, Vertex* next);
300
216M
    void append(Vertex* v) { insert(v, fTail, nullptr); }
301
30.4M
    void append(const VertexList& list) {
302
30.4M
        if (!list.fHead) {
303
15.2M
            return;
304
15.2M
        }
305
15.2M
        if (fTail) {
306
15.2M
            fTail->fNext = list.fHead;
307
15.2M
            list.fHead->fPrev = fTail;
308
15.8k
        } else {
309
15.8k
            fHead = list.fHead;
310
15.8k
        }
311
15.2M
        fTail = list.fTail;
312
15.2M
    }
313
7.79M
    void prepend(Vertex* v) { insert(v, nullptr, fHead); }
314
    void remove(Vertex* v);
315
0
    void close() {
316
0
        if (fHead && fTail) {
317
0
            fTail->fNext = fHead;
318
0
            fHead->fPrev = fTail;
319
0
        }
320
0
    }
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 {
328
78.5k
    Line(double a, double b, double c) : fA(a), fB(b), fC(c) {}
329
42.5M
    Line(Vertex* p, Vertex* q) : Line(p->fPoint, q->fPoint) {}
330
    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
59.6M
             static_cast<double>(p.fX) * q.fY) {}
335
163M
    double dist(const SkPoint& p) const { return fA * p.fX + fB * p.fY + fC; }
336
78.5k
    Line operator*(double v) const { return Line(fA * v, fB * v, fC * v); }
337
169k
    double magSq() const { return fA * fA + fB * fB; }
338
169k
    void normalize() {
339
169k
        double len = sqrt(this->magSq());
340
169k
        if (len == 0.0) {
341
0
            return;
342
0
        }
343
169k
        double scale = 1.0f / len;
344
169k
        fA *= scale;
345
169k
        fB *= scale;
346
169k
        fC *= scale;
347
169k
    }
348
74.9k
    bool nearParallel(const Line& o) const {
349
74.9k
        return fabs(o.fA - fA) < 0.00001 && fabs(o.fB - fB) < 0.00001;
350
74.9k
    }
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 {
376
    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
24.4M
        , fLine(top, bottom) {
396
24.4M
        }
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;
416
417
156M
    double dist(const SkPoint& p) const {
418
        // Coerce points coincident with the vertices to have dist = 0, since converting from
419
        // a double intersection point back to float storage might construct a point that's no
420
        // longer on the ideal line.
421
156M
        return (p == fTop->fPoint || p == fBottom->fPoint) ? 0.0 : fLine.dist(p);
422
156M
    }
423
84.4M
    bool isRightOf(Vertex* v) const { return this->dist(v->fPoint) < 0.0; }
424
72.1M
    bool isLeftOf(Vertex* v) const { return this->dist(v->fPoint) > 0.0; }
425
18.0M
    void recompute() { fLine = Line(fTop, fBottom); }
426
    void insertAbove(Vertex*, const Comparator&);
427
    void insertBelow(Vertex*, const Comparator&);
428
    void disconnect();
429
    bool intersect(const Edge& other, SkPoint* p, uint8_t* alpha = nullptr) const;
430
};
431
432
struct GrTriangulator::EdgeList {
433
88.6k
    EdgeList() : fHead(nullptr), fTail(nullptr) {}
434
    Edge* fHead;
435
    Edge* fTail;
436
    void insert(Edge* edge, Edge* prev, Edge* next);
437
    void insert(Edge* edge, Edge* prev);
438
77.3k
    void append(Edge* e) { insert(e, fTail, nullptr); }
439
    void remove(Edge* edge);
440
0
    void removeAll() {
441
0
        while (fHead) {
442
0
            this->remove(fHead);
443
0
        }
444
0
    }
445
0
    void close() {
446
0
        if (fHead && fTail) {
447
0
            fTail->fRight = fHead;
448
0
            fHead->fLeft = fTail;
449
0
        }
450
0
    }
451
0
    bool contains(Edge* edge) const { return edge->fLeft || edge->fRight || fHead == edge; }
452
};
453
454
struct GrTriangulator::MonotonePoly {
455
    MonotonePoly(Edge* edge, Side side, int winding)
456
        : fSide(side)
457
        , fFirstEdge(nullptr)
458
        , fLastEdge(nullptr)
459
        , fPrev(nullptr)
460
        , fNext(nullptr)
461
4.59M
        , fWinding(winding) {
462
4.59M
        this->addEdge(edge);
463
4.59M
    }
464
    Side          fSide;
465
    Edge*         fFirstEdge;
466
    Edge*         fLastEdge;
467
    MonotonePoly* fPrev;
468
    MonotonePoly* fNext;
469
    int fWinding;
470
    void addEdge(Edge*);
471
};
472
473
struct GrTriangulator::Poly {
474
    Poly(Vertex* v, int winding);
475
476
    Poly* addEdge(Edge* e, Side side, SkArenaAlloc* alloc);
477
29.9k
    Vertex* lastVertex() const { return fTail ? fTail->fLastEdge->fBottom : fFirstVertex; }
478
    Vertex* fFirstVertex;
479
    int fWinding;
480
    MonotonePoly* fHead;
481
    MonotonePoly* fTail;
482
    Poly* fNext;
483
    Poly* fPartner;
484
    int fCount;
485
#if TRIANGULATOR_LOGGING
486
    int fID;
487
#endif
488
};
489
490
struct GrTriangulator::Comparator {
491
    enum class Direction { kVertical, kHorizontal };
492
10.0k
    Comparator(Direction direction) : fDirection(direction) {}
493
    bool sweep_lt(const SkPoint& a, const SkPoint& b) const;
494
    Direction fDirection;
495
};
496
497
#endif