Change drawPathWithMaskFilter to drawShapeWithMaskFilter

The caching portion of this (enabled by using GrShapes) is still in:

https://skia-review.googlesource.com/c/skia/+/146767 (Add GPU-side caching of mask-filtered path masks)

I would like to land them separately to assess the perf impact of dropping the path (im)mutability hint.

Change-Id: I584765177f2bb250a449d506d38f21f879fecdc6
Reviewed-on: https://skia-review.googlesource.com/147963
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/core/SkBlurMF.cpp b/src/core/SkBlurMF.cpp
index 4d4e250..65a2f33 100644
--- a/src/core/SkBlurMF.cpp
+++ b/src/core/SkBlurMF.cpp
@@ -26,6 +26,7 @@
 #include "GrRenderTargetContext.h"
 #include "GrResourceProvider.h"
 #include "GrShaderCaps.h"
+#include "GrShape.h"
 #include "GrStyle.h"
 #include "GrTextureProxy.h"
 #include "effects/GrCircleBlurFragmentProcessor.h"
@@ -49,7 +50,8 @@
                     SkIPoint* margin) const override;
 
 #if SK_SUPPORT_GPU
-    bool canFilterMaskGPU(const SkRRect& devRRect,
+    bool canFilterMaskGPU(const GrShape& shape,
+                          const SkRect& devSpaceShapeBounds,
                           const SkIRect& clipBounds,
                           const SkMatrix& ctm,
                           SkRect* maskRect) const override;
@@ -58,8 +60,7 @@
                              GrPaint&&,
                              const GrClip&,
                              const SkMatrix& viewMatrix,
-                             const SkStrokeRec& strokeRec,
-                             const SkPath& path) const override;
+                             const GrShape& shape) const override;
     bool directFilterRRectMaskGPU(GrContext*,
                                   GrRenderTargetContext* renderTargetContext,
                                   GrPaint&&,
@@ -733,16 +734,19 @@
                                                GrPaint&& paint,
                                                const GrClip& clip,
                                                const SkMatrix& viewMatrix,
-                                               const SkStrokeRec& strokeRec,
-                                               const SkPath& path) const {
+                                               const GrShape& shape) const {
     SkASSERT(renderTargetContext);
 
     if (fBlurStyle != kNormal_SkBlurStyle) {
         return false;
     }
 
+    if (!viewMatrix.rectStaysRect()) {
+        return false;
+    }
+
     // TODO: we could handle blurred stroked circles
-    if (!strokeRec.isFillStyle()) {
+    if (!shape.style().isSimpleFill()) {
         return false;
     }
 
@@ -751,19 +755,27 @@
     GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
     std::unique_ptr<GrFragmentProcessor> fp;
 
+    SkRRect rrect;
     SkRect rect;
-    if (path.isRect(&rect)) {
-        SkScalar pad = 3.0f * xformedSigma;
-        rect.outset(pad, pad);
+    bool inverted;
+    if (shape.asRRect(&rrect, nullptr, nullptr, &inverted) && !inverted) {
+        rect = rrect.rect();
+        SkAssertResult(viewMatrix.mapRect(&rect));
+        if (rrect.isRect()) {
+            SkScalar pad = 3.0f * xformedSigma;
+            rect.outset(pad, pad);
 
-        fp = GrRectBlurEffect::Make(proxyProvider, *context->contextPriv().caps()->shaderCaps(),
-                                    rect, xformedSigma);
-    } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height())) {
-        fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, rect, xformedSigma);
+            fp = GrRectBlurEffect::Make(proxyProvider, *context->contextPriv().caps()->shaderCaps(),
+                                        rect, xformedSigma);
+        } else if (SkRRectPriv::IsCircle(rrect)) {
+            fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, rect, xformedSigma);
 
-        // expand the rect for the coverage geometry
-        int pad = SkScalarCeilToInt(6*xformedSigma)/2;
-        rect.outset(SkIntToScalar(pad), SkIntToScalar(pad));
+            // expand the rect for the coverage geometry
+            int pad = SkScalarCeilToInt(6*xformedSigma)/2;
+            rect.outset(SkIntToScalar(pad), SkIntToScalar(pad));
+        } else {
+            return false;
+        }
     } else {
         return false;
     }
