blob: 2fc1806dc70f844e2433634dda464535d4216af7 [file] [log] [blame]
Chris Daltonf5132a02020-04-27 23:40:03 -06001/*
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 GrMiddleOutPolygonTriangulator_DEFINED
9#define GrMiddleOutPolygonTriangulator_DEFINED
10
Chris Daltond7177432021-01-15 13:12:50 -070011#include "include/core/SkPath.h"
Chris Daltonf5132a02020-04-27 23:40:03 -060012#include "include/core/SkPoint.h"
13#include "include/private/SkTemplates.h"
14#include "src/core/SkMathPriv.h"
Chris Daltond7177432021-01-15 13:12:50 -070015#include "src/core/SkPathPriv.h"
Chris Dalton8731a712021-05-14 14:48:54 -060016#include "src/gpu/GrVertexWriter.h"
Chris Dalton69669812021-07-27 10:00:12 -060017#include "src/gpu/tessellate/GrPathXform.h"
Chris Daltonf5132a02020-04-27 23:40:03 -060018
19// This class emits a polygon triangulation with a "middle-out" topology. Conceptually, middle-out
20// emits one large triangle with vertices on both endpoints and a middle point, then recurses on
21// both sides of the new triangle. i.e.:
22//
23// void emit_middle_out_triangulation(int startIdx, int endIdx) {
24// if (startIdx + 1 == endIdx) {
25// return;
26// }
27// int middleIdx = startIdx + SkNextPow2(endIdx - startIdx) / 2;
28//
29// // Recurse on the left half.
30// emit_middle_out_triangulation(startIdx, middleIdx);
31//
32// // Emit a large triangle with vertices on both endpoints and a middle point.
33// emit_triangle(vertices[startIdx], vertices[middleIdx], vertices[endIdx - 1]);
34//
35// // Recurse on the right half.
36// emit_middle_out_triangulation(middleIdx, endIdx);
37// }
38//
39// Middle-out produces drastically less work for the rasterizer as compared a linear triangle strip
40// or fan.
41//
42// This class is designed to not know or store all the vertices in the polygon at once. The caller
43// pushes each vertex in linear order (perhaps while parsing a path), then rather than relying on
44// recursion, we manipulate an O(log N) stack to determine the correct middle-out triangulation.
45class GrMiddleOutPolygonTriangulator {
46public:
Chris Dalton50516f32021-07-18 22:26:27 -060047 // Writes out 3 SkPoints per triangle to "vertexWriter". Additionally writes out "pad32Count"
48 // repetitions of "pad32Value" after each triangle. Set pad32Count to 0 if the triangles are
49 // to be tightly packed.
Chris Dalton40c906f2021-07-26 11:27:05 -060050 GrMiddleOutPolygonTriangulator(GrVertexWriter&& vertexWriter, int pad32Count,
Chris Dalton50516f32021-07-18 22:26:27 -060051 uint32_t pad32Value, int maxPushVertexCalls)
Chris Dalton40c906f2021-07-26 11:27:05 -060052 : fVertexWriter(std::move(vertexWriter))
53 , fPad32Count(pad32Count)
54 , fPad32Value(pad32Value) {
Chris Daltonf5132a02020-04-27 23:40:03 -060055 // Determine the deepest our stack can ever go.
Chris Daltona36a5a12020-05-14 17:55:52 -060056 int maxStackDepth = SkNextLog2(maxPushVertexCalls) + 1;
Chris Daltonf5132a02020-04-27 23:40:03 -060057 if (maxStackDepth > kStackPreallocCount) {
58 fVertexStack.reset(maxStackDepth);
59 }
60 SkDEBUGCODE(fStackAllocCount = maxStackDepth;)
61 // The stack will always contain a starting point. This is an implicit moveTo(0, 0)
62 // initially, but will be overridden if moveTo() gets called before adding geometry.
63 fVertexStack[0] = {0, {0, 0}};
64 fTop = fVertexStack;
65 }
66
67 void pushVertex(const SkPoint& pt) {
68 if (pt == fVertexStack[0].fPoint) {
69 this->close();
70 return;
71 }
72 // This new vertex we are about to add is one vertex away from the top of the stack.
73 // i.e., it is guaranteed to be the next vertex in the polygon after the one stored in fTop.
74 int vertexIdxDelta = 1;
75 // Our topology wants triangles that have the same vertexIdxDelta on both sides:
76 // e.g., a run of 9 points should be triangulated as:
77 //
78 // [0, 1, 2], [2, 3, 4], [4, 5, 6], [6, 7, 8] // vertexIdxDelta == 1
79 // [0, 2, 4], [4, 6, 8] // vertexIdxDelta == 2
80 // [0, 4, 8] // vertexIdxDelta == 4
81 //
82 // Emit as many new triangles as we can with equal-delta sides and pop their vertices off
83 // the stack before pushing this new vertex.
84 //
85 // (This is a stack-based implementation of the recursive example method from the class
86 // comment.)
87 while (vertexIdxDelta == fTop->fVertexIdxDelta) {
Chris Daltona36a5a12020-05-14 17:55:52 -060088 this->popTopTriangle(pt);
Chris Daltonf5132a02020-04-27 23:40:03 -060089 vertexIdxDelta *= 2;
Chris Daltonf5132a02020-04-27 23:40:03 -060090 }
Chris Daltona36a5a12020-05-14 17:55:52 -060091 this->pushVertex(vertexIdxDelta, pt);
Chris Daltonf5132a02020-04-27 23:40:03 -060092 }
93
94 int close() {
Chris Daltona36a5a12020-05-14 17:55:52 -060095 if (fTop == fVertexStack) { // The stack only contains one point (the starting point).
96 return fTotalClosedTriangleCount;
Chris Daltonf5132a02020-04-27 23:40:03 -060097 }
98 // We will count vertices by walking the stack backwards.
99 int finalVertexCount = 1;
100 // Add an implicit line back to the starting point, then triangulate the rest of the
101 // polygon. Since we simply have to finish now, we aren't picky anymore about making the
102 // vertexIdxDeltas match.
103 const SkPoint& p0 = fVertexStack[0].fPoint;
104 SkASSERT(fTop->fPoint != p0); // We should have detected and handled this case earlier.
Chris Daltona36a5a12020-05-14 17:55:52 -0600105 while (fTop - 1 > fVertexStack) {
Chris Daltonf5132a02020-04-27 23:40:03 -0600106 finalVertexCount += fTop->fVertexIdxDelta;
Chris Daltona36a5a12020-05-14 17:55:52 -0600107 this->popTopTriangle(p0);
Chris Daltonf5132a02020-04-27 23:40:03 -0600108 }
Chris Daltona36a5a12020-05-14 17:55:52 -0600109 SkASSERT(fTop == fVertexStack + 1);
Chris Daltonf5132a02020-04-27 23:40:03 -0600110 finalVertexCount += fTop->fVertexIdxDelta;
Chris Daltonf5132a02020-04-27 23:40:03 -0600111 SkASSERT(fVertexStack[0].fVertexIdxDelta == 0);
Chris Daltona36a5a12020-05-14 17:55:52 -0600112 fTop = fVertexStack;
Chris Daltonf5132a02020-04-27 23:40:03 -0600113 int numTriangles = finalVertexCount - 2;
114 SkASSERT(numTriangles >= 0);
Chris Daltona36a5a12020-05-14 17:55:52 -0600115 fTotalClosedTriangleCount += numTriangles;
116 return fTotalClosedTriangleCount;
Chris Daltonf5132a02020-04-27 23:40:03 -0600117 }
118
119 void closeAndMove(const SkPoint& startPt) {
120 this->close();
Chris Daltona36a5a12020-05-14 17:55:52 -0600121 SkASSERT(fTop == fVertexStack); // The stack should only contain a starting point now.
Chris Daltonf5132a02020-04-27 23:40:03 -0600122 fTop->fPoint = startPt; // Modify the starting point.
123 SkASSERT(fTop->fVertexIdxDelta == 0); // Ensure we are in the initial stack state.
124 }
125
Chris Dalton40c906f2021-07-26 11:27:05 -0600126 GrVertexWriter detachVertexWriter() { return std::move(fVertexWriter); }
127
128 static GrVertexWriter WritePathInnerFan(GrVertexWriter&& vertexWriter,
129 int pad32Count,
130 uint32_t pad32Value,
Chris Dalton69669812021-07-27 10:00:12 -0600131 const GrPathXform& pathXform,
Chris Dalton40c906f2021-07-26 11:27:05 -0600132 const SkPath& path,
133 int* numTrianglesWritten) {
Chris Dalton69669812021-07-27 10:00:12 -0600134 GrMiddleOutPolygonTriangulator middleOut(std::move(vertexWriter),
135 pad32Count,
136 pad32Value,
Chris Dalton50516f32021-07-18 22:26:27 -0600137 path.countVerbs());
Chris Daltond7177432021-01-15 13:12:50 -0700138 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
139 switch (verb) {
Chris Dalton69669812021-07-27 10:00:12 -0600140 SkPoint pt;
Chris Daltond7177432021-01-15 13:12:50 -0700141 case SkPathVerb::kMove:
Chris Dalton69669812021-07-27 10:00:12 -0600142 middleOut.closeAndMove(pathXform.mapPoint(pts[0]));
Chris Daltond7177432021-01-15 13:12:50 -0700143 break;
144 case SkPathVerb::kLine:
145 case SkPathVerb::kQuad:
146 case SkPathVerb::kConic:
147 case SkPathVerb::kCubic:
Chris Dalton69669812021-07-27 10:00:12 -0600148 pt = pts[SkPathPriv::PtsInIter((unsigned)verb) - 1];
149 middleOut.pushVertex(pathXform.mapPoint(pt));
Chris Daltond7177432021-01-15 13:12:50 -0700150 break;
151 case SkPathVerb::kClose:
152 break;
153 }
154 }
Chris Dalton40c906f2021-07-26 11:27:05 -0600155 *numTrianglesWritten = middleOut.close();
156 return middleOut.detachVertexWriter();
Chris Daltond7177432021-01-15 13:12:50 -0700157 }
158
Chris Daltonf5132a02020-04-27 23:40:03 -0600159private:
160 struct StackVertex {
161 // How many polygon vertices away is this vertex from the previous vertex on the stack?
162 // i.e., the ith stack element's vertex index in the original polygon is:
163 //
164 // fVertexStack[i].fVertexIdxDelta + fVertexStack[i - 1].fVertexIdxDelta + ... +
165 // fVertexStack[1].fVertexIdxDelta.
166 //
167 // NOTE: fVertexStack[0].fVertexIdxDelta always == 0.
168 int fVertexIdxDelta;
169 SkPoint fPoint;
170 };
171
Chris Daltona36a5a12020-05-14 17:55:52 -0600172 void pushVertex(int vertexIdxDelta, const SkPoint& point) {
Chris Daltonf5132a02020-04-27 23:40:03 -0600173 ++fTop;
174 // We should never push deeper than fStackAllocCount.
Chris Daltona36a5a12020-05-14 17:55:52 -0600175 SkASSERT(fTop < fVertexStack + fStackAllocCount);
176 fTop->fVertexIdxDelta = vertexIdxDelta;
177 fTop->fPoint = point;
Chris Daltonf5132a02020-04-27 23:40:03 -0600178 }
179
Chris Daltona36a5a12020-05-14 17:55:52 -0600180 void popTopTriangle(const SkPoint& lastPt) {
181 SkASSERT(fTop > fVertexStack); // We should never pop the starting point.
Chris Daltonf5132a02020-04-27 23:40:03 -0600182 --fTop;
Chris Dalton40c906f2021-07-26 11:27:05 -0600183 fVertexWriter.write(fTop[0].fPoint, fTop[1].fPoint, lastPt);
Chris Dalton50516f32021-07-18 22:26:27 -0600184 if (fPad32Count) {
Chris Daltondf2dbad2021-05-14 16:21:15 -0600185 // Output a 4-point conic with w=Inf.
Chris Dalton40c906f2021-07-26 11:27:05 -0600186 fVertexWriter.fill(fPad32Value, fPad32Count);
Chris Dalton8731a712021-05-14 14:48:54 -0600187 }
Chris Daltonf5132a02020-04-27 23:40:03 -0600188 }
189
190 constexpr static int kStackPreallocCount = 32;
Chris Dalton40c906f2021-07-26 11:27:05 -0600191 GrVertexWriter fVertexWriter;
Chris Dalton50516f32021-07-18 22:26:27 -0600192 const int fPad32Count;
193 const uint32_t fPad32Value;
Chris Daltonf5132a02020-04-27 23:40:03 -0600194 SkAutoSTMalloc<kStackPreallocCount, StackVertex> fVertexStack;
195 SkDEBUGCODE(int fStackAllocCount;)
196 StackVertex* fTop;
Chris Daltona36a5a12020-05-14 17:55:52 -0600197 int fTotalClosedTriangleCount = 0;
Chris Daltonf5132a02020-04-27 23:40:03 -0600198};
199
200#endif