From c4b12f19a46946e1c02f3525e0ea4902b09feac5 Mon Sep 17 00:00:00 2001 From: "senorblanco@chromium.org" Date: Wed, 5 Feb 2014 17:51:22 +0000 Subject: [PATCH] Implement correct clipping for image filters. Image filters in Skia currently clip the size of the the offscreen bitmap used for filtering to the device clip bounds. This means that any pixel-moving filter (e.g., blur) has edge artifacts at the clip boundaries. This is problematic for tiling, where a single SkPicture is played back with a clip set to the tile boundaries. By implementing the onFilterBounds() traversal, and using it in saveLayer() when a filter is present, we can clip the layer to the expanded clip rect. Note that this requires that the traversal be performed in reverse as compared to computeFastBounds(). (It's also done in device space, unlike computeFastBounds()). New test imagefiltersclipped tests pixel-moving filters when clipped by various clip rects. New test imageblurtiled tests tiled (compositor-style) rendering of blurred text. There should be no artifacts at the tile boundaries. BUG=337831 R=reed@google.com Review URL: https://codereview.chromium.org/23011012 git-svn-id: http://skia.googlecode.com/svn/trunk@13323 2bbb7eff-a529-9590-31e7-b0007b416f81 --- expectations/gm/ignored-tests.txt | 14 +++ gm/imageblurtiled.cpp | 76 ++++++++++++ gm/imagefiltersclipped.cpp | 143 ++++++++++++++++++++++ gm/imagefiltersscaled.cpp | 6 +- gyp/gmslides.gypi | 2 + include/core/SkCanvas.h | 5 +- include/core/SkImageFilter.h | 12 +- include/effects/SkBitmapSource.h | 1 + include/effects/SkBlurImageFilter.h | 2 + include/effects/SkComposeImageFilter.h | 2 +- include/effects/SkDisplacementMapEffect.h | 3 + include/effects/SkDropShadowImageFilter.h | 4 + include/effects/SkMergeImageFilter.h | 2 - include/effects/SkMorphologyImageFilter.h | 1 + include/effects/SkOffsetImageFilter.h | 2 +- src/core/SkCanvas.cpp | 15 ++- src/core/SkImageFilter.cpp | 26 +++- src/effects/SkBitmapSource.cpp | 6 + src/effects/SkBlurImageFilter.cpp | 15 +++ src/effects/SkComposeImageFilter.cpp | 2 +- src/effects/SkDisplacementMapEffect.cpp | 9 ++ src/effects/SkDropShadowImageFilter.cpp | 19 +++ src/effects/SkMergeImageFilter.cpp | 32 ----- src/effects/SkMorphologyImageFilter.cpp | 14 +++ src/effects/SkOffsetImageFilter.cpp | 7 +- 25 files changed, 369 insertions(+), 51 deletions(-) create mode 100644 gm/imageblurtiled.cpp create mode 100644 gm/imagefiltersclipped.cpp diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index b6c9107e49..f4bb10af40 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -40,3 +40,17 @@ filltypespersp # deprecated calling pattern. # https://codereview.chromium.org/154163002/ extractbitmap + +# Added by senorblanco as part of https://codereview.chromium.org/23011012/ +colorfilterimagefilter +dropshadowimagefilter +imageblur +imageblur_large +imagefiltersbase +imagefilterscropped +imagefiltersgraph +imagefiltersscaled +morphology +offsetimagefilter +spritebitmap +xfermodeimagefilter diff --git a/gm/imageblurtiled.cpp b/gm/imageblurtiled.cpp new file mode 100644 index 0000000000..96df4367b4 --- /dev/null +++ b/gm/imageblurtiled.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "SkBlurImageFilter.h" +#include "SkRandom.h" + +#define WIDTH 640 +#define HEIGHT 480 + +namespace skiagm { + +class ImageBlurTiledGM : public GM { +public: + ImageBlurTiledGM(SkScalar sigmaX, SkScalar sigmaY) + : fSigmaX(sigmaX), fSigmaY(sigmaY) { + } + +protected: + virtual SkString onShortName() { + return SkString("imageblurtiled"); + } + + virtual SkISize onISize() { + return make_isize(WIDTH, HEIGHT); + } + + virtual void onDraw(SkCanvas* canvas) { + SkPaint paint; + paint.setImageFilter(new SkBlurImageFilter(fSigmaX, fSigmaY))->unref(); + const SkScalar tile_size = SkIntToScalar(128); + SkRect bounds; + canvas->getClipBounds(&bounds); + for (SkScalar y = bounds.top(); y < bounds.bottom(); y += tile_size) { + for (SkScalar x = bounds.left(); x < bounds.right(); x += tile_size) { + canvas->save(); + canvas->clipRect(SkRect::MakeXYWH(x, y, tile_size, tile_size)); + canvas->saveLayer(NULL, &paint); + const char* str[] = { + "The quick", + "brown fox", + "jumped over", + "the lazy dog.", + }; + SkPaint textPaint; + textPaint.setAntiAlias(true); + textPaint.setTextSize(SkIntToScalar(100)); + int posY = 0; + for (unsigned i = 0; i < SK_ARRAY_COUNT(str); i++) { + posY += 100; + canvas->drawText(str[i], strlen(str[i]), SkIntToScalar(0), + SkIntToScalar(posY), textPaint); + } + canvas->restore(); + canvas->restore(); + } + } + } + +private: + SkScalar fSigmaX; + SkScalar fSigmaY; + + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory1(void*) { return new ImageBlurTiledGM(3.0f, 3.0f); } +static GMRegistry reg1(MyFactory1); + +} diff --git a/gm/imagefiltersclipped.cpp b/gm/imagefiltersclipped.cpp new file mode 100644 index 0000000000..514fc20363 --- /dev/null +++ b/gm/imagefiltersclipped.cpp @@ -0,0 +1,143 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "SkColor.h" +#include "SkBitmapSource.h" +#include "SkBlurImageFilter.h" +#include "SkDisplacementMapEffect.h" +#include "SkDropShadowImageFilter.h" +#include "SkGradientShader.h" +#include "SkMorphologyImageFilter.h" +#include "SkOffsetImageFilter.h" +#include "SkScalar.h" + +namespace skiagm { + +class ImageFiltersClippedGM : public GM { +public: + ImageFiltersClippedGM() : fInitialized(false) { + this->setBGColor(0x00000000); + } + +protected: + virtual SkString onShortName() { + return SkString("imagefiltersclipped"); + } + + virtual SkISize onISize() { + return make_isize(860, 500); + } + + void make_checkerboard() { + fCheckerboard.allocN32Pixels(64, 64); + SkBitmapDevice device(fCheckerboard); + SkCanvas canvas(&device); + canvas.clear(0x00000000); + SkPaint darkPaint; + darkPaint.setColor(0xFF404040); + SkPaint lightPaint; + lightPaint.setColor(0xFFA0A0A0); + for (int y = 0; y < 64; y += 16) { + for (int x = 0; x < 64; x += 16) { + canvas.save(); + canvas.translate(SkIntToScalar(x), SkIntToScalar(y)); + canvas.drawRect(SkRect::MakeXYWH(0, 0, 8, 8), darkPaint); + canvas.drawRect(SkRect::MakeXYWH(8, 0, 8, 8), lightPaint); + canvas.drawRect(SkRect::MakeXYWH(0, 8, 8, 8), lightPaint); + canvas.drawRect(SkRect::MakeXYWH(8, 8, 8, 8), darkPaint); + canvas.restore(); + } + } + } + + void make_gradient_circle(int width, int height) { + SkScalar x = SkIntToScalar(width / 2); + SkScalar y = SkIntToScalar(height / 2); + SkScalar radius = SkScalarMul(SkMinScalar(x, y), SkIntToScalar(4) / SkIntToScalar(5)); + fGradientCircle.allocN32Pixels(width, height); + SkBitmapDevice device(fGradientCircle); + SkCanvas canvas(&device); + canvas.clear(0x00000000); + SkColor colors[2]; + colors[0] = SK_ColorWHITE; + colors[1] = SK_ColorBLACK; + SkAutoTUnref shader( + SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, NULL, 2, + SkShader::kClamp_TileMode) + ); + SkPaint paint; + paint.setShader(shader); + canvas.drawCircle(x, y, radius, paint); + } + + virtual void onDraw(SkCanvas* canvas) { + if (!fInitialized) { + this->make_checkerboard(); + this->make_gradient_circle(64, 64); + fInitialized = true; + } + canvas->clear(0x00000000); + + SkAutoTUnref gradient(new SkBitmapSource(fGradientCircle)); + SkAutoTUnref checkerboard(new SkBitmapSource(fCheckerboard)); + + SkImageFilter* filters[] = { + new SkBlurImageFilter(SkIntToScalar(12), SkIntToScalar(12)), + new SkDropShadowImageFilter(SkIntToScalar(10), SkIntToScalar(10), SkIntToScalar(3), + SK_ColorGREEN), + new SkDisplacementMapEffect(SkDisplacementMapEffect::kR_ChannelSelectorType, + SkDisplacementMapEffect::kR_ChannelSelectorType, + SkIntToScalar(12), + gradient.get(), + checkerboard.get()), + new SkDilateImageFilter(2, 2, checkerboard.get()), + new SkErodeImageFilter(2, 2, checkerboard.get()), + new SkOffsetImageFilter(SkIntToScalar(-16), SkIntToScalar(32)), + }; + + SkRect r = SkRect::MakeWH(SkIntToScalar(64), SkIntToScalar(64)); + SkScalar margin = SkIntToScalar(16); + SkRect bounds = r; + bounds.outset(margin, margin); + + for (int xOffset = 0; xOffset < 80; xOffset += 16) { + canvas->save(); + bounds.fLeft = SkIntToScalar(xOffset); + for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) { + SkPaint paint; + paint.setColor(SK_ColorWHITE); + paint.setImageFilter(filters[i]); + paint.setAntiAlias(true); + canvas->save(); + canvas->clipRect(bounds); + if (i == 5) { + canvas->translate(SkIntToScalar(16), SkIntToScalar(-32)); + } + canvas->drawCircle(r.centerX(), r.centerY(), + SkScalarDiv(r.width()*2, SkIntToScalar(5)), paint); + canvas->restore(); + canvas->translate(r.width() + margin, 0); + } + canvas->restore(); + canvas->translate(0, r.height() + margin); + } + } + +private: + bool fInitialized; + SkBitmap fCheckerboard; + SkBitmap fGradientCircle; + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory(void*) { return new ImageFiltersClippedGM; } +static GMRegistry reg(MyFactory); + +} diff --git a/gm/imagefiltersscaled.cpp b/gm/imagefiltersscaled.cpp index ad52d902e7..a9c4a10587 100644 --- a/gm/imagefiltersscaled.cpp +++ b/gm/imagefiltersscaled.cpp @@ -13,6 +13,7 @@ #include "SkDropShadowImageFilter.h" #include "SkGradientShader.h" #include "SkMorphologyImageFilter.h" +#include "SkOffsetImageFilter.h" #include "SkScalar.h" namespace skiagm { @@ -96,6 +97,7 @@ protected: checkerboard.get()), new SkDilateImageFilter(1, 1, checkerboard.get()), new SkErodeImageFilter(1, 1, checkerboard.get()), + new SkOffsetImageFilter(SkIntToScalar(32), 0), }; SkVector scales[] = { @@ -120,7 +122,9 @@ protected: paint.setAntiAlias(true); canvas->save(); canvas->scale(scales[j].fX, scales[j].fY); - canvas->clipRect(bounds); + if (5 == i) { + canvas->translate(SkIntToScalar(-32), 0); + } canvas->drawCircle(r.centerX(), r.centerY(), SkScalarDiv(r.width()*2, SkIntToScalar(5)), paint); canvas->restore(); diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index c6c11e36bf..30fd5e6b30 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -88,6 +88,7 @@ '../gm/hittestpath.cpp', '../gm/imagealphathreshold.cpp', '../gm/imageblur.cpp', + '../gm/imageblurtiled.cpp', '../gm/imagemagnifier.cpp', '../gm/inversepaths.cpp', '../gm/lerpmode.cpp', @@ -95,6 +96,7 @@ '../gm/lumafilter.cpp', '../gm/image.cpp', '../gm/imagefiltersbase.cpp', + '../gm/imagefiltersclipped.cpp', '../gm/imagefilterscropped.cpp', '../gm/imagefiltersgraph.cpp', '../gm/imagefiltersscaled.cpp', diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index af42c129ed..e6c9ee2fab 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -1039,8 +1039,11 @@ protected: // Clip rectangle bounds. Called internally by saveLayer. // returns false if the entire rectangle is entirely clipped out + // If non-NULL, The imageFilter parameter will be used to expand the clip + // and offscreen bounds for any margin required by the filter DAG. bool clipRectBounds(const SkRect* bounds, SaveFlags flags, - SkIRect* intersection); + SkIRect* intersection, + const SkImageFilter* imageFilter = NULL); // Called by child classes that override clipPath and clipRRect to only // track fast conservative clip bounds, rather than exact clips. diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h index 37993c0ea3..6b53987891 100644 --- a/include/core/SkImageFilter.h +++ b/include/core/SkImageFilter.h @@ -83,7 +83,7 @@ public: * Given the src bounds of an image, this returns the bounds of the result * image after the filter has been applied. */ - bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst); + bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const; /** * Returns true if the filter can be processed on the GPU. This is most @@ -188,8 +188,14 @@ protected: */ virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, SkBitmap* result, SkIPoint* offset); - // Default impl copies src into dst and returns true - virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*); + // Given the bounds of the destination rect to be filled in device + // coordinates (first parameter), and the CTM, compute (conservatively) + // which rect of the source image would be required (third parameter). + // Used for clipping and temp-buffer allocations, so the result need not + // be exact, but should never be smaller than the real answer. The default + // implementation recursively unions all input bounds, or returns false if + // no inputs. + virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const; // Applies "matrix" to the crop rect, and sets "rect" to the intersection of // "rect" and the transformed crop rect. If there is no overlap, returns diff --git a/include/effects/SkBitmapSource.h b/include/effects/SkBitmapSource.h index e0e241b67f..fa6dafc3a5 100644 --- a/include/effects/SkBitmapSource.h +++ b/include/effects/SkBitmapSource.h @@ -24,6 +24,7 @@ protected: virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE; virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, SkBitmap* result, SkIPoint* offset) SK_OVERRIDE; + virtual bool onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const SK_OVERRIDE; private: SkBitmap fBitmap; diff --git a/include/effects/SkBlurImageFilter.h b/include/effects/SkBlurImageFilter.h index 1d51f9a5e9..f35269179b 100644 --- a/include/effects/SkBlurImageFilter.h +++ b/include/effects/SkBlurImageFilter.h @@ -27,6 +27,8 @@ protected: virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, SkBitmap* result, SkIPoint* offset) SK_OVERRIDE; + virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&, + SkIRect* dst) const SK_OVERRIDE; bool canFilterImageGPU() const SK_OVERRIDE { return true; } virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, diff --git a/include/effects/SkComposeImageFilter.h b/include/effects/SkComposeImageFilter.h index 744647d469..9cf7465a42 100644 --- a/include/effects/SkComposeImageFilter.h +++ b/include/effects/SkComposeImageFilter.h @@ -22,7 +22,7 @@ protected: virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, SkBitmap* result, SkIPoint* loc) SK_OVERRIDE; - virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE; + virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const SK_OVERRIDE; private: typedef SkImageFilter INHERITED; diff --git a/include/effects/SkDisplacementMapEffect.h b/include/effects/SkDisplacementMapEffect.h index ba7e25901e..de07fe4ca4 100644 --- a/include/effects/SkDisplacementMapEffect.h +++ b/include/effects/SkDisplacementMapEffect.h @@ -39,6 +39,9 @@ public: SkIPoint* offset) SK_OVERRIDE; virtual void computeFastBounds(const SkRect& src, SkRect* dst) const SK_OVERRIDE; + virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&, + SkIRect* dst) const SK_OVERRIDE; + #if SK_SUPPORT_GPU virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; } virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, diff --git a/include/effects/SkDropShadowImageFilter.h b/include/effects/SkDropShadowImageFilter.h index 14a78930db..cfcad8c33b 100644 --- a/include/effects/SkDropShadowImageFilter.h +++ b/include/effects/SkDropShadowImageFilter.h @@ -20,6 +20,10 @@ protected: explicit SkDropShadowImageFilter(SkReadBuffer&); virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE; virtual bool onFilterImage(Proxy*, const SkBitmap& source, const SkMatrix&, SkBitmap* result, SkIPoint* loc) SK_OVERRIDE; + virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&, + SkIRect* dst) const SK_OVERRIDE; + + private: SkScalar fDx, fDy, fSigmaX, fSigmaY; diff --git a/include/effects/SkMergeImageFilter.h b/include/effects/SkMergeImageFilter.h index bd9ab6c8e9..74cf561fd7 100755 --- a/include/effects/SkMergeImageFilter.h +++ b/include/effects/SkMergeImageFilter.h @@ -30,8 +30,6 @@ protected: virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, SkBitmap* result, SkIPoint* loc) SK_OVERRIDE; - virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE; - private: uint8_t* fModes; // SkXfermode::Mode diff --git a/include/effects/SkMorphologyImageFilter.h b/include/effects/SkMorphologyImageFilter.h index 03359088d4..c2b0d13eb3 100644 --- a/include/effects/SkMorphologyImageFilter.h +++ b/include/effects/SkMorphologyImageFilter.h @@ -17,6 +17,7 @@ class SK_API SkMorphologyImageFilter : public SkImageFilter { public: SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input, const CropRect* cropRect); virtual void computeFastBounds(const SkRect& src, SkRect* dst) const SK_OVERRIDE; + virtual bool onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const SK_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 9237111bc1..aef158c539 100644 --- a/include/effects/SkOffsetImageFilter.h +++ b/include/effects/SkOffsetImageFilter.h @@ -26,7 +26,7 @@ protected: virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, SkBitmap* result, SkIPoint* loc) SK_OVERRIDE; - virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE; + virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const SK_OVERRIDE; private: SkVector fOffset; diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 6f4e88da86..a14bb63e5d 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -791,11 +791,18 @@ static bool bounds_affects_clip(SkCanvas::SaveFlags flags) { } bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags, - SkIRect* intersection) { + SkIRect* intersection, const SkImageFilter* imageFilter) { SkIRect clipBounds; + SkRegion::Op op = SkRegion::kIntersect_Op; if (!this->getClipDeviceBounds(&clipBounds)) { return false; } + + if (imageFilter) { + imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds); + // Filters may grow the bounds beyond the device bounds. + op = SkRegion::kReplace_Op; + } SkIRect ir; if (NULL != bounds) { SkRect r; @@ -813,11 +820,11 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags, ir = clipBounds; } - fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op); + fClipStack.clipDevRect(ir, op); // early exit if the clip is now empty if (bounds_affects_clip(flags) && - !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) { + !fMCRec->fRasterClip->op(ir, op)) { return false; } @@ -861,7 +868,7 @@ int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, fDeviceCMDirty = true; SkIRect ir; - if (!this->clipRectBounds(bounds, flags, &ir)) { + if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) { return count; } diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp index 9b7e5e488f..384f2dce6b 100644 --- a/src/core/SkImageFilter.cpp +++ b/src/core/SkImageFilter.cpp @@ -106,7 +106,7 @@ bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src, } bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) { + SkIRect* dst) const { SkASSERT(&src); SkASSERT(dst); return this->onFilterBounds(src, ctm, dst); @@ -210,8 +210,28 @@ bool SkImageFilter::applyCropRect(SkIRect* rect, const SkMatrix& matrix) const { } bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) { - *dst = src; + SkIRect* dst) const { + if (fInputCount < 1) { + return false; + } + + SkIRect bounds; + for (int i = 0; i < fInputCount; ++i) { + SkImageFilter* filter = this->getInput(i); + SkIRect rect = src; + if (filter && !filter->filterBounds(src, ctm, &rect)) { + return false; + } + if (0 == i) { + bounds = rect; + } else { + bounds.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; return true; } diff --git a/src/effects/SkBitmapSource.cpp b/src/effects/SkBitmapSource.cpp index 67567c5039..f318c91a73 100644 --- a/src/effects/SkBitmapSource.cpp +++ b/src/effects/SkBitmapSource.cpp @@ -83,3 +83,9 @@ bool SkBitmapSource::onFilterImage(Proxy* proxy, const SkBitmap&, const SkMatrix void SkBitmapSource::computeFastBounds(const SkRect&, SkRect* dst) const { *dst = fDstRect; } + +bool SkBitmapSource::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst) const { + *dst = src; + return true; +} diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp index ccc7ec3866..67b0511480 100644 --- a/src/effects/SkBlurImageFilter.cpp +++ b/src/effects/SkBlurImageFilter.cpp @@ -236,6 +236,21 @@ void SkBlurImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const dst->outset(SkScalarMul(fSigma.width(), SkIntToScalar(3)), SkScalarMul(fSigma.height(), SkIntToScalar(3))); } + +bool SkBlurImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst) const { + SkIRect bounds = src; + if (getInput(0) && !getInput(0)->filterBounds(src, ctm, &bounds)) { + return false; + } + SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height()); + ctm.mapVectors(&sigma, &localSigma, 1); + bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), + SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); + *dst = bounds; + return true; +} + bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* result, SkIPoint* offset) { #if SK_SUPPORT_GPU diff --git a/src/effects/SkComposeImageFilter.cpp b/src/effects/SkComposeImageFilter.cpp index c794b0daf0..cb513766cc 100644 --- a/src/effects/SkComposeImageFilter.cpp +++ b/src/effects/SkComposeImageFilter.cpp @@ -36,7 +36,7 @@ bool SkComposeImageFilter::onFilterImage(Proxy* proxy, bool SkComposeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) { + SkIRect* dst) const { SkImageFilter* outer = getInput(0); SkImageFilter* inner = getInput(1); diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp index e56cba1b4b..555f795a51 100644 --- a/src/effects/SkDisplacementMapEffect.cpp +++ b/src/effects/SkDisplacementMapEffect.cpp @@ -255,6 +255,15 @@ void SkDisplacementMapEffect::computeFastBounds(const SkRect& src, SkRect* dst) } } +bool SkDisplacementMapEffect::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst) const { + if (getColorInput()) { + return getColorInput()->filterBounds(src, ctm, dst); + } + *dst = src; + return true; +} + /////////////////////////////////////////////////////////////////////////////// #if SK_SUPPORT_GPU diff --git a/src/effects/SkDropShadowImageFilter.cpp b/src/effects/SkDropShadowImageFilter.cpp index 0bc7e1211f..7cb515265c 100644 --- a/src/effects/SkDropShadowImageFilter.cpp +++ b/src/effects/SkDropShadowImageFilter.cpp @@ -112,3 +112,22 @@ void SkDropShadowImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) SkScalarMul(fSigmaY, SkIntToScalar(3))); dst->join(shadowBounds); } + +bool SkDropShadowImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, + SkIRect* dst) const { + SkIRect bounds = src; + if (getInput(0) && !getInput(0)->filterBounds(src, ctm, &bounds)) { + return false; + } + SkVector offsetVec, localOffsetVec = SkVector::Make(fDx, fDy); + ctm.mapVectors(&offsetVec, &localOffsetVec, 1); + bounds.offset(-SkScalarCeilToInt(offsetVec.x()), + -SkScalarCeilToInt(offsetVec.y())); + SkVector sigma, localSigma = SkVector::Make(fSigmaX, fSigmaY); + ctm.mapVectors(&sigma, &localSigma, 1); + bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), + SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); + bounds.join(src); + *dst = bounds; + return true; +} diff --git a/src/effects/SkMergeImageFilter.cpp b/src/effects/SkMergeImageFilter.cpp index 086d8e68a1..d0a153fa50 100755 --- a/src/effects/SkMergeImageFilter.cpp +++ b/src/effects/SkMergeImageFilter.cpp @@ -65,38 +65,6 @@ SkMergeImageFilter::~SkMergeImageFilter() { } } -bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) { - if (countInputs() < 1) { - return false; - } - - SkIRect totalBounds; - - int inputCount = countInputs(); - for (int i = 0; i < inputCount; ++i) { - SkImageFilter* filter = getInput(i); - SkIRect r; - if (filter) { - if (!filter->filterBounds(src, ctm, &r)) { - return false; - } - } else { - r = src; - } - if (0 == i) { - totalBounds = r; - } else { - totalBounds.join(r); - } - } - - // 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 = totalBounds; - return true; -} - bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* result, SkIPoint* offset) { diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp index 7321eb25e2..46302ad520 100644 --- a/src/effects/SkMorphologyImageFilter.cpp +++ b/src/effects/SkMorphologyImageFilter.cpp @@ -249,6 +249,20 @@ 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; + if (getInput(0) && !getInput(0)->filterBounds(src, ctm, &bounds)) { + return false; + } + SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()), + SkIntToScalar(this->radius().height())); + ctm.mapVectors(&radius, 1); + bounds.outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y())); + *dst = bounds; + return true; +} + #if SK_SUPPORT_GPU /////////////////////////////////////////////////////////////////////////////// diff --git a/src/effects/SkOffsetImageFilter.cpp b/src/effects/SkOffsetImageFilter.cpp index 16ce36f13c..e19a3275d1 100644 --- a/src/effects/SkOffsetImageFilter.cpp +++ b/src/effects/SkOffsetImageFilter.cpp @@ -72,16 +72,19 @@ void SkOffsetImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) cons } else { *dst = src; } + SkRect copy = *dst; dst->offset(fOffset.fX, fOffset.fY); + dst->join(copy); } bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) { + SkIRect* dst) const { SkVector vec; ctm.mapVectors(&vec, &fOffset, 1); *dst = src; - dst->offset(SkScalarRoundToInt(vec.fX), SkScalarRoundToInt(vec.fY)); + dst->offset(-SkScalarCeilToInt(vec.fX), -SkScalarCeilToInt(vec.fY)); + dst->join(src); return true; }