From db64af3b178a19ecb47d2b9a373113687d8921fd Mon Sep 17 00:00:00 2001 From: senorblanco Date: Wed, 9 Dec 2015 10:11:43 -0800 Subject: [PATCH] Fix filter primitive bounds computations. 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 --- include/core/SkImageFilter.h | 27 +++++++++++ include/effects/SkBlurImageFilter.h | 3 +- include/effects/SkDisplacementMapEffect.h | 1 + include/effects/SkDropShadowImageFilter.h | 3 +- .../effects/SkMatrixConvolutionImageFilter.h | 2 +- include/effects/SkMorphologyImageFilter.h | 3 +- include/effects/SkOffsetImageFilter.h | 2 +- include/effects/SkTileImageFilter.h | 1 + src/core/SkCanvas.cpp | 10 +++- src/core/SkDevice.cpp | 4 ++ src/core/SkImageFilter.cpp | 47 +++++++++++++++---- src/core/SkMatrixImageFilter.cpp | 29 +++++++----- src/core/SkMatrixImageFilter.h | 4 +- src/core/SkRecordDraw.cpp | 8 ++++ src/effects/SkBlurImageFilter.cpp | 25 +++++----- src/effects/SkComposeImageFilter.cpp | 4 +- src/effects/SkDisplacementMapEffect.cpp | 16 +++++-- src/effects/SkDropShadowImageFilter.cpp | 24 +++++----- .../SkMatrixConvolutionImageFilter.cpp | 22 ++++----- src/effects/SkMorphologyImageFilter.cpp | 17 +++---- src/effects/SkOffsetImageFilter.cpp | 24 ++++++---- src/effects/SkTileImageFilter.cpp | 21 ++++++--- src/gpu/SkGpuDevice.cpp | 8 ++++ tests/ImageFilterTest.cpp | 6 ++- 24 files changed, 209 insertions(+), 102 deletions(-) diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h index 909a2f8284..865fcf8b97 100644 --- a/include/core/SkImageFilter.h +++ b/include/core/SkImageFilter.h @@ -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(); diff --git a/include/effects/SkBlurImageFilter.h b/include/effects/SkBlurImageFilter.h index c7193a435f..7f62634a9e 100644 --- a/include/effects/SkBlurImageFilter.h +++ b/include/effects/SkBlurImageFilter.h @@ -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; diff --git a/include/effects/SkDisplacementMapEffect.h b/include/effects/SkDisplacementMapEffect.h index 9513a54f15..253dabe0bd 100644 --- a/include/effects/SkDisplacementMapEffect.h +++ b/include/effects/SkDisplacementMapEffect.h @@ -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; } diff --git a/include/effects/SkDropShadowImageFilter.h b/include/effects/SkDropShadowImageFilter.h index bf4425e925..bff1a42013 100644 --- a/include/effects/SkDropShadowImageFilter.h +++ b/include/effects/SkDropShadowImageFilter.h @@ -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, diff --git a/include/effects/SkMatrixConvolutionImageFilter.h b/include/effects/SkMatrixConvolutionImageFilter.h index ef5ff72494..09a3acf31a 100644 --- a/include/effects/SkMatrixConvolutionImageFilter.h +++ b/include/effects/SkMatrixConvolutionImageFilter.h @@ -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 diff --git a/include/effects/SkMorphologyImageFilter.h b/include/effects/SkMorphologyImageFilter.h index 29728fd9f7..422bc01943 100644 --- a/include/effects/SkMorphologyImageFilter.h +++ b/include/effects/SkMorphologyImageFilter.h @@ -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 diff --git a/include/effects/SkOffsetImageFilter.h b/include/effects/SkOffsetImageFilter.h index 40f2ce3374..66b5515b37 100644 --- a/include/effects/SkOffsetImageFilter.h +++ b/include/effects/SkOffsetImageFilter.h @@ -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*); diff --git a/include/effects/SkTileImageFilter.h b/include/effects/SkTileImageFilter.h index a2a1bb0fe1..ea75a3ec7f 100644 --- a/include/effects/SkTileImageFilter.h +++ b/include/effects/SkTileImageFilter.h @@ -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() diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 3880fa9d73..0c9c20d1ee 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -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 cache(dstDev->getImageFilterCache()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), SkImageFilter::kApprox_SizeConstraint); diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index cf4a279b35..cf57c2d700 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -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 cache(this->getImageFilterCache()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), SkImageFilter::kApprox_SizeConstraint); diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp index 3b2b27710d..2bfd14254e 100644 --- a/src/core/SkImageFilter.cpp +++ b/src/core/SkImageFilter.cpp @@ -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()) { diff --git a/src/core/SkMatrixImageFilter.cpp b/src/core/SkMatrixImageFilter.cpp index 4370ddadee..4138ccf677 100644 --- a/src/core/SkMatrixImageFilter.cpp +++ b/src/core/SkMatrixImageFilter.cpp @@ -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 diff --git a/src/core/SkMatrixImageFilter.h b/src/core/SkMatrixImageFilter.h index 86734b629c..09bfca9fe1 100644 --- a/src/core/SkMatrixImageFilter.h +++ b/src/core/SkMatrixImageFilter.h @@ -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; diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp index b9bf92c0ba..849e8f92b2 100644 --- a/src/core/SkRecordDraw.cpp +++ b/src/core/SkRecordDraw.cpp @@ -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; } diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp index 928793de62..752749b873 100644 --- a/src/effects/SkBlurImageFilter.cpp +++ b/src/effects/SkBlurImageFilter.cpp @@ -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(); diff --git a/src/effects/SkComposeImageFilter.cpp b/src/effects/SkComposeImageFilter.cpp index 1be03a3370..59159551cc 100644 --- a/src/effects/SkComposeImageFilter.cpp +++ b/src/effects/SkComposeImageFilter.cpp @@ -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; } diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp index 3370a76495..a3fff39b46 100644 --- a/src/effects/SkDisplacementMapEffect.cpp +++ b/src/effects/SkDisplacementMapEffect.cpp @@ -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); } diff --git a/src/effects/SkDropShadowImageFilter.cpp b/src/effects/SkDropShadowImageFilter.cpp index 7519d5313d..eb05cf09ec 100644 --- a/src/effects/SkDropShadowImageFilter.cpp +++ b/src/effects/SkDropShadowImageFilter.cpp @@ -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); + dst->join(src); } - if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) { - return false; - } - *dst = bounds; - return true; } #ifndef SK_IGNORE_TO_STRING diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp index 7c5dd8368f..a1f23f7a29 100644 --- a/src/effects/SkMatrixConvolutionImageFilter.cpp +++ b/src/effects/SkMatrixConvolutionImageFilter.cpp @@ -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 { diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp index c6bbce71cb..205fc0d1eb 100644 --- a/src/effects/SkMorphologyImageFilter.cpp +++ b/src/effects/SkMorphologyImageFilter.cpp @@ -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()), diff --git a/src/effects/SkOffsetImageFilter.cpp b/src/effects/SkOffsetImageFilter.cpp index 9da026b279..c4fc5ebe30 100644 --- a/src/effects/SkOffsetImageFilter.cpp +++ b/src/effects/SkOffsetImageFilter.cpp @@ -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) { diff --git a/src/effects/SkTileImageFilter.cpp b/src/effects/SkTileImageFilter.cpp index 52ea6a756f..8ef617d520 100644 --- a/src/effects/SkTileImageFilter.cpp +++ b/src/effects/SkTileImageFilter.cpp @@ -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) { diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 2545313e92..52f730a017 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -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 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 cache(getImageFilterCache()); diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp index 2619c6ff10..6a6a7a92d6 100644 --- a/tests/ImageFilterTest.cpp +++ b/tests/ImageFilterTest.cpp @@ -1152,8 +1152,10 @@ DEF_TEST(ComposedImageFilterOffset, reporter) { SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20)); SkAutoTUnref offsetFilter(SkOffsetImageFilter::Create(0, 0, nullptr, &cropRect)); - SkAutoTUnref blurFilter(makeBlur()); - SkAutoTUnref composedFilter(SkComposeImageFilter::Create(blurFilter, offsetFilter.get())); + SkAutoTUnref blurFilter(SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, + nullptr, &cropRect)); + SkAutoTUnref composedFilter(SkComposeImageFilter::Create(blurFilter, + offsetFilter.get())); SkBitmap result; SkIPoint offset; SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, SkImageFilter::kApprox_SizeConstraint);