• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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