@@ -878,7 +890,8 @@
     return true;
 }
 
-bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
+bool SkBlurMaskFilterImpl::canFilterMaskGPU(const GrShape& shape,
+                                            const SkRect& devSpaceShapeBounds,
                                             const SkIRect& clipBounds,
                                             const SkMatrix& ctm,
                                             SkRect* maskRect) const {
@@ -887,13 +900,13 @@
         return false;
     }
 
-    // We prefer to blur small rects with small radii on the CPU.
-    if (devRRect.isRect()) {
+    // We prefer to blur paths with small blur radii on the CPU.
+    if (ctm.rectStaysRect()) {
         static const SkScalar kMIN_GPU_BLUR_SIZE  = SkIntToScalar(64);
         static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
 
-        if (devRRect.width() <= kMIN_GPU_BLUR_SIZE &&
-            devRRect.height() <= kMIN_GPU_BLUR_SIZE &&
+        if (devSpaceShapeBounds.width() <= kMIN_GPU_BLUR_SIZE &&
+            devSpaceShapeBounds.height() <= kMIN_GPU_BLUR_SIZE &&
             xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
             return false;
         }
@@ -907,7 +920,7 @@
     float sigma3 = 3 * SkScalarToFloat(xformedSigma);
 
     SkRect clipRect = SkRect::Make(clipBounds);
-    SkRect srcRect(devRRect.rect());
+    SkRect srcRect = devSpaceShapeBounds;
 
     // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
     srcRect.outset(sigma3, sigma3);
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index e001f58..0c5af3e 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -323,7 +323,8 @@
 }
 bool SkMaskFilterBase::onHasFragmentProcessor() const { return false; }
 
-bool SkMaskFilterBase::canFilterMaskGPU(const SkRRect& devRRect,
+bool SkMaskFilterBase::canFilterMaskGPU(const GrShape& shape,
+                                        const SkRect& devSpaceShapeBounds,
                                         const SkIRect& clipBounds,
                                         const SkMatrix& ctm,
                                         SkRect* maskRect) const {
@@ -335,8 +336,7 @@
                                            GrPaint&&,
                                            const GrClip&,
                                            const SkMatrix& viewMatrix,
-                                           const SkStrokeRec& strokeRec,
-                                           const SkPath& path) const {
+                                           const GrShape&) const {
     return false;
 }
 
diff --git a/src/core/SkMaskFilterBase.h b/src/core/SkMaskFilterBase.h
index 53d5ec9..cd48477 100644
--- a/src/core/SkMaskFilterBase.h
+++ b/src/core/SkMaskFilterBase.h
@@ -24,6 +24,7 @@
 class GrRenderTarget;
 class GrRenderTargetContext;
 class GrResourceProvider;
+class GrShape;
 class GrTexture;
 class GrTextureProxy;
 
@@ -89,8 +90,6 @@
      *    canFilterMaskGPU is called
      *    if (it returns true)
      *        the returned mask rect is used for quick rejecting
-     *        either directFilterMaskGPU or directFilterRRectMaskGPU is then called
-     *        if (neither of them handle the blur)
      *            the mask rect is used to generate the mask
      *            filterMaskGPU is called to filter the mask
      *
@@ -99,7 +98,8 @@
      *        filterMaskGPU(devShape, ...)
      * this would hide the RRect special case and the mask generation
      */
-    virtual bool canFilterMaskGPU(const SkRRect& devRRect,
+    virtual bool canFilterMaskGPU(const GrShape&,
+                                  const SkRect& devSpaceShapeBounds,
                                   const SkIRect& clipBounds,
                                   const SkMatrix& ctm,
                                   SkRect* maskRect) const;
@@ -113,8 +113,7 @@
                                      GrPaint&& paint,
                                      const GrClip&,
                                      const SkMatrix& viewMatrix,
-                                     const SkStrokeRec& strokeRec,
-                                     const SkPath& path) const;
+                                     const GrShape& shape) const;
     /**
      *  Try to directly render a rounded rect mask filter into the target.  Returns
      *  true if drawing was successful.  If false is returned then paint is unmodified.
diff --git a/src/gpu/GrBlurUtils.cpp b/src/gpu/GrBlurUtils.cpp
index 1fc7d4b..669292c 100644
--- a/src/gpu/GrBlurUtils.cpp
+++ b/src/gpu/GrBlurUtils.cpp
@@ -14,6 +14,8 @@
 #include "GrProxyProvider.h"
 #include "GrRenderTargetContext.h"
 #include "GrRenderTargetContextPriv.h"
+#include "GrShape.h"
+#include "GrSoftwarePathRenderer.h"
 #include "GrStyle.h"
 #include "GrTextureProxy.h"
 #include "effects/GrSimpleTextureEffect.h"
@@ -60,11 +62,29 @@
                                      GrRenderTargetContext* renderTargetContext,
                                      const GrClip& clipData,
                                      const SkMatrix& viewMatrix,
-                                     const SkPath& devPath,
+                                     const GrShape& shape,
                                      const SkMaskFilter* filter,
                                      const SkIRect& clipBounds,
-                                     GrPaint&& paint,
-                                     SkStrokeRec::InitStyle fillOrHairline) {
+                                     GrPaint&& paint) {
+    SkASSERT(filter);
+    SkASSERT(!shape.style().applies());
+
+    auto proxyProvider = context->contextPriv().proxyProvider();
+
+    sk_sp<GrTextureProxy> maskProxy;
+
+    SkStrokeRec::InitStyle fillOrHairline = shape.style().isSimpleHairline()
+                                                    ? SkStrokeRec::kHairline_InitStyle
+                                                    : SkStrokeRec::kFill_InitStyle;
+
+    // TODO: it seems like we could create an SkDraw here and set its fMatrix field rather
+    // than explicitly transforming the path to device space.
+    SkPath devPath;
+
+    shape.asPath(&devPath);
+
+    devPath.transform(viewMatrix);
+
     SkMask srcM, dstM;
     if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
                             SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
@@ -72,6 +92,8 @@
     }
     SkAutoMaskFreeImage autoSrc(srcM.fImage);
 
+    SkASSERT(SkMask::kA8_Format == srcM.fFormat);
+
     if (!as_MFB(filter)->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
         return false;
     }
@@ -96,11 +118,10 @@
         return false;
     }
 
-    auto proxyProvider = context->contextPriv().proxyProvider();
-    sk_sp<GrTextureProxy> maskProxy = proxyProvider->createTextureProxy(std::move(image),
-                                                                        kNone_GrSurfaceFlags,
-                                                                        1, SkBudgeted::kYes,
-                                                                        SkBackingFit::kApprox);
+    maskProxy = proxyProvider->createTextureProxy(std::move(image),
+                                                  kNone_GrSurfaceFlags,
+                                                  1, SkBudgeted::kYes,
+                                                  SkBackingFit::kApprox);
     if (!maskProxy) {
         return false;
     }
@@ -109,11 +130,11 @@
                      dstM.fBounds, std::move(paint), std::move(maskProxy));
 }
 
-// Create a mask of 'devPath' and place the result in 'mask'.
+// Create a mask of 'shape' and place the result in 'mask'.
 static sk_sp<GrTextureProxy> create_mask_GPU(GrContext* context,
                                              const SkIRect& maskRect,
-                                             const SkPath& devPath,
-                                             SkStrokeRec::InitStyle fillOrHairline,
+                                             const SkMatrix& origViewMatrix,
+                                             const GrShape& shape,
                                              GrAA aa,
                                              int sampleCnt) {
     if (GrAA::kNo == aa) {
@@ -140,62 +161,83 @@
 
     // Draw the mask into maskTexture with the path's integerized top-left at
     // the origin using maskPaint.
-    SkMatrix translate;
-    translate.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
-    rtContext->drawPath(clip, std::move(maskPaint), aa, translate, devPath,
-                        GrStyle(fillOrHairline));
+    SkMatrix viewMatrix = origViewMatrix;
+    viewMatrix.postTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
+    rtContext->drawShape(clip, std::move(maskPaint), aa, viewMatrix, shape);
     return rtContext->asTextureProxyRef();
 }
 
-static void draw_path_with_mask_filter(GrContext* context,
-                                       GrRenderTargetContext* renderTargetContext,
-                                       const GrClip& clip,
-                                       GrPaint&& paint,
-                                       GrAA aa,
-                                       const SkMatrix& viewMatrix,
-                                       const SkMaskFilterBase* maskFilter,
-                                       const GrStyle& style,
-                                       const SkPath* path,
-                                       bool pathIsMutable) {
-    SkASSERT(maskFilter);
+static bool get_unclipped_shape_dev_bounds(const GrShape& shape, const SkMatrix& matrix,
+                                           SkIRect* devBounds) {
+    SkRect shapeBounds = shape.styledBounds();
+    if (shapeBounds.isEmpty()) {
+        return false;
+    }
+    SkRect shapeDevBounds;
+    matrix.mapRect(&shapeDevBounds, shapeBounds);
+    // Even though these are "unclipped" bounds we still clip to the int32_t range.
+    // This is the largest int32_t that is representable exactly as a float. The next 63 larger ints
+    // would round down to this value when cast to a float, but who really cares.
+    // INT32_MIN is exactly representable.
+    static constexpr int32_t kMaxInt = 2147483520;
+    if (!shapeDevBounds.intersect(SkRect::MakeLTRB(INT32_MIN, INT32_MIN, kMaxInt, kMaxInt))) {
+        return false;
+    }
+    // Make sure that the resulting SkIRect can have representable width and height
+    if (SkScalarRoundToInt(shapeDevBounds.width()) > kMaxInt ||
+        SkScalarRoundToInt(shapeDevBounds.height()) > kMaxInt) {
+        return false;
+    }
+    shapeDevBounds.roundOut(devBounds);
+    return true;
+}
 
-    SkIRect clipBounds;
+// Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
+// is no intersection.
+static bool get_shape_and_clip_bounds(GrRenderTargetContext* renderTargetContext,
+                                      const GrClip& clip,
+                                      const GrShape& shape,
+                                      const SkMatrix& matrix,
+                                      SkIRect* unclippedDevShapeBounds,
+                                      SkIRect* devClipBounds) {
+    // compute bounds as intersection of rt size, clip, and path
     clip.getConservativeBounds(renderTargetContext->width(),
                                renderTargetContext->height(),
-                               &clipBounds);
-    SkTLazy<SkPath> tmpPath;
-    SkStrokeRec::InitStyle fillOrHairline;
+                               devClipBounds);
 
-    // We just fully apply the style here.
-    if (style.applies()) {
-        SkScalar scale = GrStyle::MatrixToScaleFactor(viewMatrix);
-        if (0 == scale || !style.applyToPath(tmpPath.init(), &fillOrHairline, *path, scale)) {
-            return;
-        }
-        pathIsMutable = true;
-        path = tmpPath.get();
-    } else if (style.isSimpleHairline()) {
-        fillOrHairline = SkStrokeRec::kHairline_InitStyle;
-    } else {
-        SkASSERT(style.isSimpleFill());
-        fillOrHairline = SkStrokeRec::kFill_InitStyle;
+    if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
+        *unclippedDevShapeBounds = SkIRect::EmptyIRect();
+        return false;
     }
 
-    // transform the path into device space
-    if (!viewMatrix.isIdentity()) {
-        SkPath* result;
-        if (pathIsMutable) {
-            result = const_cast<SkPath*>(path);
-        } else {
-            if (!tmpPath.isValid()) {
-                tmpPath.init();
-            }
-            result = tmpPath.get();
+    return true;
+}
+
+static void draw_shape_with_mask_filter(GrContext* context,
+                                        GrRenderTargetContext* renderTargetContext,
+                                        const GrClip& clip,
+                                        GrPaint&& paint,
+                                        GrAA aa,
+                                        const SkMatrix& viewMatrix,
+                                        const SkMaskFilterBase* maskFilter,
+                                        const GrShape& origShape) {
+    SkASSERT(maskFilter);
+
+    const GrShape* shape = &origShape;
+    SkTLazy<GrShape> tmpShape;
+
+    if (origShape.style().applies()) {
+        SkScalar styleScale =  GrStyle::MatrixToScaleFactor(viewMatrix);
+        if (0 == styleScale) {
+            return;
         }
-        path->transform(viewMatrix, result);
-        path = result;
-        result->setIsVolatile(true);
-        pathIsMutable = true;
+
+        tmpShape.init(origShape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, styleScale));
+        if (tmpShape.get()->isEmpty()) {
+            return;
+        }
+
+        shape = tmpShape.get();
     }
 
     if (maskFilter->directFilterMaskGPU(context,
@@ -203,16 +245,33 @@
                                         std::move(paint),
                                         clip,
                                         viewMatrix,
-                                        SkStrokeRec(fillOrHairline),
-                                        *path)) {
+                                        *shape)) {
         // the mask filter was able to draw itself directly, so there's nothing
         // left to do.
         return;
     }
 
+    // If the path is hairline, ignore inverse fill.
+    bool inverseFilled = shape->inverseFilled() &&
+                         !GrPathRenderer::IsStrokeHairlineOrEquivalent(shape->style(),
+                                                                       viewMatrix, nullptr);
+
+    SkIRect unclippedDevShapeBounds, devClipBounds;
+    if (!get_shape_and_clip_bounds(renderTargetContext,
+                                   clip, *shape,
+                                   viewMatrix,
+                                   &unclippedDevShapeBounds,
+                                   &devClipBounds)) {
+        // TODO: just cons up an opaque mask here
+        if (!inverseFilled) {
+            return;
+        }
+    }
+
     SkRect maskRect;
-    if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(path->getBounds()),
-                                     clipBounds,
+    if (maskFilter->canFilterMaskGPU(*shape,
+                                     SkRect::Make(unclippedDevShapeBounds),
+                                     devClipBounds,
                                      viewMatrix,
                                      &maskRect)) {
         // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
@@ -220,7 +279,7 @@
         // so the mask draws in a reproducible manner.
         SkIRect finalIRect;
         maskRect.roundOut(&finalIRect);
-        if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
+        if (clip_bounds_quick_reject(devClipBounds, finalIRect)) {
             // clipped out
             return;
         }
@@ -231,8 +290,8 @@
             sk_sp<GrTextureProxy> maskProxy(create_mask_GPU(
                                                         context,
                                                         finalIRect,
-                                                        *path,
-                                                        fillOrHairline,
+                                                        viewMatrix,
+                                                        *shape,
                                                         aa,
                                                         renderTargetContext->numColorSamples()));
             if (maskProxy) {
@@ -245,59 +304,52 @@
 
         if (filteredMask) {
             if (draw_mask(renderTargetContext, clip, viewMatrix,
-                            finalIRect, std::move(paint), std::move(filteredMask))) {
+                          finalIRect, std::move(paint), std::move(filteredMask))) {
                 // This path is completely drawn
                 return;
             }
         }
-
     }
 
-    sw_draw_with_mask_filter(context, renderTargetContext, clip, viewMatrix, *path, maskFilter,
-                             clipBounds, std::move(paint), fillOrHairline);
+    sw_draw_with_mask_filter(context, renderTargetContext, clip, viewMatrix, *shape,
+                             maskFilter, devClipBounds, std::move(paint));
 }
 
-void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
-                                         GrRenderTargetContext* renderTargetContext,
-                                         const GrClip& clip,
-                                         const SkPath& path,
-                                         GrPaint&& paint,
-                                         GrAA aa,
-                                         const SkMatrix& viewMatrix,
-                                         const SkMaskFilter* mf,
-                                         const GrStyle& style,
-                                         bool pathIsMutable) {
-    draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(paint), aa, viewMatrix,
-                               as_MFB(mf), style, &path, pathIsMutable);
+void GrBlurUtils::drawShapeWithMaskFilter(GrContext* context,
+                                          GrRenderTargetContext* renderTargetContext,
+                                          const GrClip& clip,
+                                          const GrShape& shape,
+                                          GrPaint&& paint,
+                                          GrAA aa,
+                                          const SkMatrix& viewMatrix,
+                                          const SkMaskFilter* mf) {
+    draw_shape_with_mask_filter(context, renderTargetContext, clip, std::move(paint), aa,
+                                viewMatrix, as_MFB(mf), shape);
 }
 
-void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
-                                         GrRenderTargetContext* renderTargetContext,
-                                         const GrClip& clip,
-                                         const SkPath& path,
-                                         const SkPaint& paint,
-                                         const SkMatrix& viewMatrix,
-                                         bool pathIsMutable) {
+void GrBlurUtils::drawShapeWithMaskFilter(GrContext* context,
+                                          GrRenderTargetContext* renderTargetContext,
+                                          const GrClip& clip,
+                                          const SkPaint& paint,
+                                          const SkMatrix& viewMatrix,
+                                          const GrShape& shape) {
     if (context->abandoned()) {
         return;
     }
 
-    SkASSERT(!pathIsMutable || path.isVolatile());
-
-    GrStyle style(paint);
-
     GrPaint grPaint;
     if (!SkPaintToGrPaint(context, renderTargetContext->colorSpaceInfo(), paint, viewMatrix,
                           &grPaint)) {
         return;
     }
+
     GrAA aa = GrAA(paint.isAntiAlias());
     SkMaskFilterBase* mf = as_MFB(paint.getMaskFilter());
     if (mf && !mf->hasFragmentProcessor()) {
         // The MaskFilter wasn't already handled in SkPaintToGrPaint
-        draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(grPaint), aa,
-                                   viewMatrix, mf, style, &path, pathIsMutable);
+        draw_shape_with_mask_filter(context, renderTargetContext, clip, std::move(grPaint), aa,
+                                    viewMatrix, mf, shape);
     } else {
-        renderTargetContext->drawPath(clip, std::move(grPaint), aa, viewMatrix, path, style);
+        renderTargetContext->drawShape(clip, std::move(grPaint), aa, viewMatrix, shape);
     }
 }
diff --git a/src/gpu/GrBlurUtils.h b/src/gpu/GrBlurUtils.h
index 4fb671b..d8f99f1 100644
--- a/src/gpu/GrBlurUtils.h
+++ b/src/gpu/GrBlurUtils.h
@@ -15,6 +15,7 @@
 class GrPaint;
 class GrRenderTarget;
 class GrRenderTargetContext;
+class GrShape;
 class GrStyle;
 struct SkIRect;
 class SkMaskFilter;
@@ -29,30 +30,27 @@
  */
 namespace GrBlurUtils {
     /**
-     * Draw a path handling the mask filter if present.
+     * Draw a shape handling the mask filter if present.
      */
-    void drawPathWithMaskFilter(GrContext*,
-                                GrRenderTargetContext*,
-                                const GrClip&,
-                                const SkPath& origSrcPath,
-                                const SkPaint&,
-                                const SkMatrix& viewMatrix,
-                                bool pathIsMutable);
+    void drawShapeWithMaskFilter(GrContext*,
+                                 GrRenderTargetContext*,
+                                 const GrClip&,
+                                 const SkPaint&,
+                                 const SkMatrix& viewMatrix,
+                                 const GrShape&);
 
     /**
-     * Draw a path handling the mask filter. The mask filter is not optional.
+     * Draw a shape handling the mask filter. The mask filter is not optional.
      * The GrPaint will be modified after return.
      */
-    void drawPathWithMaskFilter(GrContext*,
-                                GrRenderTargetContext*,
-                                const GrClip&,
-                                const SkPath& path,
-                                GrPaint&&,
-                                GrAA,
-                                const SkMatrix& viewMatrix,
-                                const SkMaskFilter*,
-                                const GrStyle&,
-                                bool pathIsMutable);
+    void drawShapeWithMaskFilter(GrContext*,
+                                 GrRenderTargetContext*,
+                                 const GrClip&,
+                                 const GrShape&,
+                                 GrPaint&&,
+                                 GrAA,
+                                 const SkMatrix& viewMatrix,
+                                 const SkMaskFilter*);
 };
 
 #endif
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 66bbc53..bc98827 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -70,8 +70,11 @@
 
     void drawPath(const GrClip& clip, const SkPath& path, const SkPaint& paint,
                   const SkMatrix& viewMatrix, bool pathIsMutable) override {
-        GrBlurUtils::drawPathWithMaskFilter(fRenderTargetContext->fContext, fRenderTargetContext,
-                                            clip, path, paint, viewMatrix, pathIsMutable);
+        // TODO: we are losing the mutability of the path here
+        GrShape shape(path, paint);
+
+        GrBlurUtils::drawShapeWithMaskFilter(fRenderTargetContext->fContext, fRenderTargetContext,
+                                             clip, paint, viewMatrix, shape);
     }
 
     void makeGrPaint(GrMaskFormat maskFormat, const SkPaint& skPaint, const SkMatrix& viewMatrix,
@@ -1497,23 +1500,19 @@
         SkRect rects[2];
         if (style.isSimpleFill() && fills_as_nested_rects(viewMatrix, path, rects)) {
             // Concave AA paths are expensive - try to avoid them for special cases
-            SkRect rects[2];
-
-            if (fills_as_nested_rects(viewMatrix, path, rects)) {
-                std::unique_ptr<GrDrawOp> op = GrRectOpFactory::MakeAAFillNestedRects(
-                                fContext, std::move(paint), viewMatrix, rects);
-                if (op) {
-                    this->addDrawOp(clip, std::move(op));
-                }
-                // A null return indicates that there is nothing to draw in this case.
-                return;
+            std::unique_ptr<GrDrawOp> op = GrRectOpFactory::MakeAAFillNestedRects(
+                            fContext, std::move(paint), viewMatrix, rects);
+            if (op) {
+                this->addDrawOp(clip, std::move(op));
             }
+            // Returning here indicates that there is nothing to draw in this case.
+            return;
         }
     }
 
     GrShape shape(path, style);
 
-    return this->drawShape(clip, std::move(paint), aa, viewMatrix, shape);
+    this->drawShape(clip, std::move(paint), aa, viewMatrix, shape);
 }
 
 void GrRenderTargetContext::drawShape(const GrClip& clip,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 81b533a..2070110 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -16,6 +16,7 @@
 #include "GrGpu.h"
 #include "GrImageTextureMaker.h"
 #include "GrRenderTargetContextPriv.h"
+#include "GrShape.h"
 #include "GrStyle.h"
 #include "GrSurfaceProxyPriv.h"
 #include "GrTextureAdjuster.h"
@@ -377,13 +378,15 @@
 void SkGpuDevice::drawRect(const SkRect& rect, const SkPaint& paint) {
     ASSERT_SINGLE_OWNER
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawRect", fContext.get());
+
+    GrStyle style(paint);
+
     // A couple reasons we might need to call drawPath.
     if (paint.getMaskFilter() || paint.getPathEffect()) {
-        SkPath path;
-        path.setIsVolatile(true);
-        path.addRect(rect);
-        GrBlurUtils::drawPathWithMaskFilter(fContext.get(), fRenderTargetContext.get(),
-                                            this->clip(), path, paint, this->ctm(), true);
+        GrShape shape(rect, style);
+
+        GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(),
+                                             this->clip(), paint, this->ctm(), shape);
         return;
     }
 
@@ -393,7 +396,6 @@
         return;
     }
 
-    GrStyle style(paint);
     fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
                                    this->ctm(), rect, &style);
 }
