Shadow fixes to avoid popping.

* Make geometric shadow generation more tolerant of nearly-convex paths.
* Ensure analytic and geometric shadow positions match with large blurs.

Bug: skia:
Change-Id: I8d3ba43b90d1520cb20f89de9f0b13d11a1a08c3
Reviewed-on: https://skia-review.googlesource.com/127045
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index a5fb6fc..ee833d7 100644
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -281,6 +281,16 @@
         this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
                                lightPos, kLightWidth, .5f);
 
+        // path ops bug
+        SkPath tmpClipPathBug;
+        tmpClipPathBug.addCircle(88.0344925f, 0, 60);
+        Op(fSquareRRectPath, tmpClipPathBug, kIntersect_SkPathOp, &tmpPath);
+
+        canvas->translate(250, 0);
+        zPlaneParams.fZ = SkTMax(1.0f, 32 + fZDelta);
+        this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
+                               lightPos, kLightWidth, .5f);
+
         // perspective paths
         SkPoint pivot = SkPoint::Make(fWideRectPath.getBounds().width()/2,
                                       fWideRectPath.getBounds().height()/2);
@@ -318,7 +328,7 @@
     }
 
     bool onAnimate(const SkAnimTimer& timer) override {
-        fAnimTranslate = timer.pingPong(30, 0, 200, -200);
+        fAnimTranslate = timer.pingPong(30, 0, 125, -125);
         fAnimAngle = timer.pingPong(15, 0, 0, 20);
         if (fDoAlphaAnimation) {
             fAnimAlpha = timer.pingPong(5, 0, 1, 0);
diff --git a/src/core/SkDrawShadowInfo.h b/src/core/SkDrawShadowInfo.h
index 47799e5..da63a08 100644
--- a/src/core/SkDrawShadowInfo.h
+++ b/src/core/SkDrawShadowInfo.h
@@ -47,7 +47,7 @@
                           SkScalar* blurRadius, SkScalar* scale, SkVector* translate) {
     SkScalar zRatio = SkTPin(occluderZ / (lightZ - occluderZ), 0.0f, 0.95f);
     *blurRadius = lightRadius*zRatio;
-    *scale = SkTMax(lightZ / (lightZ - occluderZ), 1.0f);
+    *scale = SkTPin(lightZ / (lightZ - occluderZ), 1.0f, 1.95f);
     *translate = SkVector::Make(-zRatio * lightX, -zRatio * lightY);
 }
 
diff --git a/src/utils/SkShadowTessellator.cpp b/src/utils/SkShadowTessellator.cpp
index 0bf1ada..4990db9 100755
--- a/src/utils/SkShadowTessellator.cpp
+++ b/src/utils/SkShadowTessellator.cpp
@@ -88,6 +88,7 @@
 
     bool                fSucceeded;
     bool                fTransparent;
+    bool                fIsConvex;
 
     SkColor             fUmbraColor;
     SkColor             fPenumbraColor;
@@ -128,12 +129,27 @@
     *n = steps;
 }
 
+static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
+    static constexpr SkScalar kClose = (SK_Scalar1 / 16);
+    static constexpr SkScalar kCloseSqd = kClose * kClose;
+
+    SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
+    return distSq < kCloseSqd;
+}
+
+static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
+    SkVector v0 = p1 - p0;
+    SkVector v1 = p2 - p0;
+    return v0.cross(v1);
+}
+
 SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
         : fZPlaneParams(zPlaneParams)
         , fZOffset(0)
         , fFirstVertexIndex(-1)
         , fSucceeded(false)
         , fTransparent(transparent)
