blob: 17b03b4b10535b94647ad43f18b14959aed0d2f2 [file] [log] [blame]
ethannicholase9709e82016-01-07 13:34:16 -08001/*
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 Dalton17dc4182020-03-25 16:18:16 -06008#ifndef GrTriangulator_DEFINED
9#define GrTriangulator_DEFINED
ethannicholase9709e82016-01-07 13:34:16 -080010
Chris Dalton57ea1fc2021-01-05 13:37:44 -070011#include "include/core/SkPath.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/core/SkPoint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/private/SkColorData.h"
Chris Dalton57ea1fc2021-01-05 13:37:44 -070014#include "src/core/SkArenaAlloc.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040015#include "src/gpu/GrColor.h"
senorblanco6599eff2016-03-10 08:38:45 -080016
Chris Daltond081dce2020-01-23 12:09:04 -070017class GrEagerVertexAllocator;
senorblanco6599eff2016-03-10 08:38:45 -080018struct SkRect;
ethannicholase9709e82016-01-07 13:34:16 -080019
Chris Dalton57ea1fc2021-01-05 13:37:44 -070020#define TRIANGULATOR_WIREFRAME 0
21
ethannicholase9709e82016-01-07 13:34:16 -080022/**
23 * Provides utility functions for converting paths to a collection of triangles.
24 */
Chris Dalton57ea1fc2021-01-05 13:37:44 -070025class GrTriangulator {
26public:
Chris Dalton854ee852021-01-05 15:12:59 -070027 static int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
28 GrEagerVertexAllocator* vertexAllocator, bool* isLinear) {
29 GrTriangulator triangulator(path);
30 int count = triangulator.pathToTriangles(tolerance, clipBounds, vertexAllocator,
31 path.getFillType());
32 *isLinear = triangulator.fIsLinear;
33 return count;
Chris Dalton57ea1fc2021-01-05 13:37:44 -070034 }
ethannicholase9709e82016-01-07 13:34:16 -080035
Chris Dalton854ee852021-01-05 15:12:59 -070036 static int PathToAATriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
37 GrEagerVertexAllocator* vertexAllocator, bool* isLinear) {
38 GrTriangulator triangulator(path);
39 triangulator.fRoundVerticesToQuarterPixel = true;
40 triangulator.fEmitCoverage = true;
41 int count = triangulator.pathToTriangles(tolerance, clipBounds, vertexAllocator,
42 SkPathFillType::kWinding);
43 *isLinear = triangulator.fIsLinear;
44 return count;
45 }
46
47 static int TriangulateSimpleInnerPolygons(const SkPath& path,
48 GrEagerVertexAllocator* vertexAllocator,
49 bool *isLinear) {
50 GrTriangulator triangulator(path);
51 triangulator.fCullCollinearVertices = false;
52 triangulator.fSimpleInnerPolygons = true;
53 int count = triangulator.pathToTriangles(0, SkRect::MakeEmpty(), vertexAllocator,
54 path.getFillType());
55 *isLinear = triangulator.fIsLinear;
56 return count;
57 }
ethannicholase9709e82016-01-07 13:34:16 -080058
Chris Dalton57ea1fc2021-01-05 13:37:44 -070059 struct WindingVertex {
60 SkPoint fPos;
61 int fWinding;
62 };
Chris Dalton6ccc0322020-01-29 11:38:16 -070063
Chris Dalton57ea1fc2021-01-05 13:37:44 -070064 // *DEPRECATED*: Once CCPR is removed this method will go away.
Chris Dalton6ccc0322020-01-29 11:38:16 -070065 //
Chris Dalton57ea1fc2021-01-05 13:37:44 -070066 // Triangulates a path to an array of vertices. Each triangle is represented as a set of three
67 // WindingVertex entries, each of which contains the position and winding count (which is the
68 // same for all three vertices of a triangle). The 'verts' out parameter is set to point to the
69 // resultant vertex array. CALLER IS RESPONSIBLE for deleting this buffer to avoid a memory
70 // leak!
71 static int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
72 WindingVertex** verts);
73
74 // Structs used by GrTriangulator internals.
75 struct Vertex;
76 struct VertexList;
77 struct Edge;
78 struct EdgeList;
79 struct Poly;
80 struct Comparator;
81
82private:
Chris Dalton854ee852021-01-05 15:12:59 -070083 GrTriangulator(const SkPath& path) : fPath(path) {}
Chris Dalton57ea1fc2021-01-05 13:37:44 -070084
85 // There are six stages to the basic algorithm:
86 //
87 // 1) Linearize the path contours into piecewise linear segments:
88 void pathToContours(float tolerance, const SkRect& clipBounds, VertexList* contours);
89
90 // 2) Build a mesh of edges connecting the vertices:
Chris Dalton811dc6a2021-01-07 16:40:32 -070091 void contoursToMesh(VertexList* contours, int contourCnt, VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -070092
93 // 3) Sort the vertices in Y (and secondarily in X) (merge_sort()).
94 static void SortMesh(VertexList* vertices, const Comparator&);
95
96 // 4) Simplify the mesh by inserting new vertices at intersecting edges:
97 enum class SimplifyResult {
98 kAlreadySimple,
99 kFoundSelfIntersection,
100 kAbort
101 };
102
Chris Dalton811dc6a2021-01-07 16:40:32 -0700103 SimplifyResult simplify(VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700104
105 // 5) Tessellate the simplified mesh into monotone polygons:
106 Poly* tessellate(const VertexList& vertices);
107
108 // 6) Triangulate the monotone polygons directly into a vertex buffer:
109 void* polysToTriangles(Poly* polys, void* data, SkPathFillType overrideFillType);
110
111 // For screenspace antialiasing, the algorithm is modified as follows:
112 //
113 // Run steps 1-5 above to produce polygons.
114 // 5b) Apply fill rules to extract boundary contours from the polygons (extract_boundaries()).
115 // 5c) Simplify boundaries to remove "pointy" vertices that cause inversions
116 // (simplify_boundary()).
117 // 5d) Displace edges by half a pixel inward and outward along their normals. Intersect to find
118 // new vertices, and set zero alpha on the exterior and one alpha on the interior. Build a
119 // new antialiased mesh from those vertices (stroke_boundary()).
120 // Run steps 3-6 above on the new mesh, and produce antialiased triangles.
121 //
122 // The vertex sorting in step (3) is a merge sort, since it plays well with the linked list
123 // of vertices (and the necessity of inserting new vertices on intersection).
124 //
125 // Stages (4) and (5) use an active edge list -- a list of all edges for which the
126 // sweep line has crossed the top vertex, but not the bottom vertex. It's sorted
127 // left-to-right based on the point where both edges are active (when both top vertices
128 // have been seen, so the "lower" top vertex of the two). If the top vertices are equal
129 // (shared), it's sorted based on the last point where both edges are active, so the
130 // "upper" bottom vertex.
131 //
132 // The most complex step is the simplification (4). It's based on the Bentley-Ottman
133 // line-sweep algorithm, but due to floating point inaccuracy, the intersection points are
134 // not exact and may violate the mesh topology or active edge list ordering. We
135 // accommodate this by adjusting the topology of the mesh and AEL to match the intersection
136 // points. This occurs in two ways:
137 //
138 // A) Intersections may cause a shortened edge to no longer be ordered with respect to its
139 // neighbouring edges at the top or bottom vertex. This is handled by merging the
140 // edges (merge_collinear_edges()).
141 // B) Intersections may cause an edge to violate the left-to-right ordering of the
142 // active edge list. This is handled by detecting potential violations and rewinding
143 // the active edge list to the vertex before they occur (rewind() during merging,
144 // rewind_if_necessary() during splitting).
145 //
146 // The tessellation steps (5) and (6) are based on "Triangulating Simple Polygons and
147 // Equivalent Problems" (Fournier and Montuno); also a line-sweep algorithm. Note that it
148 // currently uses a linked list for the active edge list, rather than a 2-3 tree as the
149 // paper describes. The 2-3 tree gives O(lg N) lookups, but insertion and removal also
150 // become O(lg N). In all the test cases, it was found that the cost of frequent O(lg N)
151 // insertions and removals was greater than the cost of infrequent O(N) lookups with the
152 // linked list implementation. With the latter, all removals are O(1), and most insertions
153 // are O(1), since we know the adjacent edge in the active edge list based on the topology.
154 // Only type 2 vertices (see paper) require the O(N) lookups, and these are much less
155 // frequent. There may be other data structures worth investigating, however.
156 //
157 // Note that the orientation of the line sweep algorithms is determined by the aspect ratio of
158 // the path bounds. When the path is taller than it is wide, we sort vertices based on
159 // increasing Y coordinate, and secondarily by increasing X coordinate. When the path is wider
160 // than it is tall, we sort by increasing X coordinate, but secondarily by *decreasing* Y
161 // coordinate. This is so that the "left" and "right" orientation in the code remains correct
162 // (edges to the left are increasing in Y; edges to the right are decreasing in Y). That is, the
163 // setting rotates 90 degrees counterclockwise, rather that transposing.
164
165 // Additional helpers and driver functions.
166 void appendPointToContour(const SkPoint& p, VertexList* contour);
167 void appendQuadraticToContour(const SkPoint[3], SkScalar toleranceSqd, VertexList* contour);
168 void generateCubicPoints(const SkPoint&, const SkPoint&, const SkPoint&, const SkPoint&,
169 SkScalar tolSqd, VertexList* contour, int pointsLeft);
Chris Dalton811dc6a2021-01-07 16:40:32 -0700170 bool splitEdge(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current,
171 const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700172 bool intersectEdgePair(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
Chris Dalton811dc6a2021-01-07 16:40:32 -0700173 const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700174 bool checkForIntersection(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
Chris Dalton811dc6a2021-01-07 16:40:32 -0700175 VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700176 void sanitizeContours(VertexList* contours, int contourCnt);
Chris Dalton811dc6a2021-01-07 16:40:32 -0700177 bool mergeCoincidentVertices(VertexList* mesh, const Comparator&);
178 void buildEdges(VertexList* contours, int contourCnt, VertexList* mesh, const Comparator&);
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700179 Poly* contoursToPolys(VertexList* contours, int contourCnt, VertexList* outerMesh);
180 Poly* pathToPolys(float tolerance, const SkRect& clipBounds, int contourCnt,
181 VertexList* outerMesh);
182 int pathToTriangles(float tolerance, const SkRect& clipBounds, GrEagerVertexAllocator*,
183 SkPathFillType overrideFillType);
184
185 constexpr static int kArenaChunkSize = 16 * 1024;
186 SkArenaAlloc fAlloc{kArenaChunkSize};
187 const SkPath fPath;
Chris Dalton57ea1fc2021-01-05 13:37:44 -0700188 bool fIsLinear = false;
Chris Dalton854ee852021-01-05 15:12:59 -0700189
190 // Flags.
191 bool fRoundVerticesToQuarterPixel = false;
192 bool fEmitCoverage = false;
193 bool fCullCollinearVertices = true;
194 bool fSimpleInnerPolygons = false;
Chris Daltondcc8c542020-01-28 17:55:56 -0700195};
196
ethannicholase9709e82016-01-07 13:34:16 -0800197#endif