Fix filter primitive bounds computations.
authorsenorblanco <senorblanco@chromium.org>
Wed, 9 Dec 2015 18:11:43 +0000 (10:11 -0800)
committerCommit bot <commit-bot@chromium.org>
Wed, 9 Dec 2015 18:11:43 +0000 (10:11 -0800)
Make each filter responsible for expanding its destination
bounds. Previously, we were using a union of all
intermediate bounds sizes via join() calls in many image
filters' computeFastBounds(), due to the fact that those
filters could only produce bitmaps the same size as their
inputs. Now, we compute optimal bounds for each filter as
follows:

1) Pass the (unmodified) clip bounds to the root node
of the DAG in the first recursive call to onFilterImage()
as the Context's fClipBounds.

2) Reverse-map the clip: when recursing up the DAG in
filterInput[GPU](), apply filter-specific expansion to the
clip by calling calling onFilterNodeBounds(... kReverse).
This allows upstream nodes to have a clip that respects the
current node's requirements. This is done via helper
function mapContext().

3) Forward-map the source bitmap: just prior to applying
the crop rect in applyCropRect(), we determine the filter's
preferred bounds by mapping the source bitmap bounds
forwards via onFilterNodeBounds(..., kForward).

NOTE: GMs affected by this change:
fast_slow_blurimagefilter: fast and slow paths now produce the same result
spritebitmap: drawSprite() and drawBitmap() paths now produce the same result
filterfastbounds: fast bounds are optimized; all drop-shadow results now appear
apply-filter: snug and not-snug cases give same results
dropshadowimagefilter: drawSprite() results now show shadows
draw-with-filter: no artifacts on erode edges; blur edges no longer clipped
displacement, imagefiltersbase, imagefiltersclipped, imagefilterscropexpand, imagefiltersscaled, matriximagefilter,
resizeimagefilter, localmatriximagefilter, testimagefilters: fixed incorrect clipping
imagefilterstransformed, morphology: no artifacts on erode edges

BUG=skia:1062,skia:3194,skia:3939,skia:4337,skia:4526

Review URL: https://codereview.chromium.org/1308703007

24 files changed:
include/core/SkImageFilter.h
include/effects/SkBlurImageFilter.h
include/effects/SkDisplacementMapEffect.h
include/effects/SkDropShadowImageFilter.h
include/effects/SkMatrixConvolutionImageFilter.h
include/effects/SkMorphologyImageFilter.h
include/effects/SkOffsetImageFilter.h
include/effects/SkTileImageFilter.h
src/core/SkCanvas.cpp
src/core/SkDevice.cpp
src/core/SkImageFilter.cpp
src/core/SkMatrixImageFilter.cpp
src/core/SkMatrixImageFilter.h
src/core/SkRecordDraw.cpp
src/effects/SkBlurImageFilter.cpp
src/effects/SkComposeImageFilter.cpp
src/effects/SkDisplacementMapEffect.cpp
src/effects/SkDropShadowImageFilter.cpp
src/effects/SkMatrixConvolutionImageFilter.cpp
src/effects/SkMorphologyImageFilter.cpp
src/effects/SkOffsetImageFilter.cpp
src/effects/SkTileImageFilter.cpp
src/gpu/SkGpuDevice.cpp
tests/ImageFilterTest.cpp

index 909a2f82840e93c2c6842a3407ef3b67ec3eb893..865fcf8b97b1310e551d35add15f4d9d77d93543 100644 (file)
@@ -346,6 +346,25 @@ protected:
     // implementation recursively unions all input bounds, or returns false if
     // no inputs.
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const;
+    enum MapDirection {
+        kForward_MapDirection,
+        kReverse_MapDirection
+    };
+
+    /**
+     * Performs a forwards or reverse mapping of the given rect to accommodate
+     * this filter's margin requirements. kForward_MapDirection is used to
+     * determine the destination pixels which would be touched by filtering
+     * the given given source rect (e.g., given source bitmap bounds,
+     * determine the optimal bounds of the filtered offscreen bitmap).
+     * kReverse_MapDirection is used to determine which pixels of the
+     * input(s) would be required to fill the given destination rect
+     * (e.g., clip bounds). NOTE: these operations may not be the
+     * inverse of the other. For example, blurring expands the given rect
+     * in both forward and reverse directions. Unlike
+     * onFilterBounds(), this function is non-recursive.
+     */
+    virtual void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const;
 
     // Helper function which invokes filter processing on the input at the
     // specified "index". If the input is null, it leaves "result" and
