| /* |
| * 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 "include/private/SkColorData.h" |
| #include "src/core/SkArenaAlloc.h" |
| #include "src/gpu/GrColor.h" |
| |
| class GrEagerVertexAllocator; |
| 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, |
| GrEagerVertexAllocator* vertexAllocator, bool* isLinear) { |
| SkArenaAlloc alloc(kArenaDefaultChunkSize); |
| GrTriangulator triangulator(path, &alloc); |
| Poly* polys = triangulator.pathToPolys(tolerance, clipBounds, isLinear); |
| int count = triangulator.polysToTriangles(polys, vertexAllocator); |
| return count; |
| } |
| |
| // 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&) const; |
| |
| // 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 : bool { |
| kAlreadySimple, |
| kFoundSelfIntersection |
| }; |
| |
| SimplifyResult simplify(VertexList* mesh, const Comparator&) const; |
| |
| // 5) Tessellate the simplified mesh into monotone polygons: |
| virtual Poly* tessellate(const VertexList& vertices, const Comparator&) const; |
| |
| // 6) Triangulate the monotone polygons directly into a vertex buffer: |
| void* polysToTriangles(Poly* polys, void* 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*, void* data) const; |
| void* emitTriangle(Vertex* prev, Vertex* curr, Vertex* next, int winding, void* data) const; |
| void* emitPoly(const Poly*, void *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* makeEdge(Vertex* prev, Vertex* next, EdgeType type, const Comparator&) const; |
| void setTop(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, |
| const Comparator&) const; |
| void setBottom(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, |
| const Comparator&) const; |
| void mergeEdgesAbove(Edge* edge, Edge* other, EdgeList* activeEdges, Vertex** current, |
| const Comparator&) const; |
| void mergeEdgesBelow(Edge* edge, Edge* other, EdgeList* activeEdges, Vertex** current, |
| const Comparator&) const; |
| Edge* makeConnectingEdge(Vertex* prev, Vertex* next, EdgeType, const Comparator&, |
| int windingScale = 1) const; |
| void mergeVertices(Vertex* src, Vertex* dst, VertexList* mesh, const Comparator&) const; |
| static void FindEnclosingEdges(Vertex* v, EdgeList* edges, Edge** left, Edge** right); |
| void mergeCollinearEdges(Edge* edge, EdgeList* activeEdges, Vertex** current, |
| const Comparator&) const; |
| bool splitEdge(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, |
| const Comparator&) const; |
| bool intersectEdgePair(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current, |
| const Comparator&) const; |
| Vertex* makeSortedVertex(const SkPoint&, uint8_t alpha, VertexList* mesh, Vertex* reference, |
| const Comparator&) const; |
| void computeBisector(Edge* edge1, Edge* edge2, Vertex*) const; |
| bool checkForIntersection(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current, |
| VertexList* mesh, const Comparator&) const; |
| void sanitizeContours(VertexList* contours, int contourCnt) const; |
| bool mergeCoincidentVertices(VertexList* mesh, const Comparator&) const; |
| void buildEdges(VertexList* contours, int contourCnt, VertexList* mesh, |
| const Comparator&) const; |
| Poly* contoursToPolys(VertexList* contours, int contourCnt) const; |
| Poly* pathToPolys(float tolerance, const SkRect& clipBounds, |
| bool* isLinear) const; |
| static int64_t CountPoints(Poly* polys, SkPathFillType overrideFillType); |
| int polysToTriangles(Poly*, GrEagerVertexAllocator*) const; |
| |
| // FIXME: fPath should be plumbed through function parameters instead. |
| const SkPath fPath; |
| SkArenaAlloc* const fAlloc; |
| |
| // 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) { |
| SkASSERT(fTail && !(*fTail)); |
| *fTail = alloc->make<Node>(a, b, c); |
| fTail = &(*fTail)->fNext; |
| } |
| fCount += winding; |
| } |
| |
| void concat(BreadcrumbTriangleList&& list) { |
| SkASSERT(fTail && !(*fTail)); |
| 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 { return fLine.dist(p); } |
| bool isRightOf(Vertex* v) const { return fLine.dist(v->fPoint) < 0.0; } |
| bool isLeftOf(Vertex* v) const { return fLine.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); |
| void insert(Edge* edge, Edge* prev); |
| void append(Edge* e) { insert(e, fTail, nullptr); } |
| void 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; |
| }; |
| |
| #endif |