More analytic shadow refactoring.
authorJim Van Verth <jvanverth@google.com>
Fri, 5 May 2017 19:53:23 +0000 (15:53 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Fri, 5 May 2017 20:18:13 +0000 (20:18 +0000)
This is mostly to simplify and reduce redundant calculations.

Change-Id: I895bd43fbc9defd812969870c2145f9f19d69a6b
Reviewed-on: https://skia-review.googlesource.com/15462
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
samplecode/SampleShadowUtils.cpp
src/effects/shadows/SkAmbientShadowMaskFilter.cpp
src/effects/shadows/SkSpotShadowMaskFilter.cpp
src/gpu/GrRenderTargetContext.cpp
src/gpu/GrRenderTargetContext.h
src/gpu/ops/GrShadowRRectOp.cpp
src/gpu/ops/GrShadowRRectOp.h
src/utils/SkShadowUtils.cpp

index 858d212..f5b6635 100755 (executable)
@@ -105,7 +105,7 @@ protected:
     }
 
     void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(0xFFDDDDDD);
+        canvas->drawColor(0xFFFFFFFF);
     }
 
     void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
index 584dc74..7578ae3 100644 (file)
@@ -149,32 +149,22 @@ bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context,
     SkASSERT(rtContext);
     // TODO: this will not handle local coordinates properly
 
-    if (fAmbientAlpha <= 0.0f) {
-        return true;
-    }
-
-    // only convex paths for now
-    if (!path.isConvex()) {
-        return false;
-    }
-
-    if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
-        return false;
-    }
-
-    // if circle
-    if (path.isOval(nullptr) && SkScalarNearlyEqual(path.getBounds().width(),
-                                                    path.getBounds().height())) {
-        SkRRect rrect = SkRRect::MakeOval(path.getBounds());
+    SkRect rect;
+    SkRRect rrect;
+    if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(),
+                                                  rect.height())) {
+        rrect = SkRRect::MakeOval(rect);
         return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
                                               SkMatrix::I(), strokeRec, rrect, rrect);
-    } else if (path.isRect(nullptr)) {
-        SkRRect rrect = SkRRect::MakeRect(path.getBounds());
+    } else if (path.isRect(&rect)) {
+        rrect = SkRRect::MakeRect(rect);
+        return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
+                                              SkMatrix::I(), strokeRec, rrect, rrect);
+    } else if (path.isRRect(&rrect) && rrect.isSimpleCircular()) {
         return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
                                               SkMatrix::I(), strokeRec, rrect, rrect);
     }
 
-    // TODO
     return false;
 }
 
@@ -186,62 +176,55 @@ bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
                                                              const SkStrokeRec& strokeRec,
                                                              const SkRRect& rrect,
                                                              const SkRRect& devRRect) const {
-    // It's likely the caller has already done these checks, but we have to be sure.
-
     // Fast path only supports filled rrects for now.
     // TODO: fill and stroke as well.
     if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
         return false;
     }
+    // These should have been checked by the caller.
     // Fast path only supports simple rrects with circular corners.
-    if (!devRRect.isRect() && !devRRect.isCircle() &&
-        (!devRRect.isSimple() || !devRRect.allCornersCircular())) {
-        return false;
-    }
+    SkASSERT(devRRect.isRect() || devRRect.isCircle() ||
+             (devRRect.isSimple() && devRRect.allCornersCircular()));
     // Fast path only supports uniform scale.
-    if (!viewMatrix.isSimilarity()) {
-        return false;
-    }
+    SkASSERT(viewMatrix.isSimilarity());
+    // Assume we have positive alpha
+    SkASSERT(fAmbientAlpha > 0);
+
     // 1/scale
-    SkScalar scaleFactor = viewMatrix.isScaleTranslate() ?
+    SkScalar devToSrcScale = viewMatrix.isScaleTranslate() ?
         SkScalarInvert(viewMatrix[SkMatrix::kMScaleX]) :
         sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] +
                        viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]);
 
