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