Use stroked rrects for Android shadow sample
authorjvanverth <jvanverth@google.com>
Mon, 29 Aug 2016 14:17:47 +0000 (07:17 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 29 Aug 2016 14:17:47 +0000 (07:17 -0700)
Changes the Android shadow sample to use stroked roundrects when
we can (mainly if stroked geometry area < fill geometry area).

Also changes the setup for the overstroke geometry so that it computes
the correct distance to the outer edge.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2283003003

Review-Url: https://codereview.chromium.org/2283003003

samplecode/SampleAndroidShadows.cpp
src/effects/SkGaussianEdgeShader.cpp
src/gpu/GrOvalRenderer.cpp

index c7a2f63..f411e5c 100755 (executable)
@@ -143,44 +143,46 @@ protected:
             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;
 
-        // For all of these, we outset the rect by the radius to get our coverage shape.
+        SkRect pathRect;
+        SkRRect pathRRect;
+        if (radius >= 64 ||
+            !((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
+              (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
+              path.isRect(&pathRect))) {
+            this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
+            return;
+        }
+
+        // For all of these, we outset the rect by half the radius to get our stroke shape.
+        SkScalar halfRadius = SK_ScalarHalf*radius;
         if (path.isOval(nullptr)) {
-            pathRect.outset(radius, radius);
+            pathRect.outset(halfRadius, halfRadius);
             pathRRect = SkRRect::MakeOval(pathRect);
         } else if (path.isRect(nullptr)) {
-            pathRect.outset(radius, radius);
-            pathRRect = SkRRect::MakeRectXY(pathRect, radius, radius);
+            pathRect.outset(halfRadius, halfRadius);
+            pathRRect = SkRRect::MakeRectXY(pathRect, halfRadius, halfRadius);
         } else {
-            pathRRect.outset(radius, radius);
+            pathRRect.outset(halfRadius, halfRadius);
         }
 
         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 < 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)));
+        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));
 
         sk_sp<SkShader> gaussShader = SkGaussianEdgeShader::Make();
         paint.setShader(gaussShader);
@@ -265,15 +267,6 @@ protected:
             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;
@@ -282,15 +275,26 @@ protected:
         }
         SkScalar radius = lightWidth*zRatio;
 
-        // For all of these, we outset the rect by the radius to get our coverage shape.
+        SkRect pathRect;
+        SkRRect pathRRect;
+        if (radius >= 64 ||
+            !((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
+              (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
+              path.isRect(&pathRect))) {
+            this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
+            return;
+        }
+
+        // For all of these, we outset the rect by half the radius to get our stroke shape.
+        SkScalar halfRadius = SK_ScalarHalf*radius;
         if (path.isOval(nullptr)) {
-            pathRect.outset(radius, radius);
+            pathRect.outset(halfRadius, halfRadius);
             pathRRect = SkRRect::MakeOval(pathRect);
         } else if (path.isRect(nullptr)) {
-            pathRect.outset(radius, radius);
-            pathRRect = SkRRect::MakeRectXY(pathRect, radius, radius);
+            pathRect.outset(halfRadius, halfRadius);
+            pathRRect = SkRRect::MakeRectXY(pathRect, halfRadius, halfRadius);
         } else {
-            pathRRect.outset(radius, radius);
+            pathRRect.outset(halfRadius, halfRadius);
         }
 
         // compute the transformation params
@@ -302,18 +306,34 @@ protected:
 
         SkPaint paint;
         paint.setAntiAlias(true);
+        // We outset the stroke by the length of the translation so the shadow extends to
+        // 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);
+        // 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) {
+            pad = 0;
+            paint.setStyle(SkPaint::kStrokeAndFill_Style);
+            paint.setStrokeWidth(radius);
+        } else {
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(strokeWidth);
+        }
         sk_sp<SkShader> gaussShader = 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)));
+        SkASSERT(radius < 64);
+        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)));
 
         // apply transformation to shadow
         canvas->translate(offset.fX, offset.fY);
index 1990fbf..d73bfad 100644 (file)
  /** \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.
+ The radius of the Gaussian blur is specified by the g value of the color, in 6.2 fixed point.
+ For spot shadows, we increase the stroke width to set the shadow against the shape. This pad
+ is specified by b, also in 6.2 fixed point. The r value represents the max final alpha.
+ The incoming alpha should be 1.
  */
 class SkGaussianEdgeShaderImpl : public SkShader {
 public:
@@ -70,9 +71,10 @@ public:
             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
 
             fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
-            fragBuilder->codeAppend("float radius = color.g*255.0 + color.b;");
+            fragBuilder->codeAppend("float radius = color.g*64.0;");
+            fragBuilder->codeAppend("float pad = color.b*64.0;");
 
-            fragBuilder->codeAppendf("float factor = 1.0 - clamp(%s.z/radius, 0.0, 1.0);",
+            fragBuilder->codeAppendf("float factor = 1.0 - clamp((%s.z - pad)/radius, 0.0, 1.0);",
                                      fragBuilder->distanceVectorName());
             fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
             fragBuilder->codeAppendf("%s = factor*vec4(0.0, 0.0, 0.0, color.r);", args.fOutputColor);
index e5eb665..b8c3d3e 100644 (file)
@@ -1526,7 +1526,7 @@ private:
         }
 
         // Setup geometry processor
-        SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(kStroke_RRectType == fType,
+        SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(kFill_RRectType != fType,
                                                                          false, false,
                                                                          false, localMatrix));
 
@@ -1612,73 +1612,77 @@ private:
                 verts++;
             }
             // Add the additional vertices for overstroked rrects.
