Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright 2017 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 #include "SkShadowTessellator.h"
      9 #include "SkColorData.h"
     10 #include "SkDrawShadowInfo.h"
     11 #include "SkGeometry.h"
     12 #include "SkInsetConvexPolygon.h"
     13 #include "SkPath.h"
     14 #include "SkPoint3.h"
     15 #include "SkPointPriv.h"
     16 #include "SkVertices.h"
     17 
     18 #if SK_SUPPORT_GPU
     19 #include "GrPathUtils.h"
     20 #endif
     21 
     22 
     23 /**
     24  * Base class
     25  */
     26 class SkBaseShadowTessellator {
     27 public:
     28     SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent);
     29     virtual ~SkBaseShadowTessellator() {}
     30 
     31     sk_sp<SkVertices> releaseVertices() {
     32         if (!fSucceeded) {
     33             return nullptr;
     34         }
     35         return SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, this->vertexCount(),
     36                                     fPositions.begin(), nullptr, fColors.begin(),
     37                                     this->indexCount(), fIndices.begin());
     38     }
     39 
     40 protected:
     41     static constexpr auto kMinHeight = 0.1f;
     42 
     43     int vertexCount() const { return fPositions.count(); }
     44     int indexCount() const { return fIndices.count(); }
     45 
     46     bool setZOffset(const SkRect& bounds, bool perspective);
     47 
     48     virtual void handleLine(const SkPoint& p) = 0;
     49     void handleLine(const SkMatrix& m, SkPoint* p);
     50 
     51     void handleQuad(const SkPoint pts[3]);
     52     void handleQuad(const SkMatrix& m, SkPoint pts[3]);
     53 
     54     void handleCubic(const SkMatrix& m, SkPoint pts[4]);
     55 
     56     void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
     57 
     58     bool setTransformedHeightFunc(const SkMatrix& ctm);
     59 
     60     bool addArc(const SkVector& nextNormal, bool finishArc);
     61 
     62     SkScalar heightFunc(SkScalar x, SkScalar y) {
     63         return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
     64     }
     65 
     66     SkPoint3                                fZPlaneParams;
     67     std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
     68     SkScalar                                fZOffset;
     69     // members for perspective height function
     70     SkPoint3                                fTransformedZParams;
     71     SkScalar                                fPartialDeterminants[3];
     72 
     73     // first two points
     74     SkTDArray<SkPoint>  fInitPoints;
     75     // temporary buffer
     76     SkTDArray<SkPoint>  fPointBuffer;
     77 
     78     SkTDArray<SkPoint>  fPositions;
     79     SkTDArray<SkColor>  fColors;
     80     SkTDArray<uint16_t> fIndices;
     81 
     82     int                 fFirstVertexIndex;
     83     SkVector            fFirstOutset;
     84     SkPoint             fFirstPoint;
     85 
     86     bool                fSucceeded;
     87     bool                fTransparent;
     88 
     89     SkColor             fUmbraColor;
     90     SkColor             fPenumbraColor;
     91 
     92     SkScalar            fRadius;
     93     SkScalar            fDirection;
     94     int                 fPrevUmbraIndex;
     95     SkVector            fPrevOutset;
     96     SkPoint             fPrevPoint;
     97 };
     98 
     99 static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
    100                            SkVector* newNormal) {
    101     SkVector normal;
    102     // compute perpendicular
    103     normal.fX = p0.fY - p1.fY;
    104     normal.fY = p1.fX - p0.fX;
    105     normal *= dir;
    106     if (!normal.normalize()) {
    107         return false;
    108     }
    109     *newNormal = normal;
    110     return true;
    111 }
    112 
    113 static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
    114                                  SkScalar* rotSin, SkScalar* rotCos, int* n) {
    115     const SkScalar kRecipPixelsPerArcSegment = 0.125f;
    116 
    117     SkScalar rCos = v1.dot(v2);
    118     SkScalar rSin = v1.cross(v2);
    119     SkScalar theta = SkScalarATan2(rSin, rCos);
    120 
    121     int steps = SkScalarFloorToInt(r*theta*kRecipPixelsPerArcSegment);
    122 
    123     SkScalar dTheta = theta / steps;
    124     *rotSin = SkScalarSinCos(dTheta, rotCos);
    125     *n = steps;
    126 }
    127 
    128 SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
    129         : fZPlaneParams(zPlaneParams)
    130         , fZOffset(0)
    131         , fFirstVertexIndex(-1)
    132         , fSucceeded(false)
    133         , fTransparent(transparent)
    134         , fDirection(1)
    135         , fPrevUmbraIndex(-1) {
    136     fInitPoints.setReserve(3);
    137 
    138     // child classes will set reserve for positions, colors and indices
    139 }
    140 
    141 bool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) {
    142     SkScalar minZ = this->heightFunc(bounds.fLeft, bounds.fTop);
    143     if (perspective) {
    144         SkScalar z = this->heightFunc(bounds.fLeft, bounds.fBottom);
    145         if (z < minZ) {
    146             minZ = z;
    147         }
    148         z = this->heightFunc(bounds.fRight, bounds.fTop);
    149         if (z < minZ) {
    150             minZ = z;
    151         }
    152         z = this->heightFunc(bounds.fRight, bounds.fBottom);
    153         if (z < minZ) {
    154             minZ = z;
    155         }
    156     }
    157 
    158     if (minZ < kMinHeight) {
    159         fZOffset = -minZ + kMinHeight;
    160         return true;
    161     }
    162 
    163     return false;
    164 }
    165 
    166 // tesselation tolerance values, in device space pixels
    167 #if SK_SUPPORT_GPU
    168 static const SkScalar kQuadTolerance = 0.2f;
    169 static const SkScalar kCubicTolerance = 0.2f;
    170 #endif
    171 static const SkScalar kConicTolerance = 0.5f;
    172 
    173 void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
    174     m.mapPoints(p, 1);
    175     this->handleLine(*p);
    176 }
    177 
    178 void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
    179 #if SK_SUPPORT_GPU
    180     // TODO: Pull PathUtils out of Ganesh?
    181     int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
    182     fPointBuffer.setReserve(maxCount);
    183     SkPoint* target = fPointBuffer.begin();
    184     int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
    185                                                      kQuadTolerance, &target, maxCount);
    186     fPointBuffer.setCount(count);
    187     for (int i = 0; i < count; i++) {
    188         this->handleLine(fPointBuffer[i]);
    189     }
    190 #else
    191     // for now, just to draw something
    192     this->handleLine(pts[1]);
    193     this->handleLine(pts[2]);
    194 #endif
    195 }
    196 
    197 void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
    198     m.mapPoints(pts, 3);
    199     this->handleQuad(pts);
    200 }
    201 
    202 void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
    203     m.mapPoints(pts, 4);
    204 #if SK_SUPPORT_GPU
    205     // TODO: Pull PathUtils out of Ganesh?
    206     int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
    207     fPointBuffer.setReserve(maxCount);
    208     SkPoint* target = fPointBuffer.begin();
    209     int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
    210                                                  kCubicTolerance, &target, maxCount);
    211     fPointBuffer.setCount(count);
    212     for (int i = 0; i < count; i++) {
    213         this->handleLine(fPointBuffer[i]);
    214     }
    215 #else
    216     // for now, just to draw something
    217     this->handleLine(pts[1]);
    218     this->handleLine(pts[2]);
    219     this->handleLine(pts[3]);
    220 #endif
    221 }
    222 
    223 void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
    224     if (m.hasPerspective()) {
    225         w = SkConic::TransformW(pts, w, m);
    226     }
    227     m.mapPoints(pts, 3);
    228     SkAutoConicToQuads quadder;
    229     const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
    230     SkPoint lastPoint = *(quads++);
    231     int count = quadder.countQuads();
    232     for (int i = 0; i < count; ++i) {
    233         SkPoint quadPts[3];
    234         quadPts[0] = lastPoint;
    235         quadPts[1] = quads[0];
    236         quadPts[2] = i == count - 1 ? pts[2] : quads[1];
    237         this->handleQuad(quadPts);
    238         lastPoint = quadPts[2];
    239         quads += 2;
    240     }
    241 }
    242 
    243 bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
    244     // fill in fan from previous quad
    245     SkScalar rotSin, rotCos;
    246     int numSteps;
    247     compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
    248     SkVector prevNormal = fPrevOutset;
    249     for (int i = 0; i < numSteps-1; ++i) {
    250         SkVector currNormal;
    251         currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
    252         currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
    253         *fPositions.push() = fPrevPoint + currNormal;
    254         *fColors.push() = fPenumbraColor;
    255         *fIndices.push() = fPrevUmbraIndex;
    256         *fIndices.push() = fPositions.count() - 1;
    257         *fIndices.push() = fPositions.count() - 2;
    258 
    259         prevNormal = currNormal;
    260     }
    261     if (finishArc && numSteps) {
    262         *fPositions.push() = fPrevPoint + nextNormal;
    263         *fColors.push() = fPenumbraColor;
    264         *fIndices.push() = fPrevUmbraIndex;
    265         *fIndices.push() = fPositions.count() - 1;
    266         *fIndices.push() = fPositions.count() - 2;
    267     }
    268     fPrevOutset = nextNormal;
    269 
    270     return (numSteps > 0);
    271 }
    272 
    273 bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
    274     if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
    275         fTransformedHeightFunc = [this](const SkPoint& p) {
    276             return fZPlaneParams.fZ;
    277         };
    278     } else {
    279         SkMatrix ctmInverse;
    280         if (!ctm.invert(&ctmInverse)) {
    281             return false;
    282         }
    283         // multiply by transpose
    284         fTransformedZParams = SkPoint3::Make(
    285             ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
    286             ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
    287             ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
    288 
    289             ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
    290             ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
    291             ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
    292 
    293             ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
    294             ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
    295             ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
    296         );
    297 
    298         if (ctm.hasPerspective()) {
    299             // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
    300             // so pre-compute those values that are independent of X and Y.
    301             // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
    302             fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
    303                                       ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
    304             fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
    305                                       ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
    306             fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
    307                                       ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
    308             SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
    309                                       ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
    310                                       ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
    311 
    312             // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
    313             // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
    314             fTransformedZParams.fX *= ctmDeterminant;
    315             fTransformedZParams.fY *= ctmDeterminant;
    316             fTransformedZParams.fZ *= ctmDeterminant;
    317 
    318             fTransformedHeightFunc = [this](const SkPoint& p) {
    319                 SkScalar denom = p.fX * fPartialDeterminants[0] +
    320                                  p.fY * fPartialDeterminants[1] +
    321                                  fPartialDeterminants[2];
    322                 SkScalar w = SkScalarFastInvert(denom);
    323                 return fZOffset + w*(fTransformedZParams.fX * p.fX +
    324                                      fTransformedZParams.fY * p.fY +
    325                                      fTransformedZParams.fZ);
    326             };
    327         } else {
    328             fTransformedHeightFunc = [this](const SkPoint& p) {
    329                 return fZOffset + fTransformedZParams.fX * p.fX +
    330                        fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
    331             };
    332         }
    333     }
    334 
    335     return true;
    336 }
    337 
    338 
    339 //////////////////////////////////////////////////////////////////////////////////////////////////
    340 
    341 class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
    342 public:
    343     SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
    344                                const SkPoint3& zPlaneParams, bool transparent);
    345 
    346 private:
    347     void handleLine(const SkPoint& p) override;
    348     void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
    349 
    350     static constexpr auto kMaxEdgeLenSqr = 20 * 20;
    351     static constexpr auto kInsetFactor = -0.5f;
    352 
    353     SkScalar offset(SkScalar z) {
    354         return SkDrawShadowMetrics::AmbientBlurRadius(z);
    355     }
    356     SkColor umbraColor(SkScalar z) {
    357         SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(z));
    358         return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
    359     }
    360 
    361     int                 fCentroidCount;
    362     bool                fSplitFirstEdge;
    363     bool                fSplitPreviousEdge;
    364 
    365     typedef SkBaseShadowTessellator INHERITED;
    366 };
    367 
    368 SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
    369                                                        const SkMatrix& ctm,
    370                                                        const SkPoint3& zPlaneParams,
    371                                                        bool transparent)
    372         : INHERITED(zPlaneParams, transparent)
    373         , fSplitFirstEdge(false)
    374         , fSplitPreviousEdge(false) {
    375     // Set base colors
    376     SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
    377     // umbraColor is the interior value, penumbraColor the exterior value.
    378     // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
    379     // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
    380     // the final alpha.
    381     fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
    382     fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
    383 
    384     // make sure we're not below the canvas plane
    385     this->setZOffset(path.getBounds(), ctm.hasPerspective());
    386 
    387     this->setTransformedHeightFunc(ctm);
    388 
    389     // Outer ring: 3*numPts
    390     // Middle ring: numPts
    391     fPositions.setReserve(4 * path.countPoints());
    392     fColors.setReserve(4 * path.countPoints());
    393     // Outer ring: 12*numPts
    394     // Middle ring: 0
    395     fIndices.setReserve(12 * path.countPoints());
    396 
    397     // walk around the path, tessellate and generate outer ring
    398     // if original path is transparent, will accumulate sum of points for centroid
    399     SkPath::Iter iter(path, true);
    400     SkPoint pts[4];
    401     SkPath::Verb verb;
    402     if (fTransparent) {
    403         *fPositions.push() = SkPoint::Make(0, 0);
    404         *fColors.push() = fUmbraColor;
    405         fCentroidCount = 0;
    406     }
    407     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
    408         switch (verb) {
    409             case SkPath::kLine_Verb:
    410                 this->INHERITED::handleLine(ctm, &pts[1]);
    411                 break;
    412             case SkPath::kQuad_Verb:
    413                 this->handleQuad(ctm, pts);
    414                 break;
    415             case SkPath::kCubic_Verb:
    416                 this->handleCubic(ctm, pts);
    417                 break;
    418             case SkPath::kConic_Verb:
    419                 this->handleConic(ctm, pts, iter.conicWeight());
    420                 break;
    421             case SkPath::kMove_Verb:
    422             case SkPath::kClose_Verb:
    423             case SkPath::kDone_Verb:
    424                 break;
    425         }
    426     }
    427 
    428     if (!this->indexCount()) {
    429         return;
    430     }
    431 
    432     // Finish up
    433     SkVector normal;
    434     if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
    435         SkScalar z = fTransformedHeightFunc(fPrevPoint);
    436         fRadius = this->offset(z);
    437         SkVector scaledNormal(normal);
    438         scaledNormal *= fRadius;
    439         this->addArc(scaledNormal, true);
    440 
    441         // fix-up the last and first umbra points
    442         SkVector inset = normal;
    443         // adding to an average, so multiply by an additional half
    444         inset *= 0.5f*kInsetFactor;
    445         fPositions[fPrevUmbraIndex] += inset;
    446         fPositions[fFirstVertexIndex] += inset;
    447         // we multiply by another half because now we're adding to an average of an average
    448         inset *= 0.5f;
    449         if (fSplitPreviousEdge) {
    450             fPositions[fPrevUmbraIndex - 2] += inset;
    451         }
    452         if (fSplitFirstEdge) {
    453             fPositions[fFirstVertexIndex + 2] += inset;
    454         }
    455 
    456         // set up for final edge
    457         z = fTransformedHeightFunc(fFirstPoint);
    458         normal *= this->offset(z);
    459 
    460         // make sure we don't end up with a sharp alpha edge along the quad diagonal
    461         if (fColors[fPrevUmbraIndex] != fColors[fFirstVertexIndex] &&
    462             SkPointPriv::DistanceToSqd(fFirstPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
    463             SkPoint centerPoint = fPositions[fPrevUmbraIndex] + fPositions[fFirstVertexIndex];
    464             centerPoint *= 0.5f;
    465             *fPositions.push() = centerPoint;
    466             *fColors.push() = SkPMLerp(fColors[fFirstVertexIndex], fColors[fPrevUmbraIndex], 128);
    467             centerPoint = fPositions[fPositions.count()-2] + fPositions[fFirstVertexIndex+1];
    468             centerPoint *= 0.5f;
    469             *fPositions.push() = centerPoint;
    470             *fColors.push() = fPenumbraColor;
    471 
    472             if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
    473                 *fIndices.push() = fPrevUmbraIndex;
    474                 *fIndices.push() = fPositions.count() - 3;
    475                 *fIndices.push() = fPositions.count() - 2;
    476 
    477                 *fIndices.push() = fPositions.count() - 3;
    478                 *fIndices.push() = fPositions.count() - 1;
    479                 *fIndices.push() = fPositions.count() - 2;
    480             } else {
    481                 *fIndices.push() = fPrevUmbraIndex;
    482                 *fIndices.push() = fPositions.count() - 2;
    483                 *fIndices.push() = fPositions.count() - 1;
    484 
    485                 *fIndices.push() = fPrevUmbraIndex;
    486                 *fIndices.push() = fPositions.count() - 1;
    487                 *fIndices.push() = fPositions.count() - 3;
    488             }
    489 
    490             // if transparent, add point to first one in array and add to center fan
    491             if (fTransparent) {
    492                 fPositions[0] += centerPoint;
    493                 ++fCentroidCount;
    494 
    495                 *fIndices.push() = 0;
    496                 *fIndices.push() = fPrevUmbraIndex;
    497                 *fIndices.push() = fPositions.count() - 2;
    498             }
    499 
    500             fPrevUmbraIndex = fPositions.count() - 2;
    501         }
    502 
    503         // final edge
    504         *fPositions.push() = fFirstPoint + normal;
    505         *fColors.push() = fPenumbraColor;
    506 
    507         if (fColors[fPrevUmbraIndex] > fColors[fFirstVertexIndex]) {
    508             *fIndices.push() = fPrevUmbraIndex;
    509             *fIndices.push() = fPositions.count() - 2;
    510             *fIndices.push() = fFirstVertexIndex;
    511 
    512             *fIndices.push() = fPositions.count() - 2;
    513             *fIndices.push() = fPositions.count() - 1;
    514             *fIndices.push() = fFirstVertexIndex;
    515         } else {
    516             *fIndices.push() = fPrevUmbraIndex;
    517             *fIndices.push() = fPositions.count() - 2;
    518             *fIndices.push() = fPositions.count() - 1;
    519 
    520             *fIndices.push() = fPrevUmbraIndex;
    521             *fIndices.push() = fPositions.count() - 1;
    522             *fIndices.push() = fFirstVertexIndex;
    523         }
    524         fPrevOutset = normal;
    525     }
    526 
    527     // finalize centroid
    528     if (fTransparent) {
    529         fPositions[0] *= SkScalarFastInvert(fCentroidCount);
    530         fColors[0] = this->umbraColor(fTransformedHeightFunc(fPositions[0]));
    531 
    532         *fIndices.push() = 0;
    533         *fIndices.push() = fPrevUmbraIndex;
    534         *fIndices.push() = fFirstVertexIndex;
    535     }
    536 
    537     // final fan
    538     if (fPositions.count() >= 3) {
    539         fPrevUmbraIndex = fFirstVertexIndex;
    540         fPrevPoint = fFirstPoint;
    541         fRadius = this->offset(fTransformedHeightFunc(fPrevPoint));
    542         if (this->addArc(fFirstOutset, false)) {
    543             *fIndices.push() = fFirstVertexIndex;
    544             *fIndices.push() = fPositions.count() - 1;
    545             *fIndices.push() = fFirstVertexIndex + 1;
    546         } else {
    547             // arc is too small, set the first penumbra point to be the same position
    548             // as the last one
    549             fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
    550         }
    551     }
    552     fSucceeded = true;
    553 }
    554 
    555 void SkAmbientShadowTessellator::handleLine(const SkPoint& p)  {
    556     if (fInitPoints.count() < 2) {
    557         *fInitPoints.push() = p;
    558         return;
    559     }
    560 
    561     if (fInitPoints.count() == 2) {
    562         // determine if cw or ccw
    563         SkVector v0 = fInitPoints[1] - fInitPoints[0];
    564         SkVector v1 = p - fInitPoints[0];
    565         SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
    566         if (SkScalarNearlyZero(perpDot)) {
    567             // nearly parallel, just treat as straight line and continue
    568             fInitPoints[1] = p;
    569             return;
    570         }
    571 
    572         // if perpDot > 0, winding is ccw
    573         fDirection = (perpDot > 0) ? -1 : 1;
    574 
    575         // add first quad
    576         SkVector normal;
    577         if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &normal)) {
    578             // first two points are incident, make the third point the second and continue
    579             fInitPoints[1] = p;
    580             return;
    581         }
    582 
    583         fFirstPoint = fInitPoints[0];
    584         fFirstVertexIndex = fPositions.count();
    585         SkScalar z = fTransformedHeightFunc(fFirstPoint);
    586         fFirstOutset = normal;
    587         fFirstOutset *= this->offset(z);
    588 
    589         fPrevOutset = fFirstOutset;
    590         fPrevPoint = fFirstPoint;
    591         fPrevUmbraIndex = fFirstVertexIndex;
    592 
    593         *fPositions.push() = fFirstPoint;
    594         *fColors.push() = this->umbraColor(z);
    595         *fPositions.push() = fFirstPoint + fFirstOutset;
    596         *fColors.push() = fPenumbraColor;
    597         if (fTransparent) {
    598             fPositions[0] += fFirstPoint;
    599             fCentroidCount = 1;
    600         }
    601 
    602         // add the first quad
    603         z = fTransformedHeightFunc(fInitPoints[1]);
    604         fRadius = this->offset(z);
    605         fUmbraColor = this->umbraColor(z);
    606         this->addEdge(fInitPoints[1], normal);
    607 
    608         // to ensure we skip this block next time
    609         *fInitPoints.push() = p;
    610     }
    611 
    612     SkVector normal;
    613     if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
    614         SkVector scaledNormal = normal;
    615         scaledNormal *= fRadius;
    616         this->addArc(scaledNormal, true);
    617         SkScalar z = fTransformedHeightFunc(p);
    618         fRadius = this->offset(z);
    619         fUmbraColor = this->umbraColor(z);
    620         this->addEdge(p, normal);
    621     }
    622 }
    623 
    624 void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
    625     // We compute the inset in two stages: first we inset by half the current normal,
    626     // then on the next addEdge() we add half of the next normal to get an average of the two
    627     SkVector insetNormal = nextNormal;
    628     insetNormal *= 0.5f*kInsetFactor;
    629 
    630     // Adding the other half of the average for the previous edge
    631     fPositions[fPrevUmbraIndex] += insetNormal;
    632 
    633     SkPoint umbraPoint = nextPoint + insetNormal;
    634     SkVector outsetNormal = nextNormal;
    635     outsetNormal *= fRadius;
    636     SkPoint penumbraPoint = nextPoint + outsetNormal;
    637 
    638     // For split edges, we're adding an average of two averages, so we multiply by another half
    639     if (fSplitPreviousEdge) {
    640         insetNormal *= 0.5f;
    641         fPositions[fPrevUmbraIndex - 2] += insetNormal;
    642     }
    643 
    644     // Split the edge to make sure we don't end up with a sharp alpha edge along the quad diagonal
    645     if (fColors[fPrevUmbraIndex] != fUmbraColor &&
    646         SkPointPriv::DistanceToSqd(nextPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
    647 
    648         // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
    649         SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
    650         centerPoint *= 0.5f;
    651         *fPositions.push() = centerPoint;
    652         *fColors.push() = SkPMLerp(fUmbraColor, fColors[fPrevUmbraIndex], 128);
    653         centerPoint = fPositions[fPositions.count()-2] + penumbraPoint;
    654         centerPoint *= 0.5f;
    655         *fPositions.push() = centerPoint;
    656         *fColors.push() = fPenumbraColor;
    657 
    658         // set triangularization to get best interpolation of color
    659         if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
    660             *fIndices.push() = fPrevUmbraIndex;
    661             *fIndices.push() = fPositions.count() - 3;
    662             *fIndices.push() = fPositions.count() - 2;
    663 
    664             *fIndices.push() = fPositions.count() - 3;
    665             *fIndices.push() = fPositions.count() - 1;
    666             *fIndices.push() = fPositions.count() - 2;
    667         } else {
    668             *fIndices.push() = fPrevUmbraIndex;
    669             *fIndices.push() = fPositions.count() - 2;
    670             *fIndices.push() = fPositions.count() - 1;
    671 
    672             *fIndices.push() = fPrevUmbraIndex;
    673             *fIndices.push() = fPositions.count() - 1;
    674             *fIndices.push() = fPositions.count() - 3;
    675         }
    676 
    677         // if transparent, add point to first one in array and add to center fan
    678         if (fTransparent) {
    679             fPositions[0] += centerPoint;
    680             ++fCentroidCount;
    681 
    682             *fIndices.push() = 0;
    683             *fIndices.push() = fPrevUmbraIndex;
    684             *fIndices.push() = fPositions.count() - 2;
    685         }
    686 
    687         fSplitPreviousEdge = true;
    688         if (fPrevUmbraIndex == fFirstVertexIndex) {
    689             fSplitFirstEdge = true;
    690         }
    691         fPrevUmbraIndex = fPositions.count() - 2;
    692     } else {
    693         fSplitPreviousEdge = false;
    694     }
    695 
    696     // add next quad
    697     *fPositions.push() = umbraPoint;
    698     *fColors.push() = fUmbraColor;
    699     *fPositions.push() = penumbraPoint;
    700     *fColors.push() = fPenumbraColor;
    701 
    702     // set triangularization to get best interpolation of color
    703     if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
    704         *fIndices.push() = fPrevUmbraIndex;
    705         *fIndices.push() = fPositions.count() - 3;
    706         *fIndices.push() = fPositions.count() - 2;
    707 
    708         *fIndices.push() = fPositions.count() - 3;
    709         *fIndices.push() = fPositions.count() - 1;
    710         *fIndices.push() = fPositions.count() - 2;
    711     } else {
    712         *fIndices.push() = fPrevUmbraIndex;
    713         *fIndices.push() = fPositions.count() - 2;
    714         *fIndices.push() = fPositions.count() - 1;
    715 
    716         *fIndices.push() = fPrevUmbraIndex;
    717         *fIndices.push() = fPositions.count() - 1;
    718         *fIndices.push() = fPositions.count() - 3;
    719     }
    720 
    721     // if transparent, add point to first one in array and add to center fan
    722     if (fTransparent) {
    723         fPositions[0] += nextPoint;
    724         ++fCentroidCount;
    725 
    726         *fIndices.push() = 0;
    727         *fIndices.push() = fPrevUmbraIndex;
    728         *fIndices.push() = fPositions.count() - 2;
    729     }
    730 
    731     fPrevUmbraIndex = fPositions.count() - 2;
    732     fPrevPoint = nextPoint;
    733     fPrevOutset = outsetNormal;
    734 }
    735 
    736 ///////////////////////////////////////////////////////////////////////////////////////////////////
    737 
    738 class SkSpotShadowTessellator : public SkBaseShadowTessellator {
    739 public:
    740     SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
    741                             const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
    742                             SkScalar lightRadius, bool transparent);
    743 
    744 private:
    745     void computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
    746                                     const SkMatrix& shadowTransform);
    747     void computeClipVectorsAndTestCentroid();
    748     bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
    749     int getClosestUmbraPoint(const SkPoint& point);
    750 
    751     void handleLine(const SkPoint& p) override;
    752     bool handlePolyPoint(const SkPoint& p);
    753 
    754     void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
    755     bool addInnerPoint(const SkPoint& pathPoint);
    756     void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
    757 
    758     SkScalar offset(SkScalar z) {
    759         float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
    760         return fLightRadius*zRatio;
    761     }
    762 
    763     SkScalar            fLightZ;
    764     SkScalar            fLightRadius;
    765     SkScalar            fOffsetAdjust;
    766 
    767     SkTDArray<SkPoint>  fClipPolygon;
    768     SkTDArray<SkVector> fClipVectors;
    769     SkPoint             fCentroid;
    770     SkScalar            fArea;
    771 
    772     SkTDArray<SkPoint>  fPathPolygon;
    773     SkTDArray<SkPoint>  fUmbraPolygon;
    774     int                 fCurrClipPoint;
    775     int                 fCurrUmbraPoint;
    776     bool                fPrevUmbraOutside;
    777     bool                fFirstUmbraOutside;
    778     bool                fValidUmbra;
    779 
    780     typedef SkBaseShadowTessellator INHERITED;
    781 };
    782 
    783 SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
    784                                                  const SkPoint3& zPlaneParams,
    785                                                  const SkPoint3& lightPos, SkScalar lightRadius,
    786                                                  bool transparent)
    787     : INHERITED(zPlaneParams, transparent)
    788     , fLightZ(lightPos.fZ)
    789     , fLightRadius(lightRadius)
    790     , fOffsetAdjust(0)
    791     , fCurrClipPoint(0)
    792     , fPrevUmbraOutside(false)
    793     , fFirstUmbraOutside(false)
    794     , fValidUmbra(true) {
    795 
    796     // make sure we're not below the canvas plane
    797     if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
    798         // Adjust light height and radius
    799         fLightRadius *= (fLightZ + fZOffset) / fLightZ;
    800         fLightZ += fZOffset;
    801     }
    802 
    803     // Set radius and colors
    804     SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
    805     SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
    806     fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
    807     fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
    808 
    809     // Compute the blur radius, scale and translation for the spot shadow.
    810     SkScalar radius;
    811     SkMatrix shadowTransform;
    812     if (!ctm.hasPerspective()) {
    813         SkScalar scale;
    814         SkVector translate;
    815         SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
    816                                            lightRadius, &radius, &scale, &translate);
    817         shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
    818     } else {
    819         // For perspective, we have a scale, a z-shear, and another projective divide --
    820         // this varies at each point so we can't use an affine transform.
    821         // We'll just apply this to each generated point in turn.
    822         shadowTransform.reset();
    823         // Also can't cull the center (for now).
    824         fTransparent = true;
    825         radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
    826     }
    827     fRadius = radius;
    828     SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm);
    829 
    830     // Set up our reverse mapping
    831     this->setTransformedHeightFunc(fullTransform);
    832 
    833     // TODO: calculate these reserves better
    834     // Penumbra ring: 3*numPts
    835     // Umbra ring: numPts
    836     // Inner ring: numPts
    837     fPositions.setReserve(5 * path.countPoints());
    838     fColors.setReserve(5 * path.countPoints());
    839     // Penumbra ring: 12*numPts
    840     // Umbra ring: 3*numPts
    841     fIndices.setReserve(15 * path.countPoints());
    842     fClipPolygon.setReserve(path.countPoints());
    843 
    844     // compute rough clip bounds for umbra, plus offset polygon, plus centroid
    845     this->computeClipAndPathPolygons(path, ctm, shadowTransform);
    846     if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3) {
    847         return;
    848     }
    849 
    850     // check to see if umbra collapses
    851     SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, fPathPolygon[0],
    852                                                                       fPathPolygon[1]);
    853     SkRect bounds;
    854     bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
    855     for (int i = 1; i < fPathPolygon.count(); ++i) {
    856         int j = i + 1;
    857         if (i == fPathPolygon.count() - 1) {
    858             j = 0;
    859         }
    860         SkPoint currPoint = fPathPolygon[i];
    861         SkPoint nextPoint = fPathPolygon[j];
    862         SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
    863                                                                        nextPoint);
    864         if (distSq < minDistSq) {
    865             minDistSq = distSq;
    866         }
    867     }
    868     static constexpr auto kTolerance = 1.0e-2f;
    869     if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
    870         // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
    871         SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
    872         fOffsetAdjust = newRadius - radius;
    873         SkScalar ratio = 128 * (newRadius + radius) / radius;
    874         // they aren't PMColors, but the interpolation algorithm is the same
    875         fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
    876         radius = newRadius;
    877     }
    878 
    879     // compute vectors for clip tests
    880     this->computeClipVectorsAndTestCentroid();
    881 
    882     // generate inner ring
    883     if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
    884                               &fUmbraPolygon)) {
    885         // this shouldn't happen, but just in case we'll inset using the centroid
    886         fValidUmbra = false;
    887     }
    888 
    889     // walk around the path polygon, generate outer ring and connect to inner ring
    890     if (fTransparent) {
    891         *fPositions.push() = fCentroid;
    892         *fColors.push() = fUmbraColor;
    893     }
    894     fCurrUmbraPoint = 0;
    895     for (int i = 0; i < fPathPolygon.count(); ++i) {
    896         if (!this->handlePolyPoint(fPathPolygon[i])) {
    897             return;
    898         }
    899     }
    900 
    901     if (!this->indexCount()) {
    902         return;
    903     }
    904 
    905     // finish up the final verts
    906     SkVector normal;
    907     if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
    908         normal *= fRadius;
    909         this->addArc(normal, true);
    910 
    911         // add to center fan
    912         if (fTransparent) {
    913             *fIndices.push() = 0;
    914             *fIndices.push() = fPrevUmbraIndex;
    915             *fIndices.push() = fFirstVertexIndex;
    916             // or to clip ring
    917         } else {
    918             if (fFirstUmbraOutside) {
    919                 *fIndices.push() = fPrevUmbraIndex;
    920                 *fIndices.push() = fFirstVertexIndex;
    921                 *fIndices.push() = fFirstVertexIndex + 1;
    922                 if (fPrevUmbraOutside) {
    923                     // fill out quad
    924                     *fIndices.push() = fPrevUmbraIndex;
    925                     *fIndices.push() = fFirstVertexIndex + 1;
    926                     *fIndices.push() = fPrevUmbraIndex + 1;
    927                 }
    928             } else if (fPrevUmbraOutside) {
    929                 // add tri
    930                 *fIndices.push() = fPrevUmbraIndex;
    931                 *fIndices.push() = fFirstVertexIndex;
    932                 *fIndices.push() = fPrevUmbraIndex + 1;
    933             }
    934         }
    935 
    936         // add final edge
    937         *fPositions.push() = fFirstPoint + normal;
    938         *fColors.push() = fPenumbraColor;
    939 
    940         *fIndices.push() = fPrevUmbraIndex;
    941         *fIndices.push() = fPositions.count() - 2;
    942         *fIndices.push() = fFirstVertexIndex;
    943 
    944         *fIndices.push() = fPositions.count() - 2;
    945         *fIndices.push() = fPositions.count() - 1;
    946         *fIndices.push() = fFirstVertexIndex;
    947 
    948         fPrevOutset = normal;
    949     }
    950 
    951     // final fan
    952     if (fPositions.count() >= 3) {
    953         fPrevUmbraIndex = fFirstVertexIndex;
    954         fPrevPoint = fFirstPoint;
    955         if (this->addArc(fFirstOutset, false)) {
    956             *fIndices.push() = fFirstVertexIndex;
    957             *fIndices.push() = fPositions.count() - 1;
    958             if (fFirstUmbraOutside) {
    959                 *fIndices.push() = fFirstVertexIndex + 2;
    960             } else {
    961                 *fIndices.push() = fFirstVertexIndex + 1;
    962             }
    963         } else {
    964             // no arc added, fix up by setting first penumbra point position to last one
    965             if (fFirstUmbraOutside) {
    966                 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
    967             } else {
    968                 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
    969             }
    970         }
    971     }
    972 
    973     if (ctm.hasPerspective()) {
    974         for (int i = 0; i < fPositions.count(); ++i) {
    975             SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
    976             SkScalar factor = SkScalarInvert(fLightZ - pathZ);
    977             fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
    978             fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor;
    979         }
    980 #ifdef DRAW_CENTROID
    981         SkScalar pathZ = fTransformedHeightFunc(fCentroid);
    982         SkScalar factor = SkScalarInvert(fLightZ - pathZ);
    983         fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor;
    984         fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor;
    985 #endif
    986     }
    987 #ifdef DRAW_CENTROID
    988     *fPositions.push() = fCentroid + SkVector::Make(-2, -2);
    989     *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
    990     *fPositions.push() = fCentroid + SkVector::Make(2, -2);
    991     *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
    992     *fPositions.push() = fCentroid + SkVector::Make(-2, 2);
    993     *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
    994     *fPositions.push() = fCentroid + SkVector::Make(2, 2);
    995     *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
    996 
    997     *fIndices.push() = fPositions.count() - 4;
    998     *fIndices.push() = fPositions.count() - 2;
    999     *fIndices.push() = fPositions.count() - 1;
   1000 
   1001     *fIndices.push() = fPositions.count() - 4;
   1002     *fIndices.push() = fPositions.count() - 1;
   1003     *fIndices.push() = fPositions.count() - 3;
   1004 #endif
   1005 
   1006     fSucceeded = true;
   1007 }
   1008 
   1009 void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
   1010                                                          const SkMatrix& shadowTransform) {
   1011 
   1012     fPathPolygon.setReserve(path.countPoints());
   1013 
   1014     // Walk around the path and compute clip polygon and path polygon.
   1015     // Will also accumulate sum of areas for centroid.
   1016     // For Bezier curves, we compute additional interior points on curve.
   1017     SkPath::Iter iter(path, true);
   1018     SkPoint pts[4];
   1019     SkPath::Verb verb;
   1020 
   1021     fClipPolygon.reset();
   1022 
   1023     // init centroid
   1024     fCentroid = SkPoint::Make(0, 0);
   1025     fArea = 0;
   1026 
   1027     // coefficients to compute cubic Bezier at t = 5/16
   1028     static constexpr SkScalar kA = 0.32495117187f;
   1029     static constexpr SkScalar kB = 0.44311523437f;
   1030     static constexpr SkScalar kC = 0.20141601562f;
   1031     static constexpr SkScalar kD = 0.03051757812f;
   1032 
   1033     SkPoint curvePoint;
   1034     SkScalar w;
   1035     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
   1036         switch (verb) {
   1037             case SkPath::kLine_Verb:
   1038                 ctm.mapPoints(&pts[1], 1);
   1039                 *fClipPolygon.push() = pts[1];
   1040                 this->INHERITED::handleLine(shadowTransform, &pts[1]);
   1041                 break;
   1042             case SkPath::kQuad_Verb:
   1043                 ctm.mapPoints(pts, 3);
   1044                 // point at t = 1/2
   1045                 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
   1046                 curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
   1047                 *fClipPolygon.push() = curvePoint;
   1048                 *fClipPolygon.push() = pts[2];
   1049                 this->handleQuad(shadowTransform, pts);
   1050                 break;
   1051             case SkPath::kConic_Verb:
   1052                 ctm.mapPoints(pts, 3);
   1053                 w = iter.conicWeight();
   1054                 // point at t = 1/2
   1055                 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
   1056                 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
   1057                 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
   1058                 *fClipPolygon.push() = curvePoint;
   1059                 *fClipPolygon.push() = pts[2];
   1060                 this->handleConic(shadowTransform, pts, w);
   1061                 break;
   1062             case SkPath::kCubic_Verb:
   1063                 ctm.mapPoints(pts, 4);
   1064                 // point at t = 5/16
   1065                 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
   1066                 curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
   1067                 *fClipPolygon.push() = curvePoint;
   1068                 // point at t = 11/16
   1069                 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
   1070                 curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
   1071                 *fClipPolygon.push() = curvePoint;
   1072                 *fClipPolygon.push() = pts[3];
   1073                 this->handleCubic(shadowTransform, pts);
   1074                 break;
   1075             case SkPath::kMove_Verb:
   1076             case SkPath::kClose_Verb:
   1077             case SkPath::kDone_Verb:
   1078                 break;
   1079             default:
   1080                 SkDEBUGFAIL("unknown verb");
   1081         }
   1082     }
   1083 
   1084     // finish centroid
   1085     if (fPathPolygon.count() > 0) {
   1086         SkPoint currPoint = fPathPolygon[fPathPolygon.count() - 1];
   1087         SkPoint nextPoint = fPathPolygon[0];
   1088         SkScalar quadArea = currPoint.cross(nextPoint);
   1089         fCentroid.fX += (currPoint.fX + nextPoint.fX) * quadArea;
   1090         fCentroid.fY += (currPoint.fY + nextPoint.fY) * quadArea;
   1091         fArea += quadArea;
   1092         fCentroid *= SK_Scalar1 / (3 * fArea);
   1093     }
   1094 
   1095     fCurrClipPoint = fClipPolygon.count() - 1;
   1096 }
   1097 
   1098 void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
   1099     SkASSERT(fClipPolygon.count() >= 3);
   1100 
   1101     // init clip vectors
   1102     SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
   1103     *fClipVectors.push() = v0;
   1104 
   1105     // init centroid check
   1106     bool hiddenCentroid = true;
   1107     SkVector v1 = fCentroid - fClipPolygon[0];
   1108     SkScalar initCross = v0.cross(v1);
   1109 
   1110     for (int p = 1; p < fClipPolygon.count(); ++p) {
   1111         // add to clip vectors
   1112         v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
   1113         *fClipVectors.push() = v0;
   1114         // Determine if transformed centroid is inside clipPolygon.
   1115         v1 = fCentroid - fClipPolygon[p];
   1116         if (initCross*v0.cross(v1) <= 0) {
   1117             hiddenCentroid = false;
   1118         }
   1119     }
   1120     SkASSERT(fClipVectors.count() == fClipPolygon.count());
   1121 
   1122     fTransparent = fTransparent || !hiddenCentroid;
   1123 }
   1124 
   1125 bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
   1126                                              SkPoint* clipPoint) {
   1127     SkVector segmentVector = centroid - umbraPoint;
   1128 
   1129     int startClipPoint = fCurrClipPoint;
   1130     do {
   1131         SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
   1132         SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
   1133         SkScalar t_num = dp.cross(segmentVector);
   1134         // if line segments are nearly parallel
   1135         if (SkScalarNearlyZero(denom)) {
   1136             // and collinear
   1137             if (SkScalarNearlyZero(t_num)) {
   1138                 return false;
   1139             }
   1140             // otherwise are separate, will try the next poly segment
   1141         // else if crossing lies within poly segment
   1142         } else if (t_num >= 0 && t_num <= denom) {
   1143             SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
   1144             // if umbra point is inside the clip polygon
   1145             if (s_num >= 0 && s_num <= denom) {
   1146                 segmentVector *= s_num/denom;
   1147                 *clipPoint = umbraPoint + segmentVector;
   1148                 return true;
   1149             }
   1150         }
   1151         fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
   1152     } while (fCurrClipPoint != startClipPoint);
   1153 
   1154     return false;
   1155 }
   1156 
   1157 int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
   1158     SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraPoint]);
   1159     int index = fCurrUmbraPoint;
   1160     int dir = 1;
   1161     int next = (index + dir) % fUmbraPolygon.count();
   1162 
   1163     // init travel direction
   1164     SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
   1165     if (distance < minDistance) {
   1166         index = next;
   1167         minDistance = distance;
   1168     } else {
   1169         dir = fUmbraPolygon.count()-1;
   1170     }
   1171 
   1172     // iterate until we find a point that increases the distance
   1173     next = (index + dir) % fUmbraPolygon.count();
   1174     distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
   1175     while (distance < minDistance) {
   1176         index = next;
   1177         minDistance = distance;
   1178         next = (index + dir) % fUmbraPolygon.count();
   1179         distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
   1180     }
   1181 
   1182     fCurrUmbraPoint = index;
   1183     return index;
   1184 }
   1185 
   1186 void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
   1187                                         SkPoint* pts, int count) {
   1188     // TODO: vectorize
   1189     for (int i = 0; i < count; ++i) {
   1190         pts[i] *= scale;
   1191         pts[i] += xlate;
   1192     }
   1193 }
   1194 
   1195 static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
   1196     static constexpr SkScalar kClose = (SK_Scalar1 / 16);
   1197     static constexpr SkScalar kCloseSqd = kClose*kClose;
   1198 
   1199     SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
   1200     return distSq < kCloseSqd;
   1201 }
   1202 
   1203 static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
   1204     SkVector v0 = p1 - p0;
   1205     SkVector v1 = p2 - p0;
   1206     return v0.cross(v1);
   1207 }
   1208 
   1209 static bool is_collinear(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
   1210     return (SkScalarNearlyZero(perp_dot(p0, p1, p2)));
   1211 }
   1212 
   1213 void SkSpotShadowTessellator::handleLine(const SkPoint& p) {
   1214     // remove coincident points and add to centroid
   1215     if (fPathPolygon.count() > 0) {
   1216         const SkPoint& lastPoint = fPathPolygon[fPathPolygon.count() - 1];
   1217         if (duplicate_pt(p, lastPoint)) {
   1218             return;
   1219         }
   1220         SkScalar quadArea = lastPoint.cross(p);
   1221         fCentroid.fX += (p.fX + lastPoint.fX) * quadArea;
   1222         fCentroid.fY += (p.fY + lastPoint.fY) * quadArea;
   1223         fArea += quadArea;
   1224     }
   1225 
   1226     // try to remove collinear points
   1227     if (fPathPolygon.count() > 1 && is_collinear(fPathPolygon[fPathPolygon.count()-2],
   1228                                                  fPathPolygon[fPathPolygon.count()-1],
   1229                                                  p)) {
   1230         fPathPolygon[fPathPolygon.count() - 1] = p;
   1231     } else {
   1232         *fPathPolygon.push() = p;
   1233     }
   1234 }
   1235 
   1236 bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
   1237     if (fInitPoints.count() < 2) {
   1238         *fInitPoints.push() = p;
   1239         return true;
   1240     }
   1241 
   1242     if (fInitPoints.count() == 2) {
   1243         // determine if cw or ccw
   1244         SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
   1245         if (SkScalarNearlyZero(perpDot)) {
   1246             // nearly parallel, just treat as straight line and continue
   1247             fInitPoints[1] = p;
   1248             return true;
   1249         }
   1250 
   1251         // if perpDot > 0, winding is ccw
   1252         fDirection = (perpDot > 0) ? -1 : 1;
   1253 
   1254         // add first quad
   1255         if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &fFirstOutset)) {
   1256             // first two points are incident, make the third point the second and continue
   1257             fInitPoints[1] = p;
   1258             return true;
   1259         }
   1260 
   1261         fFirstOutset *= fRadius;
   1262         fFirstPoint = fInitPoints[0];
   1263         fFirstVertexIndex = fPositions.count();
   1264         fPrevOutset = fFirstOutset;
   1265         fPrevPoint = fFirstPoint;
   1266         fPrevUmbraIndex = -1;
   1267 
   1268         this->addInnerPoint(fFirstPoint);
   1269         fPrevUmbraIndex = fFirstVertexIndex;
   1270 
   1271         if (!fTransparent) {
   1272             SkPoint clipPoint;
   1273             bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
   1274                                                   fCentroid, &clipPoint);
   1275             if (isOutside) {
   1276                 *fPositions.push() = clipPoint;
   1277                 *fColors.push() = fUmbraColor;
   1278             }
   1279             fPrevUmbraOutside = isOutside;
   1280             fFirstUmbraOutside = isOutside;
   1281         }
   1282 
   1283         SkPoint newPoint = fFirstPoint + fFirstOutset;
   1284         *fPositions.push() = newPoint;
   1285         *fColors.push() = fPenumbraColor;
   1286         this->addEdge(fInitPoints[1], fFirstOutset);
   1287 
   1288         // to ensure we skip this block next time
   1289         *fInitPoints.push() = p;
   1290     }
   1291 
   1292     // if concave, abort
   1293     SkScalar perpDot = perp_dot(fInitPoints[1], fInitPoints[2], p);
   1294     if (fDirection*perpDot > 0) {
   1295         return false;
   1296     }
   1297 
   1298     SkVector normal;
   1299     if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
   1300         normal *= fRadius;
   1301         this->addArc(normal, true);
   1302         this->addEdge(p, normal);
   1303         fInitPoints[1] = fInitPoints[2];
   1304         fInitPoints[2] = p;
   1305     }
   1306 
   1307     return true;
   1308 }
   1309 
   1310 bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
   1311     SkPoint umbraPoint;
   1312     if (!fValidUmbra) {
   1313         SkVector v = fCentroid - pathPoint;
   1314         v *= 0.95f;
   1315         umbraPoint = pathPoint + v;
   1316     } else {
   1317         umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
   1318     }
   1319 
   1320     fPrevPoint = pathPoint;
   1321 
   1322     // merge "close" points
   1323     if (fPrevUmbraIndex == -1 ||
   1324         !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
   1325         *fPositions.push() = umbraPoint;
   1326         *fColors.push() = fUmbraColor;
   1327 
   1328         return false;
   1329     } else {
   1330         return true;
   1331     }
   1332 }
   1333 
   1334 void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
   1335     // add next umbra point
   1336     bool duplicate = this->addInnerPoint(nextPoint);
   1337     int prevPenumbraIndex = duplicate ? fPositions.count()-1 : fPositions.count()-2;
   1338     int currUmbraIndex = duplicate ? fPrevUmbraIndex : fPositions.count()-1;
   1339 
   1340     if (!duplicate) {
   1341         // add to center fan if transparent or centroid showing
   1342         if (fTransparent) {
   1343             *fIndices.push() = 0;
   1344             *fIndices.push() = fPrevUmbraIndex;
   1345             *fIndices.push() = currUmbraIndex;
   1346         // otherwise add to clip ring
   1347         } else {
   1348             SkPoint clipPoint;
   1349             bool isOutside = this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
   1350                                                   &clipPoint);
   1351             if (isOutside) {
   1352                 *fPositions.push() = clipPoint;
   1353                 *fColors.push() = fUmbraColor;
   1354 
   1355                 *fIndices.push() = fPrevUmbraIndex;
   1356                 *fIndices.push() = currUmbraIndex;
   1357                 *fIndices.push() = currUmbraIndex + 1;
   1358                 if (fPrevUmbraOutside) {
   1359                     // fill out quad
   1360                     *fIndices.push() = fPrevUmbraIndex;
   1361                     *fIndices.push() = currUmbraIndex + 1;
   1362                     *fIndices.push() = fPrevUmbraIndex + 1;
   1363                 }
   1364             } else if (fPrevUmbraOutside) {
   1365                 // add tri
   1366                 *fIndices.push() = fPrevUmbraIndex;
   1367                 *fIndices.push() = currUmbraIndex;
   1368                 *fIndices.push() = fPrevUmbraIndex + 1;
   1369             }
   1370             fPrevUmbraOutside = isOutside;
   1371         }
   1372     }
   1373 
   1374     // add next penumbra point and quad
   1375     SkPoint newPoint = nextPoint + nextNormal;
   1376     *fPositions.push() = newPoint;
   1377     *fColors.push() = fPenumbraColor;
   1378 
   1379     if (!duplicate) {
   1380         *fIndices.push() = fPrevUmbraIndex;
   1381         *fIndices.push() = prevPenumbraIndex;
   1382         *fIndices.push() = currUmbraIndex;
   1383     }
   1384 
   1385     *fIndices.push() = prevPenumbraIndex;
   1386     *fIndices.push() = fPositions.count() - 1;
   1387     *fIndices.push() = currUmbraIndex;
   1388 
   1389     fPrevUmbraIndex = currUmbraIndex;
   1390     fPrevOutset = nextNormal;
   1391 }
   1392 
   1393 ///////////////////////////////////////////////////////////////////////////////////////////////////
   1394 
   1395 sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
   1396                                                    const SkPoint3& zPlane, bool transparent) {
   1397     SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
   1398     return ambientTess.releaseVertices();
   1399 }
   1400 
   1401 sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
   1402                                                 const SkPoint3& zPlane, const SkPoint3& lightPos,
   1403                                                 SkScalar lightRadius,  bool transparent) {
   1404     SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
   1405     return spotTess.releaseVertices();
   1406 }
   1407