@@ -403,6 +405,7 @@
 void SkGpuDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
     ASSERT_SINGLE_OWNER
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawRRect", fContext.get());
+
     GrPaint grPaint;
     if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
                           this->ctm(), &grPaint)) {
@@ -436,11 +439,11 @@
         // The only mask filter the native rrect drawing code could've handle was taken
         // care of above.
         // A path effect will presumably transform this rrect into something else.
-        SkPath path;
-        path.setIsVolatile(true);
-        path.addRRect(rrect);
-        GrBlurUtils::drawPathWithMaskFilter(fContext.get(), fRenderTargetContext.get(),
-                                            this->clip(), path, paint, this->ctm(), true);
+        GrShape shape(rrect, style);
+
+        // TODO: this is throwing away to work we did to create the GrPaint
+        GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(),
+                                             this->clip(), paint, this->ctm(), shape);
         return;
     }
 
@@ -482,8 +485,12 @@
     path.addRRect(inner);
     path.setFillType(SkPath::kEvenOdd_FillType);
 
-    GrBlurUtils::drawPathWithMaskFilter(fContext.get(), fRenderTargetContext.get(), this->clip(),
-                                        path, paint, this->ctm(), true);
+    // TODO: We are losing the possible mutability of the path here but this should probably be
+    // fixed by upgrading GrShape to handle DRRects.
+    GrShape shape(path, paint);
+
+    GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(), this->clip(),
+                                         paint, this->ctm(), shape);
 }
 
 