-    if (fAmbientAlpha > 0.0f) {
-        SkScalar devSpaceStrokeWidth = fOccluderHeight * kHeightFactor * kGeomFactor;
-        const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
-        const SkScalar devSpaceAmbientBlur = devSpaceStrokeWidth * umbraAlpha;
-
-        // For the ambient rrect, we outset the offset rect
-        // by half the strokeWidth to get our stroke shape.
-        SkScalar srcSpaceStrokeWidth = devSpaceStrokeWidth * scaleFactor;
-        SkScalar ambientPathOutset = 0.5f*srcSpaceStrokeWidth;
-
-        SkRRect ambientRRect;
-        SkRect insetRect = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
-        // If the rrect was an oval then its outset will also be one.
-        // We set it explicitly to avoid errors.
-        if (rrect.isOval()) {
-            ambientRRect = SkRRect::MakeOval(insetRect);
-        } else {
-            SkScalar insetRad = rrect.getSimpleRadii().fX + ambientPathOutset;
-            ambientRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
-        }
-
-        GrPaint newPaint(paint);
-        GrColor4f color = newPaint.getColor4f();
-        color.fRGBA[3] *= fAmbientAlpha;
-        newPaint.setColor4f(color);
-        SkStrokeRec ambientStrokeRec(SkStrokeRec::kFill_InitStyle);
-        bool transparent = SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
-        ambientStrokeRec.setStrokeStyle(srcSpaceStrokeWidth, transparent);
-
-        rtContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect,
-                                   devSpaceAmbientBlur,
-                                   GrStyle(ambientStrokeRec, nullptr));
+    SkScalar devSpaceInsetWidth = fOccluderHeight * kHeightFactor * kGeomFactor;
+    const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
+    const SkScalar devSpaceAmbientBlur = devSpaceInsetWidth * umbraAlpha;
+
+    // Outset the shadow rrect to the border of the penumbra
+    SkScalar ambientPathOutset = devSpaceInsetWidth * devToSrcScale;
+    SkRRect ambientRRect;
+    SkRect outsetRect = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
+    // If the rrect was an oval then its outset will also be one.
+    // We set it explicitly to avoid errors.
+    if (rrect.isOval()) {
+        ambientRRect = SkRRect::MakeOval(outsetRect);
+    } else {
+        SkScalar outsetRad = rrect.getSimpleRadii().fX + ambientPathOutset;
+        ambientRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad);
     }
 
+    GrPaint newPaint(paint);
+    GrColor4f color = newPaint.getColor4f();
+    color.fRGBA[3] *= fAmbientAlpha;
+    newPaint.setColor4f(color);
+    if (SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag)) {
+        // set a large inset to force a fill
+        devSpaceInsetWidth = ambientRRect.width();
+    }
+
+    rtContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect,
+                               devSpaceAmbientBlur, devSpaceInsetWidth);
+
     return true;
 }
 
index fc73afc..0716d9b 100644 (file)
@@ -166,27 +166,18 @@ bool SkSpotShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context,
     SkASSERT(rtContext);
     // TODO: this will not handle local coordinates properly
 
-    if (fSpotAlpha <= 0.0f) {
-        return true;
-    }
-
-    // only convex paths for now
-    if (!path.isConvex()) {
-        return false;
-    }
-
-    if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
-        return false;
-    }
-
-    // if circle
-    if (path.isOval(nullptr) && SkScalarNearlyEqual(path.getBounds().width(),
-                                                    path.getBounds().height())) {
-        SkRRect rrect = SkRRect::MakeOval(path.getBounds());
+    SkRect rect;
+    SkRRect rrect;
+    if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(),
+                                                  rect.height())) {
+        rrect = SkRRect::MakeOval(rect);
+        return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
+                                              SkMatrix::I(), strokeRec, rrect, rrect);
+    } else if (path.isRect(&rect)) {
+        rrect = SkRRect::MakeRect(rect);
         return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
                                               SkMatrix::I(), strokeRec, rrect, rrect);
