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