xref: /aosp_15_r20/external/skia/src/gpu/tessellate/StrokeIterator.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_StrokeIterator_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_tessellate_StrokeIterator_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStrokeRec.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathPriv.h"
18*c8dee2aaSAndroid Build Coastguard Worker 
19*c8dee2aaSAndroid Build Coastguard Worker #include <array>
20*c8dee2aaSAndroid Build Coastguard Worker 
21*c8dee2aaSAndroid Build Coastguard Worker class SkPath;
22*c8dee2aaSAndroid Build Coastguard Worker 
23*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::tess {
24*c8dee2aaSAndroid Build Coastguard Worker 
25*c8dee2aaSAndroid Build Coastguard Worker // This class iterates over the stroke geometry defined by a path and stroke. It automatically
26*c8dee2aaSAndroid Build Coastguard Worker // converts closes and square caps to lines, and round caps to circles so the user doesn't have to
27*c8dee2aaSAndroid Build Coastguard Worker // worry about it. At each location it provides a verb and "prevVerb" so there is context about the
28*c8dee2aaSAndroid Build Coastguard Worker // preceding join. Usage:
29*c8dee2aaSAndroid Build Coastguard Worker //
30*c8dee2aaSAndroid Build Coastguard Worker //     StrokeIterator iter(path, stroke);
31*c8dee2aaSAndroid Build Coastguard Worker //     while (iter.next()) {  // Call next() first.
32*c8dee2aaSAndroid Build Coastguard Worker //         iter.verb();
33*c8dee2aaSAndroid Build Coastguard Worker //         iter.pts();
34*c8dee2aaSAndroid Build Coastguard Worker //         iter.w();
35*c8dee2aaSAndroid Build Coastguard Worker //         iter.prevVerb();
36*c8dee2aaSAndroid Build Coastguard Worker //         iter.prevPts();
37*c8dee2aaSAndroid Build Coastguard Worker //     }
38*c8dee2aaSAndroid Build Coastguard Worker //
39*c8dee2aaSAndroid Build Coastguard Worker class StrokeIterator {
40*c8dee2aaSAndroid Build Coastguard Worker public:
StrokeIterator(const SkPath & path,const SkStrokeRec * stroke,const SkMatrix * viewMatrix)41*c8dee2aaSAndroid Build Coastguard Worker     StrokeIterator(const SkPath& path, const SkStrokeRec* stroke, const SkMatrix* viewMatrix)
42*c8dee2aaSAndroid Build Coastguard Worker             : fViewMatrix(viewMatrix), fStroke(stroke) {
43*c8dee2aaSAndroid Build Coastguard Worker         SkPathPriv::Iterate it(path);
44*c8dee2aaSAndroid Build Coastguard Worker         fIter = it.begin();
45*c8dee2aaSAndroid Build Coastguard Worker         fEnd = it.end();
46*c8dee2aaSAndroid Build Coastguard Worker     }
47*c8dee2aaSAndroid Build Coastguard Worker 
48*c8dee2aaSAndroid Build Coastguard Worker     enum class Verb {
49*c8dee2aaSAndroid Build Coastguard Worker         // Verbs that describe stroke geometry.
50*c8dee2aaSAndroid Build Coastguard Worker         kLine = (int)SkPathVerb::kLine,
51*c8dee2aaSAndroid Build Coastguard Worker         kQuad = (int)SkPathVerb::kQuad,
52*c8dee2aaSAndroid Build Coastguard Worker         kConic = (int)SkPathVerb::kConic,
53*c8dee2aaSAndroid Build Coastguard Worker         kCubic = (int)SkPathVerb::kCubic,
54*c8dee2aaSAndroid Build Coastguard Worker         kCircle,  // A stroke-width circle drawn as a 180-degree point stroke.
55*c8dee2aaSAndroid Build Coastguard Worker 
56*c8dee2aaSAndroid Build Coastguard Worker         // Helper verbs that notify callers to update their own iteration state.
57*c8dee2aaSAndroid Build Coastguard Worker         kMoveWithinContour,
58*c8dee2aaSAndroid Build Coastguard Worker         kContourFinished
59*c8dee2aaSAndroid Build Coastguard Worker     };
IsVerbGeometric(Verb verb)60*c8dee2aaSAndroid Build Coastguard Worker     constexpr static bool IsVerbGeometric(Verb verb) { return verb < Verb::kMoveWithinContour; }
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker     // Must be called first. Loads the next pair of "prev" and "current" stroke. Returns false if
63*c8dee2aaSAndroid Build Coastguard Worker     // iteration is complete.
next()64*c8dee2aaSAndroid Build Coastguard Worker     bool next() {
65*c8dee2aaSAndroid Build Coastguard Worker         if (fQueueCount) {
66*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fQueueCount >= 2);
67*c8dee2aaSAndroid Build Coastguard Worker             this->popFront();
68*c8dee2aaSAndroid Build Coastguard Worker             if (fQueueCount >= 2) {
69*c8dee2aaSAndroid Build Coastguard Worker                 return true;
70*c8dee2aaSAndroid Build Coastguard Worker             }
71*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fQueueCount == 1);
72*c8dee2aaSAndroid Build Coastguard Worker             if (this->atVerb(0) == Verb::kContourFinished) {
73*c8dee2aaSAndroid Build Coastguard Worker                 // Don't let "kContourFinished" be prevVerb at the start of the next contour.
74*c8dee2aaSAndroid Build Coastguard Worker                 fQueueCount = 0;
75*c8dee2aaSAndroid Build Coastguard Worker             }
76*c8dee2aaSAndroid Build Coastguard Worker         }
77*c8dee2aaSAndroid Build Coastguard Worker         for (; fIter != fEnd; ++fIter) {
78*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fQueueCount == 0 || fQueueCount == 1);
79*c8dee2aaSAndroid Build Coastguard Worker             auto [verb, pts, w] = *fIter;
80*c8dee2aaSAndroid Build Coastguard Worker             switch (verb) {
81*c8dee2aaSAndroid Build Coastguard Worker                 case SkPathVerb::kMove:
82*c8dee2aaSAndroid Build Coastguard Worker                     if (!this->finishOpenContour()) {
83*c8dee2aaSAndroid Build Coastguard Worker                         continue;
84*c8dee2aaSAndroid Build Coastguard Worker                     }
85*c8dee2aaSAndroid Build Coastguard Worker                     break;
86*c8dee2aaSAndroid Build Coastguard Worker                 case SkPathVerb::kCubic:
87*c8dee2aaSAndroid Build Coastguard Worker                     if (pts[3] == pts[2]) {
88*c8dee2aaSAndroid Build Coastguard Worker                         [[fallthrough]];  // i.e., "if (p3 == p2 && p2 == p1 && p1 == p0)"
89*c8dee2aaSAndroid Build Coastguard Worker                 case SkPathVerb::kConic:
90*c8dee2aaSAndroid Build Coastguard Worker                 case SkPathVerb::kQuad:
91*c8dee2aaSAndroid Build Coastguard Worker                     if (pts[2] == pts[1]) {
92*c8dee2aaSAndroid Build Coastguard Worker                         [[fallthrough]];  // i.e., "if (p2 == p1 && p1 == p0)"
93*c8dee2aaSAndroid Build Coastguard Worker                 case SkPathVerb::kLine:
94*c8dee2aaSAndroid Build Coastguard Worker                     if (pts[1] == pts[0]) {
95*c8dee2aaSAndroid Build Coastguard Worker                         fLastDegenerateStrokePt = pts;
96*c8dee2aaSAndroid Build Coastguard Worker                         continue;
97*c8dee2aaSAndroid Build Coastguard Worker                     }}}
98*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue((Verb)verb, pts, w);
99*c8dee2aaSAndroid Build Coastguard Worker                     if (fQueueCount == 1) {
100*c8dee2aaSAndroid Build Coastguard Worker                         // Defer the first verb until the end when we know what it's joined to.
101*c8dee2aaSAndroid Build Coastguard Worker                         fFirstVerbInContour = (Verb)verb;
102*c8dee2aaSAndroid Build Coastguard Worker                         fFirstPtsInContour = pts;
103*c8dee2aaSAndroid Build Coastguard Worker                         fFirstWInContour = w;
104*c8dee2aaSAndroid Build Coastguard Worker                         continue;
105*c8dee2aaSAndroid Build Coastguard Worker                     }
106*c8dee2aaSAndroid Build Coastguard Worker                     break;
107*c8dee2aaSAndroid Build Coastguard Worker                 case SkPathVerb::kClose:
108*c8dee2aaSAndroid Build Coastguard Worker                     if (!fQueueCount) {
109*c8dee2aaSAndroid Build Coastguard Worker                         fLastDegenerateStrokePt = pts;
110*c8dee2aaSAndroid Build Coastguard Worker                         continue;
111*c8dee2aaSAndroid Build Coastguard Worker                     }
112*c8dee2aaSAndroid Build Coastguard Worker                     if (pts[0] != fFirstPtsInContour[0]) {
113*c8dee2aaSAndroid Build Coastguard Worker                         // Draw a line back to the contour's starting point.
114*c8dee2aaSAndroid Build Coastguard Worker                         fClosePts = {pts[0], fFirstPtsInContour[0]};
115*c8dee2aaSAndroid Build Coastguard Worker                         this->enqueue(Verb::kLine, fClosePts.data(), nullptr);
116*c8dee2aaSAndroid Build Coastguard Worker                     }
117*c8dee2aaSAndroid Build Coastguard Worker                     // Repeat the first verb, this time as the "current" stroke instead of the prev.
118*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(fFirstVerbInContour, fFirstPtsInContour, fFirstWInContour);
119*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(Verb::kContourFinished, nullptr, nullptr);
120*c8dee2aaSAndroid Build Coastguard Worker                     fLastDegenerateStrokePt = nullptr;
121*c8dee2aaSAndroid Build Coastguard Worker                     break;
122*c8dee2aaSAndroid Build Coastguard Worker             }
123*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fQueueCount >= 2);
124*c8dee2aaSAndroid Build Coastguard Worker             ++fIter;
125*c8dee2aaSAndroid Build Coastguard Worker             return true;
126*c8dee2aaSAndroid Build Coastguard Worker         }
127*c8dee2aaSAndroid Build Coastguard Worker         return this->finishOpenContour();
128*c8dee2aaSAndroid Build Coastguard Worker     }
129*c8dee2aaSAndroid Build Coastguard Worker 
prevVerb()130*c8dee2aaSAndroid Build Coastguard Worker     Verb prevVerb() const { return this->atVerb(0); }
prevPts()131*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint* prevPts() const { return this->atPts(0); }
132*c8dee2aaSAndroid Build Coastguard Worker 
verb()133*c8dee2aaSAndroid Build Coastguard Worker     Verb verb() const { return this->atVerb(1); }
pts()134*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint* pts() const { return this->atPts(1); }
w()135*c8dee2aaSAndroid Build Coastguard Worker     float w() const { return this->atW(1); }
136*c8dee2aaSAndroid Build Coastguard Worker 
firstVerbInContour()137*c8dee2aaSAndroid Build Coastguard Worker     Verb firstVerbInContour() const { SkASSERT(fQueueCount > 0); return fFirstVerbInContour; }
firstPtsInContour()138*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint* firstPtsInContour() const {
139*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fQueueCount > 0);
140*c8dee2aaSAndroid Build Coastguard Worker         return fFirstPtsInContour;
141*c8dee2aaSAndroid Build Coastguard Worker     }
142*c8dee2aaSAndroid Build Coastguard Worker 
143*c8dee2aaSAndroid Build Coastguard Worker private:
144*c8dee2aaSAndroid Build Coastguard Worker     constexpr static int kQueueBufferCount = 8;
atVerb(int i)145*c8dee2aaSAndroid Build Coastguard Worker     Verb atVerb(int i) const {
146*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(0 <= i && i < fQueueCount);
147*c8dee2aaSAndroid Build Coastguard Worker         return fVerbs[(fQueueFrontIdx + i) & (kQueueBufferCount - 1)];
148*c8dee2aaSAndroid Build Coastguard Worker     }
backVerb()149*c8dee2aaSAndroid Build Coastguard Worker     Verb backVerb() const {
150*c8dee2aaSAndroid Build Coastguard Worker         return this->atVerb(fQueueCount - 1);
151*c8dee2aaSAndroid Build Coastguard Worker     }
atPts(int i)152*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint* atPts(int i) const {
153*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(0 <= i && i < fQueueCount);
154*c8dee2aaSAndroid Build Coastguard Worker         return fPts[(fQueueFrontIdx + i) & (kQueueBufferCount - 1)];
155*c8dee2aaSAndroid Build Coastguard Worker     }
backPts()156*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint* backPts() const {
157*c8dee2aaSAndroid Build Coastguard Worker         return this->atPts(fQueueCount - 1);
158*c8dee2aaSAndroid Build Coastguard Worker     }
atW(int i)159*c8dee2aaSAndroid Build Coastguard Worker     float atW(int i) const {
160*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(0 <= i && i < fQueueCount);
161*c8dee2aaSAndroid Build Coastguard Worker         const float* w = fW[(fQueueFrontIdx + i) & (kQueueBufferCount - 1)];
162*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(w);
163*c8dee2aaSAndroid Build Coastguard Worker         return *w;
164*c8dee2aaSAndroid Build Coastguard Worker     }
enqueue(Verb verb,const SkPoint * pts,const float * w)165*c8dee2aaSAndroid Build Coastguard Worker     void enqueue(Verb verb, const SkPoint* pts, const float* w) {
166*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fQueueCount < kQueueBufferCount);
167*c8dee2aaSAndroid Build Coastguard Worker         int i = (fQueueFrontIdx + fQueueCount) & (kQueueBufferCount - 1);
168*c8dee2aaSAndroid Build Coastguard Worker         fVerbs[i] = verb;
169*c8dee2aaSAndroid Build Coastguard Worker         fPts[i] = pts;
170*c8dee2aaSAndroid Build Coastguard Worker         fW[i] = w;
171*c8dee2aaSAndroid Build Coastguard Worker         ++fQueueCount;
172*c8dee2aaSAndroid Build Coastguard Worker     }
popFront()173*c8dee2aaSAndroid Build Coastguard Worker     void popFront() {
174*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fQueueCount > 0);
175*c8dee2aaSAndroid Build Coastguard Worker         ++fQueueFrontIdx;
176*c8dee2aaSAndroid Build Coastguard Worker         --fQueueCount;
177*c8dee2aaSAndroid Build Coastguard Worker     }
178*c8dee2aaSAndroid Build Coastguard Worker 
179*c8dee2aaSAndroid Build Coastguard Worker     // Finishes the current contour without closing it. Enqueues any necessary caps as well as the
180*c8dee2aaSAndroid Build Coastguard Worker     // contour's first stroke that we deferred at the beginning.
181*c8dee2aaSAndroid Build Coastguard Worker     // Returns false and makes no changes if the current contour was already finished.
finishOpenContour()182*c8dee2aaSAndroid Build Coastguard Worker     bool finishOpenContour() {
183*c8dee2aaSAndroid Build Coastguard Worker         if (fQueueCount) {
184*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(this->backVerb() == Verb::kLine || this->backVerb() == Verb::kQuad ||
185*c8dee2aaSAndroid Build Coastguard Worker                      this->backVerb() == Verb::kConic || this->backVerb() == Verb::kCubic);
186*c8dee2aaSAndroid Build Coastguard Worker             switch (fStroke->getCap()) {
187*c8dee2aaSAndroid Build Coastguard Worker                 case SkPaint::kButt_Cap:
188*c8dee2aaSAndroid Build Coastguard Worker                     // There are no caps, but inject a "move" so the first stroke doesn't get joined
189*c8dee2aaSAndroid Build Coastguard Worker                     // with the end of the contour when it's processed.
190*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(Verb::kMoveWithinContour, fFirstPtsInContour, fFirstWInContour);
191*c8dee2aaSAndroid Build Coastguard Worker                     break;
192*c8dee2aaSAndroid Build Coastguard Worker                 case SkPaint::kRound_Cap: {
193*c8dee2aaSAndroid Build Coastguard Worker                     // The "kCircle" verb serves as our barrier to prevent the first stroke from
194*c8dee2aaSAndroid Build Coastguard Worker                     // getting joined with the end of the contour. We just need to make sure that
195*c8dee2aaSAndroid Build Coastguard Worker                     // the first point of the contour goes last.
196*c8dee2aaSAndroid Build Coastguard Worker                     int backIdx = SkPathPriv::PtsInIter((unsigned)this->backVerb()) - 1;
197*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(Verb::kCircle, this->backPts() + backIdx, nullptr);
198*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(Verb::kCircle, fFirstPtsInContour, fFirstWInContour);
199*c8dee2aaSAndroid Build Coastguard Worker                     break;
200*c8dee2aaSAndroid Build Coastguard Worker                 }
201*c8dee2aaSAndroid Build Coastguard Worker                 case SkPaint::kSquare_Cap:
202*c8dee2aaSAndroid Build Coastguard Worker                     this->fillSquareCapPoints();  // Fills in fEndingCapPts and fBeginningCapPts.
203*c8dee2aaSAndroid Build Coastguard Worker                     // Append the ending cap onto the current contour.
204*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(Verb::kLine, fEndingCapPts.data(), nullptr);
205*c8dee2aaSAndroid Build Coastguard Worker                     // Move to the beginning cap and append it right before (and joined to) the
206*c8dee2aaSAndroid Build Coastguard Worker                     // first stroke (that we will add below).
207*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(Verb::kMoveWithinContour, fBeginningCapPts.data(), nullptr);
208*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(Verb::kLine, fBeginningCapPts.data(), nullptr);
209*c8dee2aaSAndroid Build Coastguard Worker                     break;
210*c8dee2aaSAndroid Build Coastguard Worker             }
211*c8dee2aaSAndroid Build Coastguard Worker         } else if (fLastDegenerateStrokePt) {
212*c8dee2aaSAndroid Build Coastguard Worker             // fQueueCount=0 means this subpath is zero length. Generates caps on its location.
213*c8dee2aaSAndroid Build Coastguard Worker             //
214*c8dee2aaSAndroid Build Coastguard Worker             //   "Any zero length subpath ...  shall be stroked if the 'stroke-linecap' property has
215*c8dee2aaSAndroid Build Coastguard Worker             //   a value of round or square producing respectively a circle or a square."
216*c8dee2aaSAndroid Build Coastguard Worker             //
217*c8dee2aaSAndroid Build Coastguard Worker             //   (https://www.w3.org/TR/SVG11/painting.html#StrokeProperties)
218*c8dee2aaSAndroid Build Coastguard Worker             //
219*c8dee2aaSAndroid Build Coastguard Worker             switch (fStroke->getCap()) {
220*c8dee2aaSAndroid Build Coastguard Worker                 case SkPaint::kButt_Cap:
221*c8dee2aaSAndroid Build Coastguard Worker                     // Zero-length contour with butt caps. There are no caps and no first stroke to
222*c8dee2aaSAndroid Build Coastguard Worker                     // generate.
223*c8dee2aaSAndroid Build Coastguard Worker                     return false;
224*c8dee2aaSAndroid Build Coastguard Worker                 case SkPaint::kRound_Cap:
225*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(Verb::kCircle, fLastDegenerateStrokePt, nullptr);
226*c8dee2aaSAndroid Build Coastguard Worker                     // Setting the "first" stroke as the circle causes it to be added again below,
227*c8dee2aaSAndroid Build Coastguard Worker                     // this time as the "current" stroke.
228*c8dee2aaSAndroid Build Coastguard Worker                     fFirstVerbInContour = Verb::kCircle;
229*c8dee2aaSAndroid Build Coastguard Worker                     fFirstPtsInContour = fLastDegenerateStrokePt;
230*c8dee2aaSAndroid Build Coastguard Worker                     fFirstWInContour = nullptr;
231*c8dee2aaSAndroid Build Coastguard Worker                     break;
232*c8dee2aaSAndroid Build Coastguard Worker                 case SkPaint::kSquare_Cap: {
233*c8dee2aaSAndroid Build Coastguard Worker                     SkPoint outset;
234*c8dee2aaSAndroid Build Coastguard Worker                     if (!fStroke->isHairlineStyle()) {
235*c8dee2aaSAndroid Build Coastguard Worker                         // Implement degenerate square caps as a stroke-width square in path space.
236*c8dee2aaSAndroid Build Coastguard Worker                         outset = {fStroke->getWidth() * .5f, 0};
237*c8dee2aaSAndroid Build Coastguard Worker                     } else {
238*c8dee2aaSAndroid Build Coastguard Worker                         // If the stroke is hairline, draw a 1x1 device-space square instead. This
239*c8dee2aaSAndroid Build Coastguard Worker                         // is equivalent to using:
240*c8dee2aaSAndroid Build Coastguard Worker                         //
241*c8dee2aaSAndroid Build Coastguard Worker                         //   outset = inverse(fViewMatrix).mapVector(.5, 0)
242*c8dee2aaSAndroid Build Coastguard Worker                         //
243*c8dee2aaSAndroid Build Coastguard Worker                         // And since the matrix cannot have perspective, we only need to invert the
244*c8dee2aaSAndroid Build Coastguard Worker                         // upper 2x2 of the viewMatrix to achieve this.
245*c8dee2aaSAndroid Build Coastguard Worker                         SkASSERT(!fViewMatrix->hasPerspective());
246*c8dee2aaSAndroid Build Coastguard Worker                         float a=fViewMatrix->getScaleX(), b=fViewMatrix->getSkewX(),
247*c8dee2aaSAndroid Build Coastguard Worker                               c=fViewMatrix->getSkewY(),  d=fViewMatrix->getScaleY();
248*c8dee2aaSAndroid Build Coastguard Worker                         float det = a*d - b*c;
249*c8dee2aaSAndroid Build Coastguard Worker                         if (det > 0) {
250*c8dee2aaSAndroid Build Coastguard Worker                             // outset = inverse(|a b|) * |.5|
251*c8dee2aaSAndroid Build Coastguard Worker                             //                  |c d|    | 0|
252*c8dee2aaSAndroid Build Coastguard Worker                             //
253*c8dee2aaSAndroid Build Coastguard Worker                             //     == 1/det * | d -b| * |.5|
254*c8dee2aaSAndroid Build Coastguard Worker                             //                |-c  a|   | 0|
255*c8dee2aaSAndroid Build Coastguard Worker                             //
256*c8dee2aaSAndroid Build Coastguard Worker                             //     == | d| * .5/det
257*c8dee2aaSAndroid Build Coastguard Worker                             //        |-c|
258*c8dee2aaSAndroid Build Coastguard Worker                             outset = SkVector{d, -c} * (.5f / det);
259*c8dee2aaSAndroid Build Coastguard Worker                         } else {
260*c8dee2aaSAndroid Build Coastguard Worker                             outset = {1, 0};
261*c8dee2aaSAndroid Build Coastguard Worker                         }
262*c8dee2aaSAndroid Build Coastguard Worker                     }
263*c8dee2aaSAndroid Build Coastguard Worker                     fEndingCapPts = {*fLastDegenerateStrokePt - outset,
264*c8dee2aaSAndroid Build Coastguard Worker                                      *fLastDegenerateStrokePt + outset};
265*c8dee2aaSAndroid Build Coastguard Worker                     // Add the square first as the "prev" join.
266*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(Verb::kLine, fEndingCapPts.data(), nullptr);
267*c8dee2aaSAndroid Build Coastguard Worker                     this->enqueue(Verb::kMoveWithinContour, fEndingCapPts.data(), nullptr);
268*c8dee2aaSAndroid Build Coastguard Worker                     // Setting the "first" stroke as the square causes it to be added again below,
269*c8dee2aaSAndroid Build Coastguard Worker                     // this time as the "current" stroke.
270*c8dee2aaSAndroid Build Coastguard Worker                     fFirstVerbInContour = Verb::kLine;
271*c8dee2aaSAndroid Build Coastguard Worker                     fFirstPtsInContour = fEndingCapPts.data();
272*c8dee2aaSAndroid Build Coastguard Worker                     fFirstWInContour = nullptr;
273*c8dee2aaSAndroid Build Coastguard Worker                     break;
274*c8dee2aaSAndroid Build Coastguard Worker                 }
275*c8dee2aaSAndroid Build Coastguard Worker             }
276*c8dee2aaSAndroid Build Coastguard Worker         } else {
277*c8dee2aaSAndroid Build Coastguard Worker             // This contour had no lines, beziers, or "close" verbs. There are no caps and no first
278*c8dee2aaSAndroid Build Coastguard Worker             // stroke to generate.
279*c8dee2aaSAndroid Build Coastguard Worker             return false;
280*c8dee2aaSAndroid Build Coastguard Worker         }
281*c8dee2aaSAndroid Build Coastguard Worker 
282*c8dee2aaSAndroid Build Coastguard Worker         // Repeat the first verb, this time as the "current" stroke instead of the prev.
283*c8dee2aaSAndroid Build Coastguard Worker         this->enqueue(fFirstVerbInContour, fFirstPtsInContour, fFirstWInContour);
284*c8dee2aaSAndroid Build Coastguard Worker         this->enqueue(Verb::kContourFinished, nullptr, nullptr);
285*c8dee2aaSAndroid Build Coastguard Worker         fLastDegenerateStrokePt = nullptr;
286*c8dee2aaSAndroid Build Coastguard Worker         return true;
287*c8dee2aaSAndroid Build Coastguard Worker     }
288*c8dee2aaSAndroid Build Coastguard Worker 
289*c8dee2aaSAndroid Build Coastguard Worker     // We implement square caps as two extra "kLine" verbs. This method finds the endpoints for
290*c8dee2aaSAndroid Build Coastguard Worker     // those lines.
fillSquareCapPoints()291*c8dee2aaSAndroid Build Coastguard Worker     void fillSquareCapPoints() {
292*c8dee2aaSAndroid Build Coastguard Worker         // Find the endpoints of the cap at the end of the contour.
293*c8dee2aaSAndroid Build Coastguard Worker         SkVector lastTangent;
294*c8dee2aaSAndroid Build Coastguard Worker         const SkPoint* lastPts = this->backPts();
295*c8dee2aaSAndroid Build Coastguard Worker         Verb lastVerb = this->backVerb();
296*c8dee2aaSAndroid Build Coastguard Worker         switch (lastVerb) {
297*c8dee2aaSAndroid Build Coastguard Worker             case Verb::kCubic:
298*c8dee2aaSAndroid Build Coastguard Worker                 lastTangent = lastPts[3] - lastPts[2];
299*c8dee2aaSAndroid Build Coastguard Worker                 if (!lastTangent.isZero()) {
300*c8dee2aaSAndroid Build Coastguard Worker                     break;
301*c8dee2aaSAndroid Build Coastguard Worker                 }
302*c8dee2aaSAndroid Build Coastguard Worker                 [[fallthrough]];
303*c8dee2aaSAndroid Build Coastguard Worker             case Verb::kConic:
304*c8dee2aaSAndroid Build Coastguard Worker             case Verb::kQuad:
305*c8dee2aaSAndroid Build Coastguard Worker                 lastTangent = lastPts[2] - lastPts[1];
306*c8dee2aaSAndroid Build Coastguard Worker                 if (!lastTangent.isZero()) {
307*c8dee2aaSAndroid Build Coastguard Worker                     break;
308*c8dee2aaSAndroid Build Coastguard Worker                 }
309*c8dee2aaSAndroid Build Coastguard Worker                 [[fallthrough]];
310*c8dee2aaSAndroid Build Coastguard Worker             case Verb::kLine:
311*c8dee2aaSAndroid Build Coastguard Worker                 lastTangent = lastPts[1] - lastPts[0];
312*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!lastTangent.isZero());
313*c8dee2aaSAndroid Build Coastguard Worker                 break;
314*c8dee2aaSAndroid Build Coastguard Worker             default:
315*c8dee2aaSAndroid Build Coastguard Worker                 SkUNREACHABLE;
316*c8dee2aaSAndroid Build Coastguard Worker         }
317*c8dee2aaSAndroid Build Coastguard Worker         if (!fStroke->isHairlineStyle()) {
318*c8dee2aaSAndroid Build Coastguard Worker             // Extend the cap by 1/2 stroke width.
319*c8dee2aaSAndroid Build Coastguard Worker             lastTangent *= (.5f * fStroke->getWidth()) / lastTangent.length();
320*c8dee2aaSAndroid Build Coastguard Worker         } else {
321*c8dee2aaSAndroid Build Coastguard Worker             // Extend the cap by what will be 1/2 pixel after transformation.
322*c8dee2aaSAndroid Build Coastguard Worker             lastTangent *= .5f / fViewMatrix->mapVector(lastTangent.fX, lastTangent.fY).length();
323*c8dee2aaSAndroid Build Coastguard Worker         }
324*c8dee2aaSAndroid Build Coastguard Worker         SkPoint lastPoint = lastPts[SkPathPriv::PtsInIter((unsigned)lastVerb) - 1];
325*c8dee2aaSAndroid Build Coastguard Worker         fEndingCapPts = {lastPoint, lastPoint + lastTangent};
326*c8dee2aaSAndroid Build Coastguard Worker 
327*c8dee2aaSAndroid Build Coastguard Worker         // Find the endpoints of the cap at the beginning of the contour.
328*c8dee2aaSAndroid Build Coastguard Worker         SkVector firstTangent = fFirstPtsInContour[1] - fFirstPtsInContour[0];
329*c8dee2aaSAndroid Build Coastguard Worker         if (firstTangent.isZero()) {
330*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fFirstVerbInContour == Verb::kQuad || fFirstVerbInContour == Verb::kConic ||
331*c8dee2aaSAndroid Build Coastguard Worker                      fFirstVerbInContour == Verb::kCubic);
332*c8dee2aaSAndroid Build Coastguard Worker             firstTangent = fFirstPtsInContour[2] - fFirstPtsInContour[0];
333*c8dee2aaSAndroid Build Coastguard Worker             if (firstTangent.isZero()) {
334*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(fFirstVerbInContour == Verb::kCubic);
335*c8dee2aaSAndroid Build Coastguard Worker                 firstTangent = fFirstPtsInContour[3] - fFirstPtsInContour[0];
336*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!firstTangent.isZero());
337*c8dee2aaSAndroid Build Coastguard Worker             }
338*c8dee2aaSAndroid Build Coastguard Worker         }
339*c8dee2aaSAndroid Build Coastguard Worker         if (!fStroke->isHairlineStyle()) {
340*c8dee2aaSAndroid Build Coastguard Worker             // Set the the cap back by 1/2 stroke width.
341*c8dee2aaSAndroid Build Coastguard Worker             firstTangent *= (-.5f * fStroke->getWidth()) / firstTangent.length();
342*c8dee2aaSAndroid Build Coastguard Worker         } else {
343*c8dee2aaSAndroid Build Coastguard Worker             // Set the cap back by what will be 1/2 pixel after transformation.
344*c8dee2aaSAndroid Build Coastguard Worker             firstTangent *=
345*c8dee2aaSAndroid Build Coastguard Worker                     -.5f / fViewMatrix->mapVector(firstTangent.fX, firstTangent.fY).length();
346*c8dee2aaSAndroid Build Coastguard Worker         }
347*c8dee2aaSAndroid Build Coastguard Worker         fBeginningCapPts = {fFirstPtsInContour[0] + firstTangent, fFirstPtsInContour[0]};
348*c8dee2aaSAndroid Build Coastguard Worker     }
349*c8dee2aaSAndroid Build Coastguard Worker 
350*c8dee2aaSAndroid Build Coastguard Worker     // Info and iterators from the original path.
351*c8dee2aaSAndroid Build Coastguard Worker     const SkMatrix* const fViewMatrix;  // For hairlines.
352*c8dee2aaSAndroid Build Coastguard Worker     const SkStrokeRec* const fStroke;
353*c8dee2aaSAndroid Build Coastguard Worker     SkPathPriv::RangeIter fIter;
354*c8dee2aaSAndroid Build Coastguard Worker     SkPathPriv::RangeIter fEnd;
355*c8dee2aaSAndroid Build Coastguard Worker 
356*c8dee2aaSAndroid Build Coastguard Worker     // Info for the current contour we are iterating.
357*c8dee2aaSAndroid Build Coastguard Worker     Verb fFirstVerbInContour;
358*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint* fFirstPtsInContour;
359*c8dee2aaSAndroid Build Coastguard Worker     const float* fFirstWInContour;
360*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint* fLastDegenerateStrokePt = nullptr;
361*c8dee2aaSAndroid Build Coastguard Worker 
362*c8dee2aaSAndroid Build Coastguard Worker     // The queue is implemented as a roll-over array with a floating front index.
363*c8dee2aaSAndroid Build Coastguard Worker     Verb fVerbs[kQueueBufferCount];
364*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint* fPts[kQueueBufferCount];
365*c8dee2aaSAndroid Build Coastguard Worker     const float* fW[kQueueBufferCount];
366*c8dee2aaSAndroid Build Coastguard Worker     int fQueueFrontIdx = 0;
367*c8dee2aaSAndroid Build Coastguard Worker     int fQueueCount = 0;
368*c8dee2aaSAndroid Build Coastguard Worker 
369*c8dee2aaSAndroid Build Coastguard Worker     // Storage space for geometry that gets defined implicitly by the path, but does not have
370*c8dee2aaSAndroid Build Coastguard Worker     // actual points in memory to reference.
371*c8dee2aaSAndroid Build Coastguard Worker     std::array<SkPoint, 2> fClosePts;
372*c8dee2aaSAndroid Build Coastguard Worker     std::array<SkPoint, 2> fEndingCapPts;
373*c8dee2aaSAndroid Build Coastguard Worker     std::array<SkPoint, 2> fBeginningCapPts;
374*c8dee2aaSAndroid Build Coastguard Worker };
375*c8dee2aaSAndroid Build Coastguard Worker 
376*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skgpu::tess
377*c8dee2aaSAndroid Build Coastguard Worker 
378*c8dee2aaSAndroid Build Coastguard Worker #endif  // skgpu_tessellate_StrokeIterator_DEFINED
379