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