-    } else if (path.isRect(nullptr)) {
-        SkRRect rrect = SkRRect::MakeRect(path.getBounds());
+    } else if (path.isRRect(&rrect) && rrect.isSimpleCircular()) {
         return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
                                               SkMatrix::I(), strokeRec, rrect, rrect);
     }
@@ -202,113 +193,115 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
                                                           const SkStrokeRec& strokeRec,
                                                           const SkRRect& rrect,
                                                           const SkRRect& devRRect) const {
-    // It's likely the caller has already done these checks, but we have to be sure.
-
     // Fast path only supports filled rrects for now.
     // TODO: fill and stroke as well.
     if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
         return false;
     }
+    // These should have been checked by the caller.
     // Fast path only supports simple rrects with circular corners.
-    if (!devRRect.isRect() && !devRRect.isCircle() &&
-        (!devRRect.isSimple() || !devRRect.allCornersCircular())) {
-        return false;
-    }
+    SkASSERT(devRRect.isRect() || devRRect.isCircle() ||
+        (devRRect.isSimple() && devRRect.allCornersCircular()));
     // Fast path only supports uniform scale.
-    if (!viewMatrix.isSimilarity()) {
-        return false;
-    }
+    SkASSERT(viewMatrix.isSimilarity());
+    // Assume we have positive alpha
+    SkASSERT(fSpotAlpha > 0);
+
     // 1/scale
-    SkScalar scaleFactor = viewMatrix.isScaleTranslate() ?
+    SkScalar devToSrcScale = viewMatrix.isScaleTranslate() ?
         SkScalarInvert(viewMatrix[SkMatrix::kMScaleX]) :
         sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] +
                        viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]);
 