-            // Effectively this is an additional rrect, drawn inside out,
-            // with outerRadius == -innerRadius. This will give us correct AA in the center.
+            // Effectively this is an additional stroked rrect, with its
+            // outer radius = outerRadius - innerRadius, and inner radius = 0.
+            // This will give us correct AA in the center and the correct
+            // distance to the outer edge.
             //
-            // Note that args.fInnerRadius is negative in this case.
-            // Also, the offset is a constant vector pointing to the right, which guarantees
-            // that the distance value along the inner rectangle is constant, which
-            // is what we want to get nice anti-aliasing.
+            // Also, the outer offset is a constant vector pointing to the right, which
+            // guarantees that the distance value along the outer rectangle is constant.
             if (kOverstroke_RRectType == fType) {
+                SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
+                // this is the normalized distance from the outer rectangle of this
+                // geometry to the outer edge
+                SkScalar maxOffset = -args.fInnerRadius/overstrokeOuterRadius;
+
                 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[1]);
                 verts->fColor = color;
-                verts->fOffset = SkPoint::Make(0, 0);
-                verts->fOuterRadius = -args.fInnerRadius;
-                verts->fInnerRadius = innerRadius;
+                verts->fOffset = SkPoint::Make(maxOffset, 0);
+                verts->fOuterRadius = overstrokeOuterRadius;
+                verts->fInnerRadius = 0;
                 verts++;
 
                 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[1]);
                 verts->fColor = color;
-                verts->fOffset = SkPoint::Make(0, 0);
-                verts->fOuterRadius = -args.fInnerRadius;
-                verts->fInnerRadius = innerRadius;
+                verts->fOffset = SkPoint::Make(maxOffset, 0);
+                verts->fOuterRadius = overstrokeOuterRadius;
+                verts->fInnerRadius = 0;
                 verts++;
 
-                SkScalar inset = outerRadius - args.fInnerRadius;
-                verts->fPos = SkPoint::Make(bounds.fLeft + inset,
-                                            bounds.fTop + inset);
+                verts->fPos = SkPoint::Make(bounds.fLeft + overstrokeOuterRadius,
+                                            bounds.fTop + overstrokeOuterRadius);
                 verts->fColor = color;
-                verts->fOffset = SkPoint::Make(1, 0);
-                verts->fOuterRadius = -args.fInnerRadius;
-                verts->fInnerRadius = innerRadius;
+                verts->fOffset = SkPoint::Make(0, 0);
+                verts->fOuterRadius = overstrokeOuterRadius;
+                verts->fInnerRadius = 0;
                 verts++;
 
-                verts->fPos = SkPoint::Make(bounds.fRight - inset,
-                                            bounds.fTop + inset);
+                verts->fPos = SkPoint::Make(bounds.fRight - overstrokeOuterRadius,
+                                            bounds.fTop + overstrokeOuterRadius);
                 verts->fColor = color;
-                verts->fOffset = SkPoint::Make(1, 0);
-                verts->fOuterRadius = -args.fInnerRadius;
-                verts->fInnerRadius = innerRadius;
+                verts->fOffset = SkPoint::Make(0, 0);
+                verts->fOuterRadius = overstrokeOuterRadius;
+                verts->fInnerRadius = 0;
                 verts++;
 
-                verts->fPos = SkPoint::Make(bounds.fLeft + inset,
-                                            bounds.fBottom - inset);
+                verts->fPos = SkPoint::Make(bounds.fLeft + overstrokeOuterRadius,
+                                            bounds.fBottom - overstrokeOuterRadius);
                 verts->fColor = color;
-                verts->fOffset = SkPoint::Make(1, 0);
-                verts->fOuterRadius = -args.fInnerRadius;
-                verts->fInnerRadius = innerRadius;
+                verts->fOffset = SkPoint::Make(0, 0);
+                verts->fOuterRadius = overstrokeOuterRadius;
+                verts->fInnerRadius = 0;
                 verts++;
 
-                verts->fPos = SkPoint::Make(bounds.fRight - inset,
-                                            bounds.fBottom - inset);
+                verts->fPos = SkPoint::Make(bounds.fRight - overstrokeOuterRadius,
+                                            bounds.fBottom - overstrokeOuterRadius);
                 verts->fColor = color;
-                verts->fOffset = SkPoint::Make(1, 0);
-                verts->fOuterRadius = -args.fInnerRadius;
-                verts->fInnerRadius = innerRadius;
+                verts->fOffset = SkPoint::Make(0, 0);
+                verts->fOuterRadius = overstrokeOuterRadius;
+                verts->fInnerRadius = 0;
                 verts++;
 
                 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[2]);
                 verts->fColor = color;
-                verts->fOffset = SkPoint::Make(0, 0);
-                verts->fOuterRadius = -args.fInnerRadius;
-                verts->fInnerRadius = innerRadius;
+                verts->fOffset = SkPoint::Make(maxOffset, 0);
+                verts->fOuterRadius = overstrokeOuterRadius;
+                verts->fInnerRadius = 0;
                 verts++;
 
                 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[2]);
                 verts->fColor = color;
-                verts->fOffset = SkPoint::Make(0, 0);
-                verts->fOuterRadius = -args.fInnerRadius;
-                verts->fInnerRadius = innerRadius;
+                verts->fOffset = SkPoint::Make(maxOffset, 0);
+                verts->fOuterRadius = overstrokeOuterRadius;
+                verts->fInnerRadius = 0;
                 verts++;
             }
         }