@@ -630,8 +637,12 @@
                                        this->ctm(), origSrcPath, GrStyle(paint));
         return;
     }
-    GrBlurUtils::drawPathWithMaskFilter(fContext.get(), fRenderTargetContext.get(), this->clip(),
-                                        origSrcPath, paint, this->ctm(), pathIsMutable);
+
+    // TODO: losing possible mutability of 'origSrcPath' here
+    GrShape shape(origSrcPath, paint);
+
+    GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(), this->clip(),
+                                         paint, this->ctm(), shape);
 }
 
 static const int kBmpSmallTileSize = 1 << 10;
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index 9b2641e..49753ce 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -10,6 +10,7 @@
 #include "GrCaps.h"
 #include "GrColorSpaceXform.h"
 #include "GrRenderTargetContext.h"
+#include "GrShape.h"
 #include "GrStyle.h"
 #include "GrTextureAdjuster.h"
 #include "GrTextureMaker.h"
@@ -336,10 +337,8 @@
         }
     }
 
-    SkPath rectPath;
-    rectPath.addRect(clippedDstRect);
-    rectPath.setIsVolatile(true);
-    GrBlurUtils::drawPathWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(),
-                                        rectPath, std::move(grPaint), aa, viewMatrix, mf,
-                                        GrStyle::SimpleFill(), true);
+    GrShape shape(clippedDstRect, GrStyle::SimpleFill());
+
+    GrBlurUtils::drawShapeWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(),
+                                         shape, std::move(grPaint), aa, viewMatrix, mf);
 }