-    if (fSpotAlpha > 0.0f) {
-        float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f);
-
-        SkScalar devSpaceSpotRadius = 2.0f * fLightRadius * zRatio;
-        // handle scale of radius and pad due to CTM
-        const SkScalar srcSpaceSpotRadius = devSpaceSpotRadius * scaleFactor;
-
-        SkRRect spotShadowRRect;
-        // Compute the scale and translation for the spot shadow.
-        const SkScalar scale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight);
-        rrect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
-
-        SkPoint spotOffset = SkPoint::Make(zRatio*(-fLightPos.fX), zRatio*(-fLightPos.fY));
-        // Adjust for the effect of the scale.
-        spotOffset.fX += scale*viewMatrix[SkMatrix::kMTransX];
-        spotOffset.fY += scale*viewMatrix[SkMatrix::kMTransY];
-        // This offset is in dev space, need to transform it into source space.
-        SkMatrix ctmInverse;
-        if (!viewMatrix.invert(&ctmInverse)) {
-            // Since the matrix is a similarity, this should never happen, but just in case...
-            SkDebugf("Matrix is degenerate. Will not render spot shadow!\n");
-            return true;
-        }
-        ctmInverse.mapPoints(&spotOffset, 1);
-
-        // We want to extend the stroked area in so that it meets up with the caster
-        // geometry. The stroked geometry will, by definition already be inset half the
-        // stroke width but we also have to account for the scaling and translation when
-        // computing a new stroke shape.
+    float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f);
+
+    SkScalar devSpaceSpotBlur = 2.0f * fLightRadius * zRatio;
+    // handle scale of radius and pad due to CTM
+    const SkScalar srcSpaceSpotBlur = devSpaceSpotBlur * devToSrcScale;
+
+    // Compute the scale and translation for the spot shadow.
+    const SkScalar spotScale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight);
+    SkPoint spotOffset = SkPoint::Make(zRatio*(-fLightPos.fX), zRatio*(-fLightPos.fY));
+    // Adjust translate for the effect of the scale.
+    spotOffset.fX += spotScale*viewMatrix[SkMatrix::kMTransX];
+    spotOffset.fY += spotScale*viewMatrix[SkMatrix::kMTransY];
+    // This offset is in dev space, need to transform it into source space.
+    SkMatrix ctmInverse;
+    if (!viewMatrix.invert(&ctmInverse)) {
+        // Since the matrix is a similarity, this should never happen, but just in case...
+        SkDebugf("Matrix is degenerate. Will not render spot shadow!\n");
+        return true;
+    }
+    ctmInverse.mapPoints(&spotOffset, 1);
+
+    // Compute the transformed shadow rrect
+    SkRRect spotShadowRRect;
+    SkMatrix shadowTransform;
+    shadowTransform.setScaleTranslate(spotScale, spotScale, spotOffset.fX, spotOffset.fY);
+    rrect.transform(shadowTransform, &spotShadowRRect);
+    SkScalar spotRadius = spotShadowRRect.getSimpleRadii().fX;
+
+    // Compute the insetWidth
+    SkScalar blurOutset = 0.5f*srcSpaceSpotBlur;
+    SkScalar insetWidth = blurOutset;
+    if (fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag) {
+        // If transparent, just do a fill
+        insetWidth += spotShadowRRect.width();
+    } else {
+        // For shadows, instead of using a stroke we specify an inset from the penumbra
+        // border. We want to extend this inset area so that it meets up with the caster
+        // geometry. The inset geometry will by default already be inset by the blur width.
+        //
+        // We compare the min and max corners inset by the radius between the original
+        // rrect and the shadow rrect. The distance between the two plus the difference
+        // between the scaled radius and the original radius gives the distance from the
+        // transformed shadow shape to the original shape in that corner. The max
+        // of these gives the maximum distance we need to cover.
         //
-        // We begin by transforming the min and max corners -- inset by the radius -- by the scale
-        // and translation. The distance from that to the original inset point plus the difference
-        // between the scaled radius and the original radius gives the distance from the 
-        // transformed shadow shape to the original shape. If the max of the two distances is
-        // greater than the strokeWidth then we need to inset and increase the strokeWidth to cover
-        // the hole. If less, then we can outset and decrease the strokeWidth to reduce our
-        // coverage.
-
-        // This factor handles both the transform scale and subtracting the original value.
-        SkScalar comboScale = scale - 1;
-        SkScalar r = rrect.getSimpleRadii().fX;
-        SkPoint upperLeftOffset = SkPoint::Make(comboScale*(rrect.rect().fLeft + r),
-                                                comboScale*(rrect.rect().fTop + r));
-        upperLeftOffset += spotOffset;
-        SkPoint lowerRightOffset = SkPoint::Make(comboScale*(rrect.rect().fRight - r),
-                                                 comboScale*(rrect.rect().fBottom - r));
-        lowerRightOffset += spotOffset;
-
-        SkScalar maxOffset = SkTMax(upperLeftOffset.length(), lowerRightOffset.length());
-        maxOffset += comboScale*r;
-        SkScalar insetAmount = maxOffset - (0.5f * srcSpaceSpotRadius);
-        SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
-
-        SkScalar strokedDiff = SkTMin(spotShadowRRect.width(), spotShadowRRect.height())
-                             - (insetAmount + strokeWidth);
-
-        SkStrokeRec spotStrokeRec(SkStrokeRec::kFill_InitStyle);
-        // If the caster has too large a stroke or is transparent, just fill it.
-        if (strokedDiff < 0 || fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag) {
-            spotStrokeRec.setStrokeStyle(srcSpaceSpotRadius, true);
+        // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
+        // that to get the full insetWidth.
+        SkScalar maxOffset;
+        if (rrect.isRect()) {
+            // Manhattan distance works better for rects
+            maxOffset = SkTMax(SkTMax(SkTAbs(spotShadowRRect.rect().fLeft -
+                                             rrect.rect().fLeft),
+                                      SkTAbs(spotShadowRRect.rect().fTop -
+                                             rrect.rect().fTop)),
+                               SkTMax(SkTAbs(spotShadowRRect.rect().fRight -
+                                             rrect.rect().fRight),
+                                      SkTAbs(spotShadowRRect.rect().fBottom -
+                                             rrect.rect().fBottom)));
         } else {
-            // Since we can't have unequal strokes, inset the shadow rect so the inner
-            // and outer edges of the stroke will land where we want.
-            insetAmount *= 0.5f;
-            SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount, insetAmount);
-            // If the shadowRRect was an oval then its inset will also be one.
-            // We set it explicitly to avoid errors.
-            if (spotShadowRRect.isOval()) {
-                spotShadowRRect = SkRRect::MakeOval(insetRect);
-            } else {
-                SkScalar insetRad = spotShadowRRect.getSimpleRadii().fX - insetAmount;
-                spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
-            }
-            spotStrokeRec.setStrokeStyle(strokeWidth, false);
+            SkScalar dr = spotRadius - rrect.getSimpleRadii().fX;
+            SkPoint upperLeftOffset = SkPoint::Make(spotShadowRRect.rect().fLeft -
+                                                    rrect.rect().fLeft + dr,
+                                                    spotShadowRRect.rect().fTop -
+                                                    rrect.rect().fTop + dr);
+            SkPoint lowerRightOffset = SkPoint::Make(spotShadowRRect.rect().fRight -
+                                                     rrect.rect().fRight - dr,
+                                                     spotShadowRRect.rect().fBottom -
+                                                     rrect.rect().fBottom - dr);
+            maxOffset = SkScalarSqrt(SkTMax(upperLeftOffset.lengthSqd(),
+                                            lowerRightOffset.lengthSqd())) + dr;
         }
+        insetWidth += maxOffset;
+    }
 