+        , fIsConvex(true)
         , fDirection(1)
         , fPrevUmbraIndex(-1) {
     fInitPoints.setReserve(3);
@@ -398,9 +414,6 @@
         : INHERITED(zPlaneParams, transparent)
         , fSplitFirstEdge(false)
         , fSplitPreviousEdge(false) {
-    // TODO: support some concave paths
-    SkASSERT(path.isConvex());
-
     // Set base colors
     SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
     // umbraColor is the interior value, penumbraColor the exterior value.
@@ -454,12 +467,22 @@
             case SkPath::kDone_Verb:
                 break;
         }
+        // TODO: add support for concave paths
+        if (!fIsConvex) {
+            return;
+        }
     }
 
     if (!this->indexCount()) {
         return;
     }
 
+    // final convexity check
+    // TODO: add support for concave paths
+    if (fDirection*perp_dot(fInitPoints[1], fInitPoints[2], fFirstPoint) > 0) {
+        return;
+    }
+
     // Finish up
     SkVector normal;
     if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
@@ -560,6 +583,11 @@
 }
 
 void SkAmbientShadowTessellator::handleLine(const SkPoint& p)  {
+    // skip duplicate points
+    if (!fInitPoints.isEmpty() && duplicate_pt(p, fInitPoints[fInitPoints.count() - 1])) {
+        return;
+    }
+
     if (fInitPoints.count() < 2) {
         *fInitPoints.push() = p;
         return;
@@ -567,9 +595,7 @@
 
     if (fInitPoints.count() == 2) {
         // determine if cw or ccw
-        SkVector v0 = fInitPoints[1] - fInitPoints[0];
-        SkVector v1 = p - fInitPoints[0];
-        SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
+        SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
         if (SkScalarNearlyZero(perpDot)) {
             // nearly parallel, just treat as straight line and continue
             fInitPoints[1] = p;
@@ -614,6 +640,15 @@
 
         // to ensure we skip this block next time
         *fInitPoints.push() = p;
+    } else {
+        // reuse fInitPoints to track last three points
+        fInitPoints[0] = fInitPoints[1];
+        fInitPoints[1] = fInitPoints[2];
+        fInitPoints[2] = p;
+        // convexity check
+        if (fDirection*perp_dot(fInitPoints[0], fInitPoints[1], p) > 0) {
+            fIsConvex = false;
+        }
     }
 
     SkVector normal;
@@ -740,6 +775,7 @@
     void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
     bool addInnerPoint(const SkPoint& pathPoint);
     void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
+    void addToClip(const SkVector& nextPoint);
 
     SkScalar offset(SkScalar z) {
         float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
@@ -835,8 +871,11 @@
         return;
     }
 
+    // compute vectors for clip tests
+    this->computeClipVectorsAndTestCentroid();
+
     // check to see if umbra collapses
-    if (path.isConvex()) {
+    if (fIsConvex) {
         SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
                                                                           fPathPolygon[0],
                                                                           fPathPolygon[1]);
@@ -867,9 +906,6 @@
         }
     }
 
-    // compute vectors for clip tests
-    this->computeClipVectorsAndTestCentroid();
-
     if (ctm.hasPerspective()) {
         for (int i = 0; i < fPositions.count(); ++i) {
             SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
@@ -878,14 +914,16 @@
         }
     }
 
-    if (path.isConvex()) {
+    if (fIsConvex) {
         if (!this->computeConvexShadow(radius)) {
             return;
         }
     } else {
-        if (!this->computeConcaveShadow(radius)) {
-            return;
-        }
+        // For now
+        return;
+        //if (!this->computeConcaveShadow(radius)) {
+        //    return;
+        //}
     }
 
     if (ctm.hasPerspective()) {
@@ -919,6 +957,12 @@
     fSucceeded = true;
 }
 
+void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
+    if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count()-1])) {
+        *fClipPolygon.push() = point;
+    }
+}
+
 void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
                                                          const SkMatrix& shadowTransform) {
 
@@ -949,7 +993,7 @@
         switch (verb) {
             case SkPath::kLine_Verb:
                 ctm.mapPoints(&pts[1], 1);
-                *fClipPolygon.push() = pts[1];
+                this->addToClip(pts[1]);
                 this->INHERITED::handleLine(shadowTransform, &pts[1]);
                 break;
             case SkPath::kQuad_Verb:
@@ -957,8 +1001,8 @@
                 // point at t = 1/2
                 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
                 curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
-                *fClipPolygon.push() = curvePoint;
-                *fClipPolygon.push() = pts[2];
+                this->addToClip(curvePoint);
+                this->addToClip(pts[2]);
                 this->handleQuad(shadowTransform, pts);
                 break;
             case SkPath::kConic_Verb:
@@ -968,8 +1012,8 @@
                 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
                 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
                 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
-                *fClipPolygon.push() = curvePoint;
-                *fClipPolygon.push() = pts[2];
+                this->addToClip(curvePoint);
+                this->addToClip(pts[2]);
                 this->handleConic(shadowTransform, pts, w);
                 break;
             case SkPath::kCubic_Verb:
@@ -977,12 +1021,12 @@
                 // point at t = 5/16
                 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
                 curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
-                *fClipPolygon.push() = curvePoint;
+                this->addToClip(curvePoint);
                 // point at t = 11/16
                 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
                 curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
-                *fClipPolygon.push() = curvePoint;
-                *fClipPolygon.push() = pts[3];
+                this->addToClip(curvePoint);
+                this->addToClip(pts[3]);
                 this->handleCubic(shadowTransform, pts);
                 break;
             case SkPath::kMove_Verb:
@@ -1013,17 +1057,23 @@
 
     // init clip vectors
     SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
+    SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
+    SkScalar winding = v0.cross(v1);
     *fClipVectors.push() = v0;
 
     // init centroid check
     bool hiddenCentroid = true;
-    SkVector v1 = fCentroid - fClipPolygon[0];
+    v1 = fCentroid - fClipPolygon[0];
     SkScalar initCross = v0.cross(v1);
 
     for (int p = 1; p < fClipPolygon.count(); ++p) {
         // add to clip vectors
         v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
         *fClipVectors.push() = v0;
+        v1 = fClipPolygon[(p + 2) % fClipPolygon.count()] - fClipPolygon[p];
+        if (winding*v0.cross(v1) < 0) {
+            fIsConvex = false;
+        }
         // Determine if transformed centroid is inside clipPolygon.
         v1 = fCentroid - fClipPolygon[p];
         if (initCross*v0.cross(v1) <= 0) {
@@ -1343,20 +1393,6 @@
     }
 }
 
-static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
-    static constexpr SkScalar kClose = (SK_Scalar1 / 16);
-    static constexpr SkScalar kCloseSqd = kClose*kClose;
-
-    SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
-    return distSq < kCloseSqd;
-}
-
-static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
-    SkVector v0 = p1 - p0;
-    SkVector v1 = p2 - p0;
-    return v0.cross(v1);
-}
-
 static bool is_collinear(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
     return (SkScalarNearlyZero(perp_dot(p0, p1, p2)));
 }
@@ -1534,7 +1570,7 @@
 
 sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
                                                    const SkPoint3& zPlane, bool transparent) {
-    if (!path.isFinite() || !path.isConvex() || !ctm.isFinite() || !zPlane.isFinite()) {
+    if (!path.isFinite() || !ctm.isFinite() || !zPlane.isFinite()) {
         return nullptr;
     }
     SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
@@ -1544,7 +1580,7 @@
 sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
                                                 const SkPoint3& zPlane, const SkPoint3& lightPos,
                                                 SkScalar lightRadius,  bool transparent) {
-    if (!path.isFinite() || !path.isConvex() || !ctm.isFinite() || !zPlane.isFinite() ||
+    if (!path.isFinite() || !ctm.isFinite() || !zPlane.isFinite() ||
         !lightPos.isFinite() || !SkScalarIsFinite(lightRadius) || !(lightRadius > 0)) {
         return nullptr;
     }