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