-        spotShadowRRect.offset(spotOffset.fX, spotOffset.fY);
-
-        GrColor4f color = paint.getColor4f();
-        color.fRGBA[3] *= fSpotAlpha;
-        paint.setColor4f(color);
-        rtContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect,
-                                   devSpaceSpotRadius, GrStyle(spotStrokeRec, nullptr));
+    // Outset the shadow rrect to the border of the penumbra
+    SkRect outsetRect = spotShadowRRect.rect().makeOutset(blurOutset, blurOutset);
+    if (spotShadowRRect.isOval()) {
+        spotShadowRRect = SkRRect::MakeOval(outsetRect);
+    } else {
+        SkScalar outsetRad = spotRadius + blurOutset;
+        spotShadowRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad);
     }
 
+    GrColor4f color = paint.getColor4f();
+    color.fRGBA[3] *= fSpotAlpha;
+    paint.setColor4f(color);
+    rtContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect,
+                               devSpaceSpotBlur, insetWidth);
+
     return true;
 }
 
index e21522f..f81dc46 100644 (file)
@@ -985,8 +985,8 @@ void GrRenderTargetContext::drawShadowRRect(const GrClip& clip,
                                             GrPaint&& paint,
                                             const SkMatrix& viewMatrix,
                                             const SkRRect& rrect,
-                                            SkScalar blurRadius,
-                                            const GrStyle& style) {
+                                            SkScalar blurWidth,
+                                            SkScalar insetWidth) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
@@ -995,14 +995,11 @@ void GrRenderTargetContext::drawShadowRRect(const GrClip& clip,
         return;
     }
 
-    SkASSERT(!style.pathEffect()); // this should've been devolved to a path in SkGpuDevice
-
     AutoCheckFlush acf(this->drawingManager());
-    const SkStrokeRec stroke = style.strokeRec();
     // TODO: add instancing support?
 
     std::unique_ptr<GrLegacyMeshDrawOp> op = GrShadowRRectOp::Make(paint.getColor(), viewMatrix,
-                                                                   rrect, blurRadius, stroke);
+                                                                   rrect, blurWidth, insetWidth);
     if (op) {
         GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
         this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
index dd42db6..b3ff137 100644 (file)
@@ -158,15 +158,16 @@ public:
      * @param paint        describes how to color pixels.
      * @param viewMatrix   transformation matrix
      * @param rrect        the roundrect to draw
-     * @param blurRadius   amount of shadow blur to apply (in device space)
-     * @param style        style to apply to the rrect. Currently path effects are not allowed.
+     * @param blurWidth    amount of shadow blur to apply (in device space)
+     * @param insetWidth   minimum amount to inset from the rrect edge (in local space).
+     *                     We may inset more depending on the blur radius and geometry.
      */
     void drawShadowRRect(const GrClip&,
                          GrPaint&&,
                          const SkMatrix& viewMatrix,
                          const SkRRect& rrect,
                          SkScalar blurRadius,
-                         const GrStyle& style);
+                         SkScalar insetWidth);
 
     /**
      * Shortcut for filling a SkPath consisting of nested rrects using a paint. The result is
index 7803449..fc99b6d 100644 (file)
@@ -188,49 +188,38 @@ class ShadowCircularRRectOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
-    // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
-    // whether the rrect is only stroked or stroked and filled.
+    // An insetWidth > 1/2 rect width or height indicates a simple fill.
     ShadowCircularRRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
-                          float devRadius, bool isCircle, float blurRadius,
-                          float devStrokeWidth, bool strokeOnly)
+                          float devRadius, bool isCircle, float blurRadius, float insetWidth)
             : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
         SkRect bounds = devRect;
-        SkASSERT(devStrokeWidth > 0 || !strokeOnly);
+        SkASSERT(insetWidth > 0);
         SkScalar innerRadius = 0.0f;
         SkScalar outerRadius = devRadius;
         SkScalar umbraInset;
+
+        RRectType type = kFill_RRectType;
         if (isCircle) {
             umbraInset = 0;
+        } else if (insetWidth > 0 && insetWidth <= outerRadius) {
+            // If the client has requested a stroke smaller than the outer radius,
+            // we will assume they want no special umbra inset (this is for ambient shadows).
+            umbraInset = outerRadius;
         } else {
             umbraInset = SkTMax(outerRadius, blurRadius);
         }
 
-        RRectType type = kFill_RRectType;
-        if (devStrokeWidth > 0) {
-            SkScalar halfWidth = SkScalarHalf(devStrokeWidth);
-            outerRadius += halfWidth;
-            bounds.outset(halfWidth, halfWidth);
-
-            // If the client has requested a stroke smaller than the outer radius,
-            // we will assume they want no special umbra inset (this is for ambient shadows).
-            if (devStrokeWidth <= outerRadius) {
-                umbraInset = outerRadius;
-            }
-
-            if (strokeOnly) {
-                // If stroke is greater than width or height, this is still a fill,
-                // otherwise we compute stroke params.
-                if (isCircle) {
-                    innerRadius = devRadius - halfWidth;
-                    type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
-                } else {
-                    if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
-                        // We don't worry about a real inner radius, we just need to know if we
-                        // need to create overstroke vertices.
-                        innerRadius = SkTMax(devStrokeWidth - umbraInset, 0.0f);
-                        type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
-                    }
-                }
+        // If stroke is greater than width or height, this is still a fill,
+        // otherwise we compute stroke params.
+        if (isCircle) {
+            innerRadius = devRadius - insetWidth;
+            type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
+        } else {
+            if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) {
+                // We don't worry about a real inner radius, we just need to know if we
+                // need to create overstroke vertices.
+                innerRadius = SkTMax(insetWidth - umbraInset, 0.0f);
+                type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
             }
         }
 
@@ -650,45 +639,30 @@ namespace GrShadowRRectOp {
 std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
                                          const SkMatrix& viewMatrix,
                                          const SkRRect& rrect,
-                                         const SkScalar blurRadius,
-                                         const SkStrokeRec& stroke) {
+                                         SkScalar blurWidth,
+                                         SkScalar insetWidth) {
     // Shadow rrect ops only handle simple circular rrects.
     SkASSERT(viewMatrix.isSimilarity() &&
              (rrect.isSimple() || rrect.isRect() || rrect.isOval()));
+    SkASSERT(rrect.getSimpleRadii().fX > SK_ScalarNearlyZero &&
+             SkScalarNearlyEqual(rrect.getSimpleRadii().fX, rrect.getSimpleRadii().fY));
 
     // Do any matrix crunching before we reset the draw state for device coords.
     const SkRect& rrectBounds = rrect.getBounds();
     SkRect bounds;
     viewMatrix.mapRect(&bounds, rrectBounds);
 
-    SkVector radii = rrect.getSimpleRadii();
-    SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
-                                   viewMatrix[SkMatrix::kMSkewX] * radii.fY);
-    SkDEBUGCODE(SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * radii.fX +
-                                               viewMatrix[SkMatrix::kMScaleY] * radii.fY));
-    SkASSERT(SkScalarNearlyEqual(xRadius, yRadius));
-
-    // Hairline style is unexpected and meaningless with this DrawOp.
-    // It will be treated as a fill, which will make it visibly obvious that something's wrong.
-    SkStrokeRec::Style style = stroke.getStyle();
-    SkASSERT(SkStrokeRec::kHairline_Style != style);
-
-    // Do mapping of stroke. Use -1 to indicate fill-only draws.
-    SkScalar scaledStrokeWidth = -1;
-    bool isStrokeOnly = SkStrokeRec::kStroke_Style == style;
-    bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
-    if (hasStroke) {
-        SkScalar strokeWidth = stroke.getWidth();
-        // As the matrix is a similarity matrix, this should be isotropic.
-        scaledStrokeWidth = SkScalarAbs(
-                   strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX]));
-    }
+    // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
+    SkScalar radius = rrect.getSimpleRadii().fX;
+    SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
+    SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
+    SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
 
     return std::unique_ptr<GrLegacyMeshDrawOp>(new ShadowCircularRRectOp(color, viewMatrix, bounds,
-                                                                         xRadius, rrect.isOval(),
-                                                                         blurRadius,
-                                                                         scaledStrokeWidth,
-                                                                         isStrokeOnly));
+                                                                         scaledRadius,
+                                                                         rrect.isOval(),
+                                                                         blurWidth,
+                                                                         scaledInsetWidth));
 }
 }
 
@@ -707,16 +681,16 @@ DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
     viewMatrix.postTranslate(translateX, translateY);
     viewMatrix.postScale(scale, scale);
     GrColor color = GrRandomColor(random);
-    SkStrokeRec stroke = GrTest::TestStrokeRec(random);
-    SkScalar blurRadius = random->nextSScalar1() * 72.f;
+    SkScalar insetWidth = random->nextSScalar1() * 72.f;
+    SkScalar blurWidth = random->nextSScalar1() * 72.f;
     bool isCircle = random->nextBool();
     if (isCircle) {
         SkRect circle = GrTest::TestSquare(random);
         SkRRect rrect = SkRRect::MakeOval(circle);
-        return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurRadius, stroke);
+        return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth);
     } else {
         const SkRRect& rrect = GrTest::TestRRectSimple(random);
-        return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurRadius, stroke);
+        return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth);
     }
 }
 
index dec63ca..f972788 100644 (file)
@@ -20,7 +20,7 @@ class SkStrokeRec;
 namespace GrShadowRRectOp {
 
 std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor, const SkMatrix& viewMatrix, const SkRRect& rrect,
-                                         const SkScalar blurRadius, const SkStrokeRec& stroke);
+                                         SkScalar blurWidth, SkScalar insetWidth);
 }
 
 #endif
index c6102a9..19b2c2d 100644 (file)
@@ -526,7 +526,8 @@ static bool draw_analytic_shadows(SkCanvas* canvas, const SkPath& path, SkScalar
 
     SkRect rect;
     SkRRect rrect;
-    if (canvas->getTotalMatrix().isSimilarity()) {
+    const SkMatrix& ctm = canvas->getTotalMatrix();
+    if (ctm.rectStaysRect() && ctm.isSimilarity()) {
         if (path.isRect(&rect)) {
             SkPaint newPaint;
             newPaint.setColor(color);