xref: /aosp_15_r20/external/skia/src/gpu/tessellate/MiddleOutPolygonTriangulator.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2020 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skgpu_tessellate_MiddleOutPolygonTriangulator_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_tessellate_MiddleOutPolygonTriangulator_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkMathPriv.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathPriv.h"
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
21*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
22*c8dee2aaSAndroid Build Coastguard Worker #include <tuple>
23*c8dee2aaSAndroid Build Coastguard Worker 
24*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::tess {
25*c8dee2aaSAndroid Build Coastguard Worker 
26*c8dee2aaSAndroid Build Coastguard Worker // This class generates a middle-out triangulation of a polygon. Conceptually, middle-out emits one
27*c8dee2aaSAndroid Build Coastguard Worker // large triangle with vertices on both endpoints and a middle point, then recurses on both sides of
28*c8dee2aaSAndroid Build Coastguard Worker // the new triangle. i.e.:
29*c8dee2aaSAndroid Build Coastguard Worker //
30*c8dee2aaSAndroid Build Coastguard Worker //     void emit_middle_out_triangulation(int startIdx, int endIdx) {
31*c8dee2aaSAndroid Build Coastguard Worker //         if (startIdx + 1 == endIdx) {
32*c8dee2aaSAndroid Build Coastguard Worker //             return;
33*c8dee2aaSAndroid Build Coastguard Worker //         }
34*c8dee2aaSAndroid Build Coastguard Worker //         int middleIdx = startIdx + SkNextPow2(endIdx - startIdx) / 2;
35*c8dee2aaSAndroid Build Coastguard Worker //
36*c8dee2aaSAndroid Build Coastguard Worker //         // Recurse on the left half.
37*c8dee2aaSAndroid Build Coastguard Worker //         emit_middle_out_triangulation(startIdx, middleIdx);
38*c8dee2aaSAndroid Build Coastguard Worker //
39*c8dee2aaSAndroid Build Coastguard Worker //         // Emit a large triangle with vertices on both endpoints and a middle point.
40*c8dee2aaSAndroid Build Coastguard Worker //         emit_triangle(vertices[startIdx], vertices[middleIdx], vertices[endIdx - 1]);
41*c8dee2aaSAndroid Build Coastguard Worker //
42*c8dee2aaSAndroid Build Coastguard Worker //         // Recurse on the right half.
43*c8dee2aaSAndroid Build Coastguard Worker //         emit_middle_out_triangulation(middleIdx, endIdx);
44*c8dee2aaSAndroid Build Coastguard Worker //     }
45*c8dee2aaSAndroid Build Coastguard Worker //
46*c8dee2aaSAndroid Build Coastguard Worker // Middle-out produces drastically less work for the rasterizer as compared a linear triangle strip
47*c8dee2aaSAndroid Build Coastguard Worker // or fan.
48*c8dee2aaSAndroid Build Coastguard Worker //
49*c8dee2aaSAndroid Build Coastguard Worker // This class is designed to not know or store all the vertices in the polygon at once. The caller
50*c8dee2aaSAndroid Build Coastguard Worker // pushes each vertex in linear order (perhaps while parsing a path), then rather than relying on
51*c8dee2aaSAndroid Build Coastguard Worker // recursion, we manipulate an O(log N) stack to determine the correct middle-out triangulation.
52*c8dee2aaSAndroid Build Coastguard Worker class MiddleOutPolygonTriangulator {
53*c8dee2aaSAndroid Build Coastguard Worker private:
54*c8dee2aaSAndroid Build Coastguard Worker     // Internal representation of how we store vertices on our stack.
55*c8dee2aaSAndroid Build Coastguard Worker     struct StackVertex {
56*c8dee2aaSAndroid Build Coastguard Worker         SkPoint fPoint;
57*c8dee2aaSAndroid Build Coastguard Worker         // How many polygon vertices away is this vertex from the previous vertex on the stack?
58*c8dee2aaSAndroid Build Coastguard Worker         // i.e., the ith stack element's vertex index in the original polygon is:
59*c8dee2aaSAndroid Build Coastguard Worker         //
60*c8dee2aaSAndroid Build Coastguard Worker         //     fVertexStack[i].fVertexIdxDelta + fVertexStack[i - 1].fVertexIdxDelta + ... +
61*c8dee2aaSAndroid Build Coastguard Worker         //     fVertexStack[1].fVertexIdxDelta.
62*c8dee2aaSAndroid Build Coastguard Worker         //
63*c8dee2aaSAndroid Build Coastguard Worker         // NOTE: fVertexStack[0].fVertexIdxDelta always == 0.
64*c8dee2aaSAndroid Build Coastguard Worker         int fVertexIdxDelta;
65*c8dee2aaSAndroid Build Coastguard Worker     };
66*c8dee2aaSAndroid Build Coastguard Worker 
67*c8dee2aaSAndroid Build Coastguard Worker public:
68*c8dee2aaSAndroid Build Coastguard Worker     // RAII. This class is designed to first allow the caller to iterate the triangles that will be
69*c8dee2aaSAndroid Build Coastguard Worker     // popped off our stack, and then (during the destructor) it actually pops the finished vertices
70*c8dee2aaSAndroid Build Coastguard Worker     // and pushes a new one. Example usage:
71*c8dee2aaSAndroid Build Coastguard Worker     //
72*c8dee2aaSAndroid Build Coastguard Worker     //   for (auto [p0, p1, p2] : middleOut.pushVertex(pt)) {
73*c8dee2aaSAndroid Build Coastguard Worker     //       vertexWriter << p0 << p1 << p2;
74*c8dee2aaSAndroid Build Coastguard Worker     //   }
75*c8dee2aaSAndroid Build Coastguard Worker     //
76*c8dee2aaSAndroid Build Coastguard Worker     // The above code iterates over the triangles being popped, and then once iteration is finished,
77*c8dee2aaSAndroid Build Coastguard Worker     // the PoppedTriangleStack is destroyed, triggering the pending stack update.
78*c8dee2aaSAndroid Build Coastguard Worker     class PoppedTriangleStack {
79*c8dee2aaSAndroid Build Coastguard Worker     public:
PoppedTriangleStack(MiddleOutPolygonTriangulator * middleOut,SkPoint lastPoint,StackVertex * end,StackVertex * newTopVertex,StackVertex newTopValue)80*c8dee2aaSAndroid Build Coastguard Worker         PoppedTriangleStack(MiddleOutPolygonTriangulator* middleOut,
81*c8dee2aaSAndroid Build Coastguard Worker                             SkPoint lastPoint,
82*c8dee2aaSAndroid Build Coastguard Worker                             StackVertex* end,
83*c8dee2aaSAndroid Build Coastguard Worker                             StackVertex* newTopVertex,
84*c8dee2aaSAndroid Build Coastguard Worker                             StackVertex newTopValue)
85*c8dee2aaSAndroid Build Coastguard Worker             : fMiddleOut(middleOut)
86*c8dee2aaSAndroid Build Coastguard Worker             , fLastPoint(lastPoint)
87*c8dee2aaSAndroid Build Coastguard Worker             , fEnd(end)
88*c8dee2aaSAndroid Build Coastguard Worker             , fNewTopVertex(newTopVertex)
89*c8dee2aaSAndroid Build Coastguard Worker             , fNewTopValue(newTopValue) {
90*c8dee2aaSAndroid Build Coastguard Worker         }
91*c8dee2aaSAndroid Build Coastguard Worker 
PoppedTriangleStack(PoppedTriangleStack && that)92*c8dee2aaSAndroid Build Coastguard Worker         PoppedTriangleStack(PoppedTriangleStack&& that) {
93*c8dee2aaSAndroid Build Coastguard Worker             memcpy(this, &that, sizeof(*this));
94*c8dee2aaSAndroid Build Coastguard Worker             that.fMiddleOut = nullptr;  // Don't do a stack update during our destructor.
95*c8dee2aaSAndroid Build Coastguard Worker         }
96*c8dee2aaSAndroid Build Coastguard Worker 
~PoppedTriangleStack()97*c8dee2aaSAndroid Build Coastguard Worker         ~PoppedTriangleStack() {
98*c8dee2aaSAndroid Build Coastguard Worker             if (fMiddleOut) {
99*c8dee2aaSAndroid Build Coastguard Worker                 fMiddleOut->fTop = fNewTopVertex;
100*c8dee2aaSAndroid Build Coastguard Worker                 *fNewTopVertex = fNewTopValue;
101*c8dee2aaSAndroid Build Coastguard Worker             }
102*c8dee2aaSAndroid Build Coastguard Worker         }
103*c8dee2aaSAndroid Build Coastguard Worker 
104*c8dee2aaSAndroid Build Coastguard Worker         struct Iter {
105*c8dee2aaSAndroid Build Coastguard Worker             bool operator!=(const Iter& iter) { return fVertex != iter.fVertex; }
106*c8dee2aaSAndroid Build Coastguard Worker             void operator++() { --fVertex; }
107*c8dee2aaSAndroid Build Coastguard Worker             std::tuple<SkPoint, SkPoint, SkPoint> operator*() {
108*c8dee2aaSAndroid Build Coastguard Worker                 return {fVertex[-1].fPoint, fVertex[0].fPoint, fLastPoint};
109*c8dee2aaSAndroid Build Coastguard Worker             }
110*c8dee2aaSAndroid Build Coastguard Worker             StackVertex* fVertex;
111*c8dee2aaSAndroid Build Coastguard Worker             SkPoint fLastPoint;
112*c8dee2aaSAndroid Build Coastguard Worker         };
113*c8dee2aaSAndroid Build Coastguard Worker 
begin()114*c8dee2aaSAndroid Build Coastguard Worker         Iter begin() const { return {fMiddleOut ? fMiddleOut->fTop : fEnd, fLastPoint}; }
end()115*c8dee2aaSAndroid Build Coastguard Worker         Iter end() const { return {fEnd, fLastPoint}; }
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker     private:
118*c8dee2aaSAndroid Build Coastguard Worker         MiddleOutPolygonTriangulator* fMiddleOut;
119*c8dee2aaSAndroid Build Coastguard Worker         SkPoint fLastPoint;
120*c8dee2aaSAndroid Build Coastguard Worker         StackVertex* fEnd;
121*c8dee2aaSAndroid Build Coastguard Worker         StackVertex* fNewTopVertex;
122*c8dee2aaSAndroid Build Coastguard Worker         StackVertex fNewTopValue;
123*c8dee2aaSAndroid Build Coastguard Worker     };
124*c8dee2aaSAndroid Build Coastguard Worker 
125*c8dee2aaSAndroid Build Coastguard Worker     // maxPushVertexCalls is an upper bound on the number of times the caller will call
126*c8dee2aaSAndroid Build Coastguard Worker     // pushVertex(). The caller must not call it more times than this. (Beware of int overflow.)
127*c8dee2aaSAndroid Build Coastguard Worker     MiddleOutPolygonTriangulator(int maxPushVertexCalls, SkPoint startPoint = {0,0}) {
128*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(maxPushVertexCalls >= 0);
129*c8dee2aaSAndroid Build Coastguard Worker         // Determine the deepest our stack can ever go.
130*c8dee2aaSAndroid Build Coastguard Worker         int maxStackDepth = SkNextLog2(maxPushVertexCalls) + 1;
131*c8dee2aaSAndroid Build Coastguard Worker         if (maxStackDepth > kStackPreallocCount) {
132*c8dee2aaSAndroid Build Coastguard Worker             fVertexStack.reset(maxStackDepth);
133*c8dee2aaSAndroid Build Coastguard Worker         }
134*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(fStackAllocCount = maxStackDepth;)
135*c8dee2aaSAndroid Build Coastguard Worker         // The stack will always contain a starting point. This is an implicit moveTo(0, 0)
136*c8dee2aaSAndroid Build Coastguard Worker         // initially, but will be overridden if moveTo() gets called before adding geometry.
137*c8dee2aaSAndroid Build Coastguard Worker         fVertexStack[0] = {startPoint, 0};
138*c8dee2aaSAndroid Build Coastguard Worker         fTop = fVertexStack;
139*c8dee2aaSAndroid Build Coastguard Worker     }
140*c8dee2aaSAndroid Build Coastguard Worker 
141*c8dee2aaSAndroid Build Coastguard Worker     // Returns an RAII object that first allows the caller to iterate the triangles we will pop,
142*c8dee2aaSAndroid Build Coastguard Worker     // pops those triangles, and finally pushes 'pt' onto the vertex stack.
pushVertex(SkPoint pt)143*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] PoppedTriangleStack pushVertex(SkPoint pt) {
144*c8dee2aaSAndroid Build Coastguard Worker         // Our topology wants triangles that have the same vertexIdxDelta on both sides:
145*c8dee2aaSAndroid Build Coastguard Worker         // e.g., a run of 9 points should be triangulated as:
146*c8dee2aaSAndroid Build Coastguard Worker         //
147*c8dee2aaSAndroid Build Coastguard Worker         //    [0, 1, 2], [2, 3, 4], [4, 5, 6], [6, 7, 8]  // vertexIdxDelta == 1
148*c8dee2aaSAndroid Build Coastguard Worker         //    [0, 2, 4], [4, 6, 8]  // vertexIdxDelta == 2
149*c8dee2aaSAndroid Build Coastguard Worker         //    [0, 4, 8]  // vertexIdxDelta == 4
150*c8dee2aaSAndroid Build Coastguard Worker         //
151*c8dee2aaSAndroid Build Coastguard Worker         // Find as many new triangles as we can pop off the stack that have equal-delta sides. (This
152*c8dee2aaSAndroid Build Coastguard Worker         // is a stack-based implementation of the recursive example method from the class comment.)
153*c8dee2aaSAndroid Build Coastguard Worker         StackVertex* endVertex = fTop;
154*c8dee2aaSAndroid Build Coastguard Worker         int vertexIdxDelta = 1;
155*c8dee2aaSAndroid Build Coastguard Worker         while (endVertex->fVertexIdxDelta == vertexIdxDelta) {
156*c8dee2aaSAndroid Build Coastguard Worker             --endVertex;
157*c8dee2aaSAndroid Build Coastguard Worker             vertexIdxDelta *= 2;
158*c8dee2aaSAndroid Build Coastguard Worker         }
159*c8dee2aaSAndroid Build Coastguard Worker 
160*c8dee2aaSAndroid Build Coastguard Worker         // Once the above triangles are popped, push 'pt' to the top of the stack.
161*c8dee2aaSAndroid Build Coastguard Worker         StackVertex* newTopVertex = endVertex + 1;
162*c8dee2aaSAndroid Build Coastguard Worker         StackVertex newTopValue = {pt, vertexIdxDelta};
163*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(newTopVertex < fVertexStack + fStackAllocCount); // Is fStackAllocCount enough?
164*c8dee2aaSAndroid Build Coastguard Worker 
165*c8dee2aaSAndroid Build Coastguard Worker         return PoppedTriangleStack(this, pt, endVertex, newTopVertex, newTopValue);
166*c8dee2aaSAndroid Build Coastguard Worker     }
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker     // Returns an RAII object that first allows the caller to iterate the remaining triangles, then
169*c8dee2aaSAndroid Build Coastguard Worker     // resets the vertex stack with newStartPoint.
closeAndMove(SkPoint newStartPoint)170*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] PoppedTriangleStack closeAndMove(SkPoint newStartPoint) {
171*c8dee2aaSAndroid Build Coastguard Worker         // Add an implicit line back to the starting point.
172*c8dee2aaSAndroid Build Coastguard Worker         SkPoint startPt = fVertexStack[0].fPoint;
173*c8dee2aaSAndroid Build Coastguard Worker 
174*c8dee2aaSAndroid Build Coastguard Worker         // Triangulate the rest of the polygon. Since we simply have to finish now, we can't be
175*c8dee2aaSAndroid Build Coastguard Worker         // picky anymore about getting a pure middle-out topology.
176*c8dee2aaSAndroid Build Coastguard Worker         StackVertex* endVertex = std::min(fTop, fVertexStack + 1);
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker         // Once every remaining triangle is popped, reset the vertex stack with newStartPoint.
179*c8dee2aaSAndroid Build Coastguard Worker         StackVertex* newTopVertex = fVertexStack;
180*c8dee2aaSAndroid Build Coastguard Worker         StackVertex newTopValue = {newStartPoint, 0};
181*c8dee2aaSAndroid Build Coastguard Worker 
182*c8dee2aaSAndroid Build Coastguard Worker         return PoppedTriangleStack(this, startPt, endVertex, newTopVertex, newTopValue);
183*c8dee2aaSAndroid Build Coastguard Worker     }
184*c8dee2aaSAndroid Build Coastguard Worker 
185*c8dee2aaSAndroid Build Coastguard Worker     // Returns an RAII object that first allows the caller to iterate the remaining triangles, then
186*c8dee2aaSAndroid Build Coastguard Worker     // resets the vertex stack with the same starting point as it had before.
close()187*c8dee2aaSAndroid Build Coastguard Worker     [[nodiscard]] PoppedTriangleStack close() {
188*c8dee2aaSAndroid Build Coastguard Worker         return this->closeAndMove(fVertexStack[0].fPoint);
189*c8dee2aaSAndroid Build Coastguard Worker     }
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker private:
192*c8dee2aaSAndroid Build Coastguard Worker     constexpr static int kStackPreallocCount = 32;
193*c8dee2aaSAndroid Build Coastguard Worker     skia_private::AutoSTMalloc<kStackPreallocCount, StackVertex> fVertexStack;
194*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(int fStackAllocCount;)
195*c8dee2aaSAndroid Build Coastguard Worker     StackVertex* fTop;
196*c8dee2aaSAndroid Build Coastguard Worker };
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker // This is a helper class that transforms and pushes a path's inner fan vertices onto a
199*c8dee2aaSAndroid Build Coastguard Worker // MiddleOutPolygonTriangulator. Example usage:
200*c8dee2aaSAndroid Build Coastguard Worker //
201*c8dee2aaSAndroid Build Coastguard Worker //   for (PathMiddleOutFanIter it(pathMatrix, path); !it.done();) {
202*c8dee2aaSAndroid Build Coastguard Worker //       for (auto [p0, p1, p2] : it.nextStack()) {
203*c8dee2aaSAndroid Build Coastguard Worker //           vertexWriter << p0 << p1 << p2;
204*c8dee2aaSAndroid Build Coastguard Worker //       }
205*c8dee2aaSAndroid Build Coastguard Worker //   }
206*c8dee2aaSAndroid Build Coastguard Worker //
207*c8dee2aaSAndroid Build Coastguard Worker class PathMiddleOutFanIter {
208*c8dee2aaSAndroid Build Coastguard Worker public:
PathMiddleOutFanIter(const SkPath & path)209*c8dee2aaSAndroid Build Coastguard Worker     PathMiddleOutFanIter(const SkPath& path) : fMiddleOut(path.countVerbs()) {
210*c8dee2aaSAndroid Build Coastguard Worker         SkPathPriv::Iterate it(path);
211*c8dee2aaSAndroid Build Coastguard Worker         fPathIter = it.begin();
212*c8dee2aaSAndroid Build Coastguard Worker         fPathEnd = it.end();
213*c8dee2aaSAndroid Build Coastguard Worker     }
214*c8dee2aaSAndroid Build Coastguard Worker 
done()215*c8dee2aaSAndroid Build Coastguard Worker     bool done() const { return fDone; }
216*c8dee2aaSAndroid Build Coastguard Worker 
nextStack()217*c8dee2aaSAndroid Build Coastguard Worker     MiddleOutPolygonTriangulator::PoppedTriangleStack nextStack() {
218*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fDone);
219*c8dee2aaSAndroid Build Coastguard Worker         if (fPathIter == fPathEnd) {
220*c8dee2aaSAndroid Build Coastguard Worker             fDone = true;
221*c8dee2aaSAndroid Build Coastguard Worker             return fMiddleOut.close();
222*c8dee2aaSAndroid Build Coastguard Worker         }
223*c8dee2aaSAndroid Build Coastguard Worker         switch (auto [verb, pts, w] = *fPathIter++; verb) {
224*c8dee2aaSAndroid Build Coastguard Worker             SkPoint pt;
225*c8dee2aaSAndroid Build Coastguard Worker             case SkPathVerb::kMove:
226*c8dee2aaSAndroid Build Coastguard Worker                 return fMiddleOut.closeAndMove(pts[0]);
227*c8dee2aaSAndroid Build Coastguard Worker             case SkPathVerb::kLine:
228*c8dee2aaSAndroid Build Coastguard Worker             case SkPathVerb::kQuad:
229*c8dee2aaSAndroid Build Coastguard Worker             case SkPathVerb::kConic:
230*c8dee2aaSAndroid Build Coastguard Worker             case SkPathVerb::kCubic:
231*c8dee2aaSAndroid Build Coastguard Worker                 pt = pts[SkPathPriv::PtsInIter((unsigned)verb) - 1];
232*c8dee2aaSAndroid Build Coastguard Worker                 return fMiddleOut.pushVertex(pt);
233*c8dee2aaSAndroid Build Coastguard Worker             case SkPathVerb::kClose:
234*c8dee2aaSAndroid Build Coastguard Worker                 return fMiddleOut.close();
235*c8dee2aaSAndroid Build Coastguard Worker         }
236*c8dee2aaSAndroid Build Coastguard Worker         SkUNREACHABLE;
237*c8dee2aaSAndroid Build Coastguard Worker     }
238*c8dee2aaSAndroid Build Coastguard Worker 
239*c8dee2aaSAndroid Build Coastguard Worker private:
240*c8dee2aaSAndroid Build Coastguard Worker     MiddleOutPolygonTriangulator fMiddleOut;
241*c8dee2aaSAndroid Build Coastguard Worker     SkPathPriv::RangeIter fPathIter;
242*c8dee2aaSAndroid Build Coastguard Worker     SkPathPriv::RangeIter fPathEnd;
243*c8dee2aaSAndroid Build Coastguard Worker     bool fDone = false;
244*c8dee2aaSAndroid Build Coastguard Worker };
245*c8dee2aaSAndroid Build Coastguard Worker 
246*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skgpu::tess
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker #endif  // skgpu_tessellate_MiddleOutPolygonTriangulator_DEFINED
249