Change saveLayer() semantics to take unfiltered bounds.

For optimizing saveLayer() offscreens, it is useful to know the
bounds of the primitive being drawn. Currently, the bounds passed to
saveLayer() are filtered, which makes it difficult to know the original
bounds of the primitive. This CL changes the semantics to accept
unfiltered bounds. This actually simplifies the callsites too. In
order to result in the correct pixels being produced, we then call
computeFastBounds() inside clipRectBounds().

The old behaviour is wrapped in #ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED,
until we can update Chrome's callsites (see
https://codereview.chromium.org/1316243002/).

This change will affect the following GMs:
testimagefilters: saveLayer bounds no longer cause clipping
imagefiltersbase: slight pixel diffs
resizeimagefilter: slight pixel diffs on the "high quality" test case
imagefilterscropexpand: displacement results are now correct
filterfastbounds: slight pixel diffs
matriximagefilter: slight pixel diffs

BUG=skia:3194 skia:4526

Review URL: https://codereview.chromium.org/1304883004
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 6dd8d04..1b32236 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -38,6 +38,10 @@
 #include "GrRenderTarget.h"
 #endif
 
+#ifndef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
+#define SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
+#endif
+
 /*
  *  Return true if the drawing this rect would hit every pixels in the canvas.
  *
@@ -408,11 +412,36 @@
     return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
 }
 
+/**
+ * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
+ * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
+ * outside of its raw-bounds by 1/2 the stroke width.  SkPaint has lots of optional
+ * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
+ * patheffects, stroking, etc.  This function takes a raw bounds and a paint, and returns the
+ * conservative "effective" bounds based on the settings in the paint... with one exception. This
+ * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
+ * deliberately ignored.
+ */
+static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
+                                                            const SkRect& rawBounds,
+                                                            SkRect* storage) {
+    SkPaint tmpUnfiltered(paint);
+    tmpUnfiltered.setImageFilter(nullptr);
+    if (tmpUnfiltered.canComputeFastBounds()) {
+        return tmpUnfiltered.computeFastBounds(rawBounds, storage);
+    } else {
+        return rawBounds;
+    }
+}
+
 class AutoDrawLooper {
 public:
+    // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
+    // paint. It's used to determine the size of the offscreen layer for filters.
+    // If null, the clip will be used instead.
     AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
                    bool skipLayerForImageFilter = false,
-                   const SkRect* bounds = nullptr) : fOrigPaint(paint) {
+                   const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
         fCanvas = canvas;
         fFilter = canvas->getDrawFilter();
         fPaint = &fOrigPaint;
@@ -447,7 +476,14 @@
             SkPaint tmp;
             tmp.setImageFilter(fPaint->getImageFilter());
             tmp.setXfermode(fPaint->getXfermode());
-            (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
+#ifndef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
+            SkRect storage;
+            if (rawBounds) {
+                // Make rawBounds include all paint outsets except for those due to image filters.
+                rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
+            }
+#endif
+            (void)canvas->internalSaveLayer(rawBounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
                                             SkCanvas::kFullLayer_SaveLayerStrategy);
             fTempLayerForImageFilter = true;
             // we remove the imagefilter/xfermode inside doNext()
@@ -1019,8 +1055,21 @@
 
     const SkMatrix& ctm = fMCRec->fMatrix;  // this->getTotalMatrix()
 
+// This is a temporary hack, until individual filters can do their own
+// bloating, when this will be removed.
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
+    SkRect storage;
+#endif
     if (imageFilter) {
         imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
+        if (bounds && imageFilter->canComputeFastBounds()) {
+            imageFilter->computeFastBounds(*bounds, &storage);
+            bounds = &storage;
+        } else {
+            bounds = nullptr;
+        }
+#endif
     }
     SkIRect ir;
     if (bounds) {
@@ -1965,10 +2014,17 @@
         } else {
             r.set(pts, SkToInt(count));
         }
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         bounds = &paint.computeFastStrokeBounds(r, &storage);
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
+            return;
+        }
+        bounds = &r;
+#endif
     }
 
     SkASSERT(pts != nullptr);
@@ -1992,10 +2048,17 @@
         SkRect tmp(r);
         tmp.sort();
 
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         bounds = &paint.computeFastBounds(tmp, &storage);
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
+            return;
+        }
+        bounds = &r;
+#endif
     }
 
     LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