@@ -405,6 +424,14 @@ protected:
     virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
                                      const SkIRect& bounds) const;
 
+    /**
+     *  Creates a modified Context for use when recursing up the image filter DAG.
+     *  The clip bounds are adjusted to accommodate any margins that this
+     *  filter requires by calling this node's
+     *  onFilterNodeBounds(..., kReverse_MapDirection).
+     */
+    Context mapContext(const Context& ctx) const;
+
 private:
     friend class SkGraphics;
     static void PurgeCache();
index c7193a435f09918be7c86973f74b43e23da5d52a..7f62634a9eb9b6d58bcc2c72c85d5dae2c2bc527 100644 (file)
@@ -27,7 +27,8 @@ protected:
     void flatten(SkWriteBuffer&) const override;
     bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result,
                        SkIPoint* offset) const override;
-    bool onFilterBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst) const override;
+    void onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
+                            SkIRect* dst, MapDirection) const override;
     bool canFilterImageGPU() const override { return true; }
     bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result,
                         SkIPoint* offset) const override;
index 9513a54f15c92e07c2df048cb48b0f82babfb890..253dabe0bdb712f6c44c6120a824f33975c62dda 100644 (file)
@@ -40,6 +40,7 @@ public:
 
     virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
                                 SkIRect* dst) const override;
+    void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override;
 
 #if SK_SUPPORT_GPU
     bool canFilterImageGPU() const override { return true; }
index bf4425e925f30f2784798cc38454d5740d731b06..bff1a42013774ea3784b189fe1447341e156bb51 100644 (file)
@@ -35,7 +35,8 @@ protected:
     void flatten(SkWriteBuffer&) const override;
     bool onFilterImage(Proxy*, const SkBitmap& source, const Context&, SkBitmap* result,
                        SkIPoint* loc) const override;
-    bool onFilterBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst) const override;
+    void onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
+                            SkIRect* dst, MapDirection) const override;
 
 private:
     SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor,
index ef5ff72494be842773b129aedeeccd85441f6d59..09a3acf31a8579797dc3a951deab841d9f33af1f 100644 (file)
@@ -79,7 +79,7 @@ protected:
 
     bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                        SkBitmap* result, SkIPoint* loc) const override;
-    bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const override;
+    void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override;
     bool canComputeFastBounds() const override;
 
 #if SK_SUPPORT_GPU
index 29728fd9f7c4599824baf1dfec6836825d820b2c..422bc019437a8bfb7fd8ea390d5d7c24e333cf63 100644 (file)
@@ -16,7 +16,8 @@
 class SK_API SkMorphologyImageFilter : public SkImageFilter {
 public:
     void computeFastBounds(const SkRect& src, SkRect* dst) const override;
-    bool onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const override;
+    void onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+                            SkIRect* dst, MapDirection) const override;
 
     /**
      * All morphology procs have the same signature: src is the source buffer, dst the
index 40f2ce337470ef438edb5d1addb0c2268b85c1b7..66b5515b3762e106f53c1d527e04d7c29ac10e22 100644 (file)
@@ -30,7 +30,7 @@ protected:
     void flatten(SkWriteBuffer&) const override;
     bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result,
                        SkIPoint* loc) const override;
-    bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const override;
+    void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override;
 
 private:
     SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input, const CropRect*);
index a2a1bb0fe1323cff144f394d6d3cc82b71a67c56..ea75a3ec7f30cbacc7242e0a2a4a088f6a967646 100644 (file)
@@ -25,6 +25,7 @@ public:
                        SkBitmap* dst, SkIPoint* offset) const override;
     bool onFilterBounds(const SkIRect& src, const SkMatrix&,
                         SkIRect* dst) const override;
+    void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override;
     void computeFastBounds(const SkRect& src, SkRect* dst) const override;
 
     SK_TO_STRING_OVERRIDE()
index 3880fa9d733f1cd843a67829ca7099b63d585d87..0c9c20d1eed231c1ac72d8d0cfeb8e1886e759d4 100644 (file)
@@ -40,8 +40,6 @@
 #include "GrRenderTarget.h"
 #endif
 
-#define SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
-
 /*
  *  Return true if the drawing this rect would hit every pixels in the canvas.
  *
@@ -1084,6 +1082,10 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
         } else {
             bounds = nullptr;
         }
+#else
+        if (bounds && !imageFilter->canComputeFastBounds()) {
+            bounds = nullptr;
+        }
 #endif
     }
     SkIRect ir;
@@ -1370,7 +1372,11 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
             const SkBitmap& src = srcDev->accessBitmap(false);
             SkMatrix matrix = *iter.fMatrix;
             matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
             SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
+#else
+            SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y());
+#endif
             SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
             SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
                                        SkImageFilter::kApprox_SizeConstraint);
index cf4a279b355523be1a982af8a989e014beaf7938..cf57c2d7006718aabfdff28cadbc8e21a0915e20 100644 (file)
@@ -411,7 +411,11 @@ void SkBaseDevice::drawBitmapAsSprite(const SkDraw& draw, const SkBitmap& bitmap
         SkIPoint offset = SkIPoint::Make(0, 0);
         SkMatrix matrix = *draw.fMatrix;
         matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
         const SkIRect clipBounds = bitmap.bounds();
+#else
+        const SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y);
+#endif
         SkAutoTUnref<SkImageFilter::Cache> cache(this->getImageFilterCache());
         SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
                                    SkImageFilter::kApprox_SizeConstraint);
index 3b2b27710de57d6fd873aafc637d775f31ddc04b..2bfd14254eac6a87843a4d18666901b75730daaf 100644 (file)
@@ -277,7 +277,7 @@ bool SkImageFilter::filterInput(int index, Proxy* proxy, const SkBitmap& src,
     }
     Context ctx(origCtx.ctm(), origCtx.clipBounds(), origCtx.cache(), constraint);
 
-    return input->filterImage(proxy, src, ctx, result, offset);
+    return input->filterImage(proxy, src, this->mapContext(ctx), result, offset);
 }
 
 bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
@@ -405,7 +405,12 @@ bool SkImageFilter::applyCropRect(const Context& ctx, const SkBitmap& src,
     }
     src.getBounds(srcBounds);
     srcBounds->offset(srcOffset);
-    return fCropRect.applyTo(*srcBounds, ctx, dstBounds) && srcBounds->intersect(*dstBounds);
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
+    return fCropRect.applyTo(*srcBounds, ctx, dstBounds);
+#else
+    this->onFilterNodeBounds(*srcBounds, ctx.ctm(), dstBounds, kForward_MapDirection);
+    return fCropRect.applyTo(*dstBounds, ctx, dstBounds);
+#endif
 }
 
 bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src,
@@ -413,7 +418,13 @@ bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitm
     SkIRect srcBounds;
     src.getBounds(&srcBounds);
     srcBounds.offset(*srcOffset);
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
     if (!fCropRect.applyTo(srcBounds, ctx, bounds)) {
+#else
+    SkIRect dstBounds;
+    this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection);
+    if (!fCropRect.applyTo(dstBounds, ctx, bounds)) {
+#endif
         return false;
     }
 
@@ -441,26 +452,44 @@ bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
         return true;
     }
 
-    SkIRect bounds;
+    SkIRect bounds, totalBounds;
+    this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection);
     for (int i = 0; i < fInputCount; ++i) {
         SkImageFilter* filter = this->getInput(i);
-        SkIRect rect = src;
-        if (filter && !filter->filterBounds(src, ctm, &rect)) {
+        SkIRect rect = bounds;
+        if (filter && !filter->filterBounds(bounds, ctm, &rect)) {
             return false;
         }
         if (0 == i) {
-            bounds = rect;
+            totalBounds = rect;
         } else {
-            bounds.join(rect);
+            totalBounds.join(rect);
         }
     }
 
     // don't modify dst until now, so we don't accidentally change it in the
     // loop, but then return false on the next filter.
-    *dst = bounds;
+    *dst = totalBounds;
     return true;
 }
 
+void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
+                                       SkIRect* dst, MapDirection) const {
+    *dst = src;
+}
+
+
+SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const {
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
+    return ctx;
+#else
+    SkIRect clipBounds;
+    this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds,
+                             MapDirection::kReverse_MapDirection);
+    return Context(ctx.ctm(), clipBounds, ctx.cache(), ctx.sizeConstraint());
+#endif
+}
+
 bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*,
                                         const SkMatrix&, const SkIRect&) const {
     return false;
@@ -506,7 +535,7 @@ bool SkImageFilter::filterInputGPU(int index, SkImageFilter::Proxy* proxy,
     }
     Context ctx(origCtx.ctm(), origCtx.clipBounds(), origCtx.cache(), constraint);
 
-    if (input->filterImage(proxy, src, ctx, result, offset)) {
+    if (input->filterImage(proxy, src, this->mapContext(ctx), result, offset)) {
         if (!result->getTexture()) {
             const SkImageInfo info = result->info();
             if (kUnknown_SkColorType == info.colorType()) {
index 4370ddadee2e42b2d9ef909b55028066ba0ebb5f..4138ccf67759f5704a5e9f43df964419a617980f 100644 (file)
@@ -97,30 +97,33 @@ void SkMatrixImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) cons
         getInput(0)->computeFastBounds(src, &bounds);
     }
     fTransform.mapRect(dst, bounds);
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
     dst->join(bounds);   // Work around for skia:3194
+#endif
 }
 
-bool SkMatrixImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                         SkIRect* dst) const {
-    SkMatrix transformInverse;
-    if (!fTransform.invert(&transformInverse)) {
-        return false;
-    }
+void SkMatrixImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+                                             SkIRect* dst, MapDirection direction) const {
     SkMatrix matrix;
     if (!ctm.invert(&matrix)) {
-        return false;
+        *dst = src;
+        return;
+    }
+    if (kForward_MapDirection == direction) {
+        matrix.postConcat(fTransform);
+    } else {
+        SkMatrix transformInverse;
+        if (!fTransform.invert(&transformInverse)) {
+            *dst = src;
+            return;
+        }
+        matrix.postConcat(transformInverse);
     }
-    matrix.postConcat(transformInverse);
     matrix.postConcat(ctm);
     SkRect floatBounds;
     matrix.mapRect(&floatBounds, SkRect::Make(src));
     SkIRect bounds = floatBounds.roundOut();
-    if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
-        return false;
-    }
-
     *dst = bounds;
-    return true;
 }
 
 #ifndef SK_IGNORE_TO_STRING
index 86734b629cfdc0ad95073d3bfad992080a23b055..09bfca9fe128782f9eea1a1f5ee3e25b58f25fe8 100644 (file)
@@ -46,8 +46,8 @@ protected:
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* loc) const override;
-    virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
-                                SkIRect* dst) const override;
+    virtual void onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
+                                    SkIRect* dst, MapDirection) const override;
 
 private:
     SkMatrix              fTransform;
index b9bf92c0bad77b9a73ff7711d7e24b905e822491..849e8f92b249aa422cb3b538b6f06f2a10d9d2c1 100644 (file)
@@ -221,6 +221,7 @@ private:
         int controlOps;        // Number of control ops in this Save block, including the Save.
         Bounds bounds;         // Bounds of everything in the block.
         const SkPaint* paint;  // Unowned.  If set, adjusts the bounds of all ops in this block.
+        SkMatrix ctm;
     };
 
     // Only Restore, SetMatrix, and Concat change the CTM.
@@ -301,6 +302,7 @@ private:
         sb.bounds =
             PaintMayAffectTransparentBlack(paint) ? fCurrentClipBounds : Bounds::MakeEmpty();
         sb.paint = paint;
+        sb.ctm = this->fCTM;
 
         fSaveStack.push(sb);
         this->pushControl();
@@ -563,9 +565,15 @@ private:
 
     bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const {
         for (int i = fSaveStack.count() - 1 - savesToIgnore; i >= 0; i--) {
+            SkMatrix inverse;
+            if (!fSaveStack[i].ctm.invert(&inverse)) {
+                return false;
+            }
+            inverse.mapRect(rect);
             if (!AdjustForPaint(fSaveStack[i].paint, rect)) {
                 return false;
             }
+            fSaveStack[i].ctm.mapRect(rect);
         }
         return true;
     }
index 928793de62fcf0b2013af246a197a1193cf76a40..752749b873f8a36e73f71800feedddaaa88d20c3 100644 (file)
@@ -82,7 +82,10 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
     }
 
     SkIRect srcBounds, dstBounds;
-    if (!this->applyCropRect(ctx, src, srcOffset, &dstBounds, &srcBounds)) {
+    if (!this->applyCropRect(this->mapContext(ctx), src, srcOffset, &dstBounds, &srcBounds)) {
+        return false;
+    }
+    if (!srcBounds.intersect(dstBounds)) {
         return false;
     }
 
@@ -184,17 +187,12 @@ void SkBlurImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const
                 SkScalarMul(fSigma.height(), SkIntToScalar(3)));
 }
 
-bool SkBlurImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                       SkIRect* dst) const {
-    SkIRect bounds = src;
+void SkBlurImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+                                           SkIRect* dst, MapDirection) const {
+    *dst = src;
     SkVector sigma = mapSigma(fSigma, ctm);
-    bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
-                  SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
-    if (this->getInput(0) && !this->getInput(0)->filterBounds(bounds, ctm, &bounds)) {
-        return false;
-    }
-    *dst = bounds;
-    return true;
+    dst->outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
+                SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
 }
 
 bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
@@ -206,7 +204,10 @@ bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const
         return false;
     }
     SkIRect srcBounds, dstBounds;
-    if (!this->applyCropRect(ctx, input, srcOffset, &dstBounds, &srcBounds)) {
+    if (!this->applyCropRect(this->mapContext(ctx), input, srcOffset, &dstBounds, &srcBounds)) {
+        return false;
+    }
+    if (!srcBounds.intersect(dstBounds)) {
         return false;
     }
     GrTexture* source = input.getTexture();
index 1be03a337084e3abfe31d84ffcc21fc6791f7a35..59159551ccafee97ccba7c3f45cfe78ea799f63a 100644 (file)
@@ -35,7 +35,9 @@ bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
 
     SkMatrix outerMatrix(ctx.ctm());
     outerMatrix.postTranslate(SkIntToScalar(-innerOffset.x()), SkIntToScalar(-innerOffset.y()));
-    Context outerContext(outerMatrix, ctx.clipBounds(), ctx.cache(), ctx.sizeConstraint());
+    SkIRect clipBounds = ctx.clipBounds();
+    clipBounds.offset(-innerOffset.x(), -innerOffset.y());
+    Context outerContext(outerMatrix, clipBounds, ctx.cache(), ctx.sizeConstraint());
     if (!this->filterInput(0, proxy, tmp, outerContext, result, &outerOffset, false)) {
         return false;
     }
index 3370a76495c17d4dd28ecd48a584401c0ef9dfa7..a3fff39b46f062dc1f0a93293f52ab8991305f91 100644 (file)
@@ -271,13 +271,19 @@ void SkDisplacementMapEffect::computeFastBounds(const SkRect& src, SkRect* dst)
     dst->outset(fScale * SK_ScalarHalf, fScale * SK_ScalarHalf);
 }
 
-bool SkDisplacementMapEffect::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                   SkIRect* dst) const {
-    SkIRect bounds = src;
+void SkDisplacementMapEffect::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+                                   SkIRect* dst, MapDirection) const {
+    *dst = src;
     SkVector scale = SkVector::Make(fScale, fScale);
     ctm.mapVectors(&scale, 1);
-    bounds.outset(SkScalarCeilToInt(scale.fX * SK_ScalarHalf),
-                  SkScalarCeilToInt(scale.fY * SK_ScalarHalf));
+    dst->outset(SkScalarCeilToInt(scale.fX * SK_ScalarHalf),
+                SkScalarCeilToInt(scale.fY * SK_ScalarHalf));
+}
+
+bool SkDisplacementMapEffect::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+                                   SkIRect* dst) const {
+    SkIRect bounds;
+    this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection);
     if (this->getColorInput()) {
         return this->getColorInput()->filterBounds(bounds, ctm, dst);
     }
index 7519d5313d08f956206aff541493bbdb2225fa38..eb05cf09eca99dfdbf0e381050fb6ae1b0074a42 100644 (file)
@@ -116,25 +116,23 @@ void SkDropShadowImageFilter::computeFastBounds(const SkRect& src, SkRect* dst)
     }
 }
 
-bool SkDropShadowImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                             SkIRect* dst) const {
-    SkIRect bounds = src;
+void SkDropShadowImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+                                                 SkIRect* dst, MapDirection direction) const {
+    *dst = src;
     SkVector offsetVec = SkVector::Make(fDx, fDy);
+    if (kReverse_MapDirection == direction) {
+        offsetVec.negate();
+    }
     ctm.mapVectors(&offsetVec, 1);
-    bounds.offset(-SkScalarCeilToInt(offsetVec.x()),
-                  -SkScalarCeilToInt(offsetVec.y()));
+    dst->offset(SkScalarCeilToInt(offsetVec.x()),
+                SkScalarCeilToInt(offsetVec.y()));
     SkVector sigma = SkVector::Make(fSigmaX, fSigmaY);
     ctm.mapVectors(&sigma, 1);
-    bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
-                  SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
+    dst->outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
+                SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
     if (fShadowMode == kDrawShadowAndForeground_ShadowMode) {
-        bounds.join(src);
-    }
-    if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
-        return false;
+        dst->join(src);
     }
-    *dst = bounds;
-    return true;
 }
 
 #ifndef SK_IGNORE_TO_STRING
index 7c5dd8368f439e528ca06691d3f9ff3bb9273ad0..a1f23f7a290b8b0837fd3ca1efc7f9e02a48cd1c 100644 (file)
@@ -280,7 +280,7 @@ bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
     }
 
     SkIRect bounds;
-    if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
+    if (!this->applyCropRect(this->mapContext(ctx), proxy, src, &srcOffset, &bounds, &src)) {
         return false;
     }
 
@@ -322,17 +322,17 @@ bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
     return true;
 }
 
-bool SkMatrixConvolutionImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                                    SkIRect* dst) const {
-    SkIRect bounds = src;
-    bounds.fRight += fKernelSize.width() - 1;
-    bounds.fBottom += fKernelSize.height() - 1;
-    bounds.offset(-fKernelOffset);
-    if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
-        return false;
+void SkMatrixConvolutionImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+                                                    SkIRect* dst, MapDirection direction) const {
+    *dst = src;
+    int w = fKernelSize.width() - 1, h = fKernelSize.height() - 1;
+    dst->fRight += w;
+    dst->fBottom += h;
+    if (kReverse_MapDirection == direction) {
+        dst->offset(-fKernelOffset);
+    } else {
+        dst->offset(fKernelOffset - SkIPoint::Make(w, h));
     }
-    *dst = bounds;
-    return true;
 }
 
 bool SkMatrixConvolutionImageFilter::canComputeFastBounds() const {
index c6bbce71cb589f0f833a4c83d95266f47fd21a9c..205fc0d1eb2c71d924d8badd35ee4c518b3dd3a3 100644 (file)
@@ -70,7 +70,7 @@ bool SkMorphologyImageFilter::filterImageGeneric(SkMorphologyImageFilter::Proc p
     }
 
     SkIRect bounds;
-    if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
+    if (!this->applyCropRect(this->mapContext(ctx), proxy, src, &srcOffset, &bounds, &src)) {
         return false;
     }
 
@@ -149,18 +149,13 @@ void SkMorphologyImageFilter::computeFastBounds(const SkRect& src, SkRect* dst)
     dst->outset(SkIntToScalar(fRadius.width()), SkIntToScalar(fRadius.height()));
 }
 
-bool SkMorphologyImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                             SkIRect* dst) const {
-    SkIRect bounds = src;
+void SkMorphologyImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+                                                 SkIRect* dst, MapDirection) const {
+    *dst = src;
     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
                                      SkIntToScalar(this->radius().height()));
     ctm.mapVectors(&radius, 1);
-    bounds.outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y()));
-    if (this->getInput(0) && !this->getInput(0)->filterBounds(bounds, ctm, &bounds)) {
-        return false;
-    }
-    *dst = bounds;
-    return true;
+    dst->outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y()));
 }
 
 SkFlattenable* SkErodeImageFilter::CreateProc(SkReadBuffer& buffer) {
@@ -637,7 +632,7 @@ bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate,
         return false;
     }
     SkIRect bounds;
-    if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
+    if (!this->applyCropRect(this->mapContext(ctx), proxy, input, &srcOffset, &bounds, &input)) {
         return false;
     }
     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
index 9da026b2793b8998a321cacb20cb9cf6d1e4591b..c4fc5ebe304eea1eb02da771489527221376515f 100644 (file)
@@ -70,24 +70,28 @@ void SkOffsetImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) cons
     } else {
         *dst = src;
     }
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
     SkRect copy = *dst;
+#endif
     dst->offset(fOffset.fX, fOffset.fY);
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
     dst->join(copy);
+#endif
 }
 
-bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                         SkIRect* dst) const {
+void SkOffsetImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+                                             SkIRect* dst, MapDirection direction) const {
     SkVector vec;
     ctm.mapVectors(&vec, &fOffset, 1);
-
-    SkIRect bounds = src;
-    bounds.offset(-SkScalarCeilToInt(vec.fX), -SkScalarCeilToInt(vec.fY));
-    bounds.join(src);
-    if (getInput(0)) {
-        return getInput(0)->filterBounds(bounds, ctm, dst);
+    if (kReverse_MapDirection == direction) {
+        vec.negate();
     }
-    *dst = bounds;
-    return true;
+
+    *dst = src;
+    dst->offset(SkScalarCeilToInt(vec.fX), SkScalarCeilToInt(vec.fY));
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
+    dst->join(src);
+#endif
 }
 
 SkFlattenable* SkOffsetImageFilter::CreateProc(SkReadBuffer& buffer) {
index 52ea6a756f1cfcf8e78da08debb869d0224142a7..8ef617d520f96dfc05c77a3d3b67dc02fdbc1b23 100644 (file)
@@ -81,21 +81,30 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
     return true;
 }
 
+void SkTileImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+                                          SkIRect* dst, MapDirection direction) const {
+    SkRect rect = kReverse_MapDirection == direction ? fSrcRect : fDstRect;
+    ctm.mapRect(&rect);
+    rect.roundOut(dst);
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
+    dst->join(src);
+#endif
+}
+
 bool SkTileImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
                                        SkIRect* dst) const {
-    SkRect srcRect;
-    ctm.mapRect(&srcRect, fSrcRect);
-    SkIRect srcIRect;
-    srcRect.roundOut(&srcIRect);
-    srcIRect.join(src);
-    *dst = srcIRect;
+    this->onFilterNodeBounds(src, ctm, dst, kReverse_MapDirection);
     return true;
 }
 
 void SkTileImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
     // This is a workaround for skia:3194.
     *dst = src;
     dst->join(fDstRect);
+#else
+    *dst = fDstRect;
+#endif
 }
 
 SkFlattenable* SkTileImageFilter::CreateProc(SkReadBuffer& buffer) {
index 2545313e92655c99f3255a55420cbfd2515460bb..52f730a01707f48de1a25bfb7138cf46e6e4336a 100644 (file)
@@ -1164,7 +1164,11 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
         SkIPoint offset = SkIPoint::Make(0, 0);
         SkMatrix matrix(*draw.fMatrix);
         matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
         SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+#else
+        SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-left, -top);
+#endif
         SkAutoTUnref<SkImageFilter::Cache> cache(getImageFilterCache());
         // This cache is transient, and is freed (along with all its contained
         // textures) when it goes out of scope.
@@ -1327,7 +1331,11 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
         SkIPoint offset = SkIPoint::Make(0, 0);
         SkMatrix matrix(*draw.fMatrix);
         matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
         SkIRect clipBounds = SkIRect::MakeWH(devTex->width(), devTex->height());
+#else
+        SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y);
+#endif
         // This cache is transient, and is freed (along with all its contained
         // textures) when it goes out of scope.
         SkAutoTUnref<SkImageFilter::Cache> cache(getImageFilterCache());
index 2619c6ff10200199354b8674d4638b755379b835..6a6a7a92d645ac56bcb6d3e702cbf4c5e89a237e 100644 (file)
@@ -1152,8 +1152,10 @@ DEF_TEST(ComposedImageFilterOffset, reporter) {
 
     SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20));
     SkAutoTUnref<SkImageFilter> offsetFilter(SkOffsetImageFilter::Create(0, 0, nullptr, &cropRect));
-    SkAutoTUnref<SkImageFilter> blurFilter(makeBlur());
-    SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(blurFilter, offsetFilter.get()));
+    SkAutoTUnref<SkImageFilter> blurFilter(SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1,
+                                                                     nullptr, &cropRect));
+    SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(blurFilter,
+                                                                            offsetFilter.get()));
     SkBitmap result;
     SkIPoint offset;
     SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, SkImageFilter::kApprox_SizeConstraint);