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