@@ -2012,10 +2075,17 @@
     SkRect storage;
     const SkRect* bounds = nullptr;
     if (paint.canComputeFastBounds()) {
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         bounds = &paint.computeFastBounds(oval, &storage);
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
+            return;
+        }
+        bounds = &oval;
+#endif
     }
 
     LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
@@ -2032,10 +2102,17 @@
     SkRect storage;
     const SkRect* bounds = nullptr;
     if (paint.canComputeFastBounds()) {
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
+            return;
+        }
+        bounds = &rrect.getBounds();
+#endif
     }
 
     if (rrect.isRect()) {
@@ -2062,10 +2139,17 @@
     SkRect storage;
     const SkRect* bounds = nullptr;
     if (paint.canComputeFastBounds()) {
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
+            return;
+        }
+        bounds = &outer.getBounds();
+#endif
     }
 
     LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
@@ -2087,10 +2171,17 @@
     const SkRect* bounds = nullptr;
     if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
         const SkRect& pathBounds = path.getBounds();
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         bounds = &paint.computeFastBounds(pathBounds, &storage);
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
+            return;
+        }
+        bounds = &pathBounds;
+#endif
     }
 
     const SkRect& r = path.getBounds();
@@ -2115,12 +2206,22 @@
     SkRect bounds = SkRect::MakeXYWH(x, y,
                                      SkIntToScalar(image->width()), SkIntToScalar(image->height()));
     if (nullptr == paint || paint->canComputeFastBounds()) {
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         if (paint) {
             paint->computeFastBounds(bounds, &bounds);
         }
         if (this->quickReject(bounds)) {
             return;
         }
+#else
+        SkRect tmp = bounds;
+        if (paint) {
+            paint->computeFastBounds(tmp, &tmp);
+        }
+        if (this->quickReject(tmp)) {
+            return;
+        }
+#endif
     }
     
     SkLazyPaint lazy;
@@ -2143,12 +2244,22 @@
     SkRect storage;
     const SkRect* bounds = &dst;
     if (nullptr == paint || paint->canComputeFastBounds()) {
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         if (paint) {
             bounds = &paint->computeFastBounds(dst, &storage);
         }
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        storage = dst;
+        if (paint) {
+            paint->computeFastBounds(dst, &storage);
+        }
+        if (this->quickReject(storage)) {
+            return;
+        }
+#endif
     }
     SkLazyPaint lazy;
     if (nullptr == paint) {
@@ -2185,10 +2296,18 @@
     if (paint->canComputeFastBounds()) {
         bitmap.getBounds(&storage);
         matrix.mapRect(&storage);
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         bounds = &paint->computeFastBounds(storage, &storage);
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        SkRect tmp = storage;
+        if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
+            return;
+        }
+        bounds = &storage;
+#endif
     }
 
     LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
@@ -2211,12 +2330,18 @@
     SkRect storage;
     const SkRect* bounds = &dst;
     if (nullptr == paint || paint->canComputeFastBounds()) {
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         if (paint) {
             bounds = &paint->computeFastBounds(dst, &storage);
         }
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
+            return;
+        }
+#endif
     }
 
     SkLazyPaint lazy;
@@ -2248,12 +2373,18 @@
     SkRect storage;
     const SkRect* bounds = &dst;
     if (nullptr == paint || paint->canComputeFastBounds()) {
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         if (paint) {
             bounds = &paint->computeFastBounds(dst, &storage);
         }
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
+            return;
+        }
+#endif
     }
     
     SkLazyPaint lazy;
@@ -2278,12 +2409,18 @@
     SkRect storage;
     const SkRect* bounds = &dst;
     if (nullptr == paint || paint->canComputeFastBounds()) {
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         if (paint) {
             bounds = &paint->computeFastBounds(dst, &storage);
         }
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
+            return;
+        }
+#endif
     }
     
     SkLazyPaint lazy;
@@ -2456,11 +2593,19 @@
     const SkRect* bounds = nullptr;
     if (paint.canComputeFastBounds()) {
         storage = blob->bounds().makeOffset(x, y);
+#ifdef SK_SAVE_LAYER_BOUNDS_ARE_FILTERED
         bounds = &paint.computeFastBounds(storage, &storage);
 
         if (this->quickReject(*bounds)) {
             return;
         }
+#else
+        SkRect tmp;
+        if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
+            return;
+        }
+        bounds = &storage;
+#endif
     }
 
     // We cannot filter in the looper as we normally do, because the paint is