Update SampleAndroidShadows to use algorithm closer to Android OpenGL

Includes:
* Update light position to be at a similar distance to Android OS
* Scale spot shadows correctly
* Compute stroke shapes and radii correctly
* Allow for larger blur radius for shadows

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2319003003
TBR=reed@google.com
NOTRY=true
NOTREECHECKS=true

Review-Url: https://codereview.chromium.org/2319003003
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index f411e5c..e14c571 100755
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -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(-2, -2, 6);
+        fLightPos = SkPoint3::Make(-700, -700, 2800);
     }
 
     // overrides from SkEventSink
@@ -148,10 +148,16 @@
 
         SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
         SkScalar radius = zValue*kHeightFactor*kGeomFactor;
+        // distance to outer of edge of geometry from original shape edge
+        SkScalar offset = radius*umbraAlpha;
 
         SkRect pathRect;
         SkRRect pathRRect;
-        if (radius >= 64 ||
+        SkScalar scaleFactors[2];
+        if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
+            return;
+        }
+        if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 64 ||
             !((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
               (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
               path.isRect(&pathRect))) {
@@ -159,33 +165,41 @@
             return;
         }
 
-        // For all of these, we outset the rect by half the radius to get our stroke shape.
-        SkScalar halfRadius = SK_ScalarHalf*radius;
+        // For all of these, we inset the offset rect by half the radius to get our stroke shape.
+        SkScalar strokeOutset = offset - SK_ScalarHalf*radius;
+        // Make sure we'll have a radius of at least 0.5 after xform
+        if (strokeOutset*scaleFactors[0] < 0.5f) {
+            strokeOutset = 0.5f / scaleFactors[0];
+        }
         if (path.isOval(nullptr)) {
-            pathRect.outset(halfRadius, halfRadius);
+            pathRect.outset(strokeOutset, strokeOutset);
             pathRRect = SkRRect::MakeOval(pathRect);
         } else if (path.isRect(nullptr)) {
-            pathRect.outset(halfRadius, halfRadius);
-            pathRRect = SkRRect::MakeRectXY(pathRect, halfRadius, halfRadius);
+            pathRect.outset(strokeOutset, strokeOutset);
+            pathRRect = SkRRect::MakeRectXY(pathRect, strokeOutset, strokeOutset);
         } else {
-            pathRRect.outset(halfRadius, halfRadius);
+            pathRRect.outset(strokeOutset, strokeOutset);
         }
 
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setStyle(SkPaint::kStroke_Style);
         // we outset the stroke a little to cover up AA on the interior edge
-        paint.setStrokeWidth(radius + 1);
-        // 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 < 64);
-        // Convert radius to 6.2 fixed point and place in the G component.
-        paint.setColor(SkColorSetARGB(1, gray, (unsigned char)(4.0f*radius), 0));
+        SkScalar pad = 0.5f;
+        paint.setStrokeWidth(radius + 2*pad);
+        // handle scale of radius and pad due to CTM
+        radius *= scaleFactors[0];
+        pad *= scaleFactors[0];
+        SkASSERT(radius < 16384);
+        SkASSERT(pad < 64);
+        // Convert radius to 14.2 fixed point and place in the R & G components.
+        // Convert pad to 6.2 fixed point and place in the B component.
+        uint16_t iRadius = (uint16_t)(radius*4.0f);
+        unsigned char alpha = (unsigned char)(ambientAlpha*255.999f);
+        paint.setColor(SkColorSetARGB(alpha, iRadius >> 8, iRadius & 0xff,
+                                      (unsigned char)(4.0f*pad)));
 
-        sk_sp<SkShader> gaussShader = SkGaussianEdgeShader::Make();
-        paint.setShader(gaussShader);
+        paint.setShader(SkGaussianEdgeShader::Make(true));
         canvas->drawRRect(pathRRect, paint);
     }
 
@@ -201,42 +215,24 @@
         } else if (zRatio > 0.95f) {
             zRatio = 0.95f;
         }
-        SkScalar radius = lightWidth*zRatio;
+        SkScalar blurRadius = lightWidth*zRatio;
 
         // 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));
-        SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
-        if (scale < 1.0f) {
-            scale = 1.0f;
-        } else if (scale > 1024.f) {
-            scale = 1024.f;
+        SkMatrix ctmInverse;
+        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+            return;
         }
+        SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
+        ctmInverse.mapPoints(&lightPos2D, 1);
+        SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+                                       zRatio*(center.fY - lightPos2D.fY));
+        SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
 
         SkAutoCanvasRestore acr(canvas, true);
 
-        SkRect occlRect;
-        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,
+                                                        SkBlurMask::ConvertRadiusToSigma(blurRadius),
                                                         SkBlurMaskFilter::kNone_BlurFlag);
 
         SkPaint paint;
@@ -245,20 +241,9 @@
         paint.setColor(SkColorSetARGB((unsigned char)(spotAlpha*255.999f), 0, 0, 0));
 
         // 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->translate(offset.fX, offset.fY);
         canvas->drawPath(path, paint);
-
-        // draw occlusion rect
-#if DRAW_OCCL_RECT
-        SkPaint stroke;
-        stroke.setStyle(SkPaint::kStroke_Style);
-        stroke.setColor(SK_ColorRED);
-        canvas->drawRect(occlRect, stroke)
-#endif
     }
 
     void drawSpotShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
@@ -273,11 +258,15 @@
         } else if (zRatio > 0.95f) {
             zRatio = 0.95f;
         }
-        SkScalar radius = lightWidth*zRatio;
+        SkScalar radius = 2.0f*lightWidth*zRatio;
 
         SkRect pathRect;
         SkRRect pathRRect;
-        if (radius >= 64 ||
+        SkScalar scaleFactors[2];
+        if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
+            return;
+        }
+        if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 16384 ||
             !((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
               (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
               path.isRect(&pathRect))) {
@@ -285,23 +274,31 @@
             return;
         }
 
-        // For all of these, we outset the rect by half the radius to get our stroke shape.
-        SkScalar halfRadius = SK_ScalarHalf*radius;
+        // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
+        SkScalar minRadius = SK_ScalarHalf/scaleFactors[0];
         if (path.isOval(nullptr)) {
-            pathRect.outset(halfRadius, halfRadius);
             pathRRect = SkRRect::MakeOval(pathRect);
         } else if (path.isRect(nullptr)) {
-            pathRect.outset(halfRadius, halfRadius);
-            pathRRect = SkRRect::MakeRectXY(pathRect, halfRadius, halfRadius);
+            pathRRect = SkRRect::MakeRectXY(pathRect, minRadius, minRadius);
         } else {
-            pathRRect.outset(halfRadius, halfRadius);
+            if (pathRRect.getSimpleRadii().fX < minRadius) {
+                pathRRect.setRectXY(pathRRect.rect(), minRadius, minRadius);
+            }
         }
 
-        // 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));
+        // compute the scale and translation for the shadow
+        SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
+        SkRRect shadowRRect;
+        pathRRect.transform(SkMatrix::MakeScale(scale, scale), &shadowRRect);
+        SkPoint center = SkPoint::Make(shadowRRect.rect().centerX(), shadowRRect.rect().centerY());
+        SkMatrix ctmInverse;
+        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+            return;
+        }
+        SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
+        ctmInverse.mapPoints(&lightPos2D, 1);
+        SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+                                       zRatio*(center.fY - lightPos2D.fY));
         SkAutoCanvasRestore acr(canvas, true);
 
         SkPaint paint;
@@ -310,9 +307,9 @@
         // the edge of the shape. We also add 1/2 to cover up AA on the interior edge.
         SkScalar pad = offset.length() + 0.5f;
         // compute area
-        SkScalar strokeWidth = radius + 2.0f*pad;
-        SkScalar strokedArea = 2.0f*strokeWidth*(pathRRect.width() + pathRRect.height());
-        SkScalar filledArea = (pathRRect.height() + radius)*(pathRRect.width() + radius);
+        SkScalar strokeWidth = radius + 2.0f*pad/scaleFactors[0];
+        SkScalar strokedArea = 2.0f*strokeWidth*(shadowRRect.width() + shadowRRect.height());
+        SkScalar filledArea = (shadowRRect.height() + radius)*(shadowRRect.width() + radius);
         // If the area of the stroked geometry is larger than the fill geometry, or
         // if our pad is too big to convert to 6.2 fixed point, just fill it.
         if (strokedArea > filledArea || pad >= 64) {
@@ -323,31 +320,22 @@
             paint.setStyle(SkPaint::kStroke_Style);
             paint.setStrokeWidth(strokeWidth);
         }
-        sk_sp<SkShader> gaussShader = SkGaussianEdgeShader::Make();
-        paint.setShader(gaussShader);
+        paint.setShader(SkGaussianEdgeShader::Make(true));
         // handle scale of radius due to CTM
-        SkScalar maxScale = canvas->getTotalMatrix().getMaxScale();
-        radius *= maxScale;
-        unsigned char gray = (unsigned char)(spotAlpha*255.999f);
-        SkASSERT(radius < 64);
+        radius *= scaleFactors[0];
+        // don't need to scale pad as it was computed from the transformed offset
+        SkASSERT(radius < 16384);
         SkASSERT(pad < 64);
-        // Convert radius and pad to 6.2 fixed point and place in the G & B components.
-        paint.setColor(SkColorSetARGB(1, gray, (unsigned char)(radius*4.0f),
-                                     (unsigned char)(pad*4.0f)));
+        // Convert radius to 14.2 fixed point and place in the R & G components.
+        // Convert pad to 6.2 fixed point and place in the B component.
+        uint16_t iRadius = (uint16_t)(radius*4.0f);
+        unsigned char alpha = (unsigned char)(spotAlpha*255.999f);
+        paint.setColor(SkColorSetARGB(alpha, iRadius >> 8, iRadius & 0xff,
+                                      (unsigned char)(4.0f*pad)));
 
         // 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);
+        canvas->drawRRect(shadowRRect, paint);
     }
 
     void drawShadowedPath(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
@@ -374,7 +362,7 @@
 
     void onDrawContent(SkCanvas* canvas) override {
         this->drawBG(canvas);
-        const SkScalar kLightWidth = 3;
+        const SkScalar kLightWidth = 2800;
         const SkScalar kAmbientAlpha = 0.25f;
         const SkScalar kSpotAlpha = 0.25f;
 
@@ -387,26 +375,26 @@
         canvas->translate(200, 90);
         lightPos.fX += 200;
         lightPos.fY += 90;
-        this->drawShadowedPath(canvas, fRectPath, 5, paint, kAmbientAlpha, 
+        this->drawShadowedPath(canvas, fRectPath, 2, paint, kAmbientAlpha, 
                                lightPos, kLightWidth, kSpotAlpha);
 
         paint.setColor(SK_ColorRED);
         canvas->translate(250, 0);
         lightPos.fX += 250;
-        this->drawShadowedPath(canvas, fRRPath, 5, paint, kAmbientAlpha,
+        this->drawShadowedPath(canvas, fRRPath, 4, paint, kAmbientAlpha,
                                lightPos, kLightWidth, kSpotAlpha);
 
         paint.setColor(SK_ColorBLUE);
         canvas->translate(-250, 110);
         lightPos.fX -= 250;
         lightPos.fY += 110;
-        this->drawShadowedPath(canvas, fCirclePath, 5, paint, 0.0f,
+        this->drawShadowedPath(canvas, fCirclePath, 8, paint, 0.0f,
                                lightPos, kLightWidth, 0.5f);
 
         paint.setColor(SK_ColorGREEN);
         canvas->translate(250, 0);
         lightPos.fX += 250;
-        this->drawShadowedPath(canvas, fRRPath, 5, paint, kAmbientAlpha,
+        this->drawShadowedPath(canvas, fRRPath, 64, paint, kAmbientAlpha,
                                lightPos, kLightWidth, kSpotAlpha);
     }