Add alternative spot shadow to Android shadow sample

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2246273003

Review-Url: https://codereview.chromium.org/2256713002
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index 38fcd20..41af384 100755
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -25,15 +25,15 @@
     SkPoint3  fLightPos;
 
     bool      fShowAmbient;
-    bool      fUseAltAmbient;
     bool      fShowSpot;
+    bool      fUseAlt;
     bool      fShowObject;
 
 public:
     ShadowsView() 
         : fShowAmbient(true)
-        , fUseAltAmbient(true)
         , fShowSpot(true)
+        , fUseAlt(true)
         , fShowObject(true) {}
 
 protected:
@@ -41,7 +41,7 @@
         fCirclePath.addCircle(0, 0, 50);
         fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
         fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
-        fLightPos = SkPoint3::Make(-220, -330, 150);
+        fLightPos = SkPoint3::Make(-2, -2, 6);
     }
 
     // overrides from SkEventSink
@@ -57,12 +57,12 @@
                 case 'B':
                     fShowAmbient = !fShowAmbient;
                     break;
-                case 'T':
-                    fUseAltAmbient = !fUseAltAmbient;
-                    break;
                 case 'S':
                     fShowSpot = !fShowSpot;
                     break;
+                case 'T':
+                    fUseAlt = !fUseAlt;
+                    break;
                 case 'O':
                     fShowObject = !fShowObject;
                     break;
@@ -143,64 +143,48 @@
             return;
         }
 
+        SkRect pathRect;
+        SkRRect pathRRect;
+        if ((!path.isOval(&pathRect) || pathRect.width() != pathRect.height()) &&
+            (!path.isRRect(&pathRRect) || !pathRRect.allCornersCircular()) &&
+            !path.isRect(&pathRect)) {
+            this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
+            return;
+        }
+
         const SkScalar kHeightFactor = 1.f / 128.f;
         const SkScalar kGeomFactor = 64;
 
         SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
         SkScalar radius = zValue*kHeightFactor*kGeomFactor;
 
-        // fast path
-        SkRect pathRect;
-        SkRRect pathRRect;
-        if ((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
-            (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
-            path.isRect(&pathRect)) {
-
-            // For all of these, we outset the rect by half the radius to get our stroke shape.
-            if (path.isOval(nullptr)) {
-                pathRect.outset(0.5f*radius, 0.5f*radius);
-                pathRRect = SkRRect::MakeOval(pathRect);
-            } else if (path.isRect(nullptr)) {
-                pathRect.outset(0.5f*radius, 0.5f*radius);
-                pathRRect = SkRRect::MakeRectXY(pathRect, 0.5f*radius, 0.5f*radius);
-            } else {
-                pathRRect.outset(0.5f*radius, 0.5f*radius);
-            }
-
-            SkPaint paint;
-            paint.setAntiAlias(true);
-            paint.setColor(SkColorSetARGB((unsigned char)(ambientAlpha*umbraAlpha*255.999f), 
-                                          0, 0, 0));
-            paint.setStrokeWidth(radius);
-            paint.setStyle(SkPaint::kStroke_Style);
-
-            paint.setShader(SkGaussianEdgeShader::Make());
-            canvas->drawRRect(pathRRect, paint);
+        // For all of these, we outset the rect by the radius to get our coverage shape.
+        if (path.isOval(nullptr)) {
+            pathRect.outset(radius, radius);
+            pathRRect = SkRRect::MakeOval(pathRect);
+        } else if (path.isRect(nullptr)) {
+            pathRect.outset(radius, radius);
+            pathRRect = SkRRect::MakeRectXY(pathRect, radius, radius);
         } else {
-            // occlude blur
-            SkRect occlRect;
-            GetOcclRect(path, &occlRect);
-            sk_sp<SkMaskFilter> f = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
-                                                           SkBlurMask::ConvertRadiusToSigma(radius),
-                                                           occlRect,
-                                                           SkBlurMaskFilter::kNone_BlurFlag);
-
-            SkPaint paint;
-            paint.setAntiAlias(true);
-            paint.setMaskFilter(std::move(f));
-            paint.setColor(SkColorSetARGB((unsigned char)(ambientAlpha*umbraAlpha*255.999f), 
-                                          0, 0, 0));
-            canvas->drawPath(path, paint);
-
-            // draw occlusion rect
-#if DRAW_OCCL_RECT
-            SkPaint stroke;
-            stroke.setStyle(SkPaint::kStroke_Style);
-            stroke.setColor(SK_ColorBLUE);
-            canvas->drawRect(occlRect, stroke);
-#endif
+            pathRRect.outset(radius, radius);
         }
 
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        // handle scale of radius due to CTM
+        SkScalar maxScale = canvas->getTotalMatrix().getMaxScale();
+        radius *= maxScale;
+        unsigned char gray = (unsigned char)(ambientAlpha*umbraAlpha*255.999f);
+        SkASSERT(radius < 256);
+        // convert the radius to fixed point 8.8 and
+        // place it in the G,B components of the color
+        unsigned char intPart = (unsigned char)radius;
+        SkScalar fracPart = radius - intPart;
+        paint.setColor(SkColorSetARGB(1, gray, intPart, (unsigned char)(fracPart*256.f)));
+
+        sk_sp<SkShader> gaussShader = std::move(SkGaussianEdgeShader::Make());
+        paint.setShader(gaussShader);
+        canvas->drawRRect(pathRRect, paint);
     }
 
     void drawSpotShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
@@ -235,10 +219,19 @@
         GetOcclRect(path, &occlRect);
         // apply inverse transform
         occlRect.offset(-offset);
+#if 0
+        // It looks like the scale may be invalid
+        SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
+        if (scale < 1.0f) {
+            scale = 1.0f;
+        } else if (scale > 1024.f) {
+            scale = 1024.f;
+        }
         occlRect.fLeft /= scale;
         occlRect.fRight /= scale;
         occlRect.fTop /= scale;
         occlRect.fBottom /= scale;
+#endif
         sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
                                                         SkBlurMask::ConvertRadiusToSigma(radius),
                                                         occlRect,
@@ -251,7 +244,10 @@
 
         // apply transformation to shadow
         canvas->translate(offset.fX, offset.fY);
+#if 0
+        // It looks like the scale may be invalid
         canvas->scale(scale, scale);
+#endif
         canvas->drawPath(path, paint);
 
         // draw occlusion rect
@@ -263,21 +259,93 @@
 #endif
     }
 
-    void drawShadowedPath(SkCanvas* canvas, const SkPath& path, SkScalar zValue, 
-                          const SkPaint& paint) {
-        const SkScalar kLightWidth = 3;
-        const SkScalar kAmbientAlpha = 0.25f;
-        const SkScalar kSpotAlpha = 0.25f;
+    void drawSpotShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
+                        SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
+        if (spotAlpha <= 0) {
+            return;
+        }
 
+        SkRect pathRect;
+        SkRRect pathRRect;
+        if ((!path.isOval(&pathRect) || pathRect.width() != pathRect.height()) &&
+            (!path.isRRect(&pathRRect) || !pathRRect.allCornersCircular()) &&
+            !path.isRect(&pathRect)) {
+            this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
+            return;
+        }
+
+        SkScalar zRatio = zValue / (lightPos.fZ - zValue);
+        if (zRatio < 0.0f) {
+            zRatio = 0.0f;
+        } else if (zRatio > 0.95f) {
+            zRatio = 0.95f;
+        }
+        SkScalar radius = lightWidth*zRatio;
+
+        // For all of these, we outset the rect by the radius to get our coverage shape.
+        if (path.isOval(nullptr)) {
+            pathRect.outset(radius, radius);
+            pathRRect = SkRRect::MakeOval(pathRect);
+        } else if (path.isRect(nullptr)) {
+            pathRect.outset(radius, radius);
+            pathRRect = SkRRect::MakeRectXY(pathRect, radius, radius);
+        } else {
+            pathRRect.outset(radius, radius);
+        }
+
+        // compute the transformation params
+        SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
+        canvas->getTotalMatrix().mapPoints(&center, 1);
+        SkPoint offset = SkPoint::Make(-zRatio*(lightPos.fX - center.fX),
+                                       -zRatio*(lightPos.fY - center.fY));
+        SkAutoCanvasRestore acr(canvas, true);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        sk_sp<SkShader> gaussShader = std::move(SkGaussianEdgeShader::Make());
+        paint.setShader(gaussShader);
+        // handle scale of radius due to CTM
+        SkScalar maxScale = canvas->getTotalMatrix().getMaxScale();
+        radius *= maxScale;
+        unsigned char gray = (unsigned char)(spotAlpha*255.999f);
+        SkASSERT(radius < 256);
+        // convert the radius to fixed point 8.8 and
+        // place it in the G,B components of the color
+        unsigned char intPart = (unsigned char)radius;
+        SkScalar fracPart = radius - intPart;
+        paint.setColor(SkColorSetARGB(1, gray, intPart, (unsigned char)(fracPart*256.f)));
+
+        // apply transformation to shadow
+        canvas->translate(offset.fX, offset.fY);
+#if 0
+        // It looks like the scale may be invalid
+        SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
+        if (scale < 1.0f) {
+            scale = 1.0f;
+        } else if (scale > 1024.f) {
+            scale = 1024.f;
+        }
+        canvas->scale(scale, scale);
+#endif
+        canvas->drawRRect(pathRRect, paint);
+    }
+
+    void drawShadowedPath(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
+                          const SkPaint& paint, SkScalar ambientAlpha,
+                          const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
         if (fShowAmbient) {
-            if (fUseAltAmbient) {
-                this->drawAmbientShadowAlt(canvas, path, zValue, kAmbientAlpha);
+            if (fUseAlt) {
+                this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
             } else {
-                this->drawAmbientShadow(canvas, path, zValue, kAmbientAlpha);
+                this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
             }
         }
         if (fShowSpot) {
-            this->drawSpotShadow(canvas, path, zValue, fLightPos, kLightWidth, kSpotAlpha);
+            if (fUseAlt) {
+                this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
+            } else {
+                this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
+            }
         }
         if (fShowObject) {
             canvas->drawPath(path, paint);
@@ -286,25 +354,40 @@
 
     void onDrawContent(SkCanvas* canvas) override {
         this->drawBG(canvas);
+        const SkScalar kLightWidth = 3;
+        const SkScalar kAmbientAlpha = 0.25f;
+        const SkScalar kSpotAlpha = 0.25f;
 
         SkPaint paint;
         paint.setAntiAlias(true);
 
+        SkPoint3 lightPos = fLightPos;
+
         paint.setColor(SK_ColorWHITE);
         canvas->translate(200, 90);
-        this->drawShadowedPath(canvas, fRectPath, 5, paint);
+        lightPos.fX += 200;
+        lightPos.fY += 90;
+        this->drawShadowedPath(canvas, fRectPath, 5, paint, kAmbientAlpha, 
+                               lightPos, kLightWidth, kSpotAlpha);
 
         paint.setColor(SK_ColorRED);
         canvas->translate(250, 0);
-        this->drawShadowedPath(canvas, fRRPath, 5, paint);
+        lightPos.fX += 250;
+        this->drawShadowedPath(canvas, fRRPath, 5, paint, kAmbientAlpha,
+                               lightPos, kLightWidth, kSpotAlpha);
 
         paint.setColor(SK_ColorBLUE);
         canvas->translate(-250, 110);
-        this->drawShadowedPath(canvas, fCirclePath, 5, paint);
+        lightPos.fX -= 250;
+        lightPos.fY += 110;
+        this->drawShadowedPath(canvas, fCirclePath, 5, paint, 0.0f,
+                               lightPos, kLightWidth, 0.5f);
 
         paint.setColor(SK_ColorGREEN);
         canvas->translate(250, 0);
-        this->drawShadowedPath(canvas, fRRPath, 5, paint);
+        lightPos.fX += 250;
+        this->drawShadowedPath(canvas, fRRPath, 5, paint, kAmbientAlpha,
+                               lightPos, kLightWidth, kSpotAlpha);
     }
 
 protected:
diff --git a/src/effects/SkGaussianEdgeShader.cpp b/src/effects/SkGaussianEdgeShader.cpp
index e519598..1990fbf 100644
--- a/src/effects/SkGaussianEdgeShader.cpp
+++ b/src/effects/SkGaussianEdgeShader.cpp
@@ -11,6 +11,10 @@
 
  /** \class SkGaussianEdgeShaderImpl
  This subclass of shader applies a Gaussian to shadow edge
+
+ The radius of the Gaussian blur is specified by the g and b values of the color,
+ where g is the integer component and b is the fractional component. The r value
+ represents the max final alpha.
  */
 class SkGaussianEdgeShaderImpl : public SkShader {
 public:
@@ -65,20 +69,13 @@
 
             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
 
-            fragBuilder->codeAppendf("vec4 output = %s;", args.fInputColor);
-            // outside the outer edge
-            fragBuilder->codeAppendf("if (%s.z <= 0) {", fragBuilder->distanceVectorName());
-            fragBuilder->codeAppend("output *= 0.0;");
-            // inside the stroke
-            fragBuilder->codeAppendf("} else if (%s.w > 0) {", fragBuilder->distanceVectorName());
-            fragBuilder->codeAppendf("float factor = %s.w/(%s.z + %s.w);",
-                                     fragBuilder->distanceVectorName(),
-                                     fragBuilder->distanceVectorName(),
+            fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
+            fragBuilder->codeAppend("float radius = color.g*255.0 + color.b;");
+
+            fragBuilder->codeAppendf("float factor = 1.0 - clamp(%s.z/radius, 0.0, 1.0);",
                                      fragBuilder->distanceVectorName());
             fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
-            fragBuilder->codeAppend("output *= factor;");
-            fragBuilder->codeAppend("}");
-            fragBuilder->codeAppendf("%s = output;", args.fOutputColor);
+            fragBuilder->codeAppendf("%s = factor*vec4(0.0, 0.0, 0.0, color.r);", args.fOutputColor);
         }
 
         static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,