Fix SkComposeImageFilter's bounds computation and offset handling
authorajuma <ajuma@chromium.org>
Fri, 13 Feb 2015 17:05:47 +0000 (09:05 -0800)
committerCommit bot <commit-bot@chromium.org>
Fri, 13 Feb 2015 17:05:47 +0000 (09:05 -0800)
This makes SkComposeImageFilter::computeFastBounds compose its
filters' bounds (rather than falling back to
SkImageFilter::computeFastBounds, which takes the union of the bounds).

This also makes SkComposeImageFilter::onFilterImage correctly handle
an offset produced when applying the inner filter; such offsets were
previously ignored.

BUG=chromium:453924

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

include/effects/SkComposeImageFilter.h
src/effects/SkComposeImageFilter.cpp
tests/ImageFilterTest.cpp

index dccbd6e..361ca2b 100644 (file)
@@ -24,6 +24,7 @@ public:
         SkImageFilter* inputs[2] = { outer, inner };
         return SkNEW_ARGS(SkComposeImageFilter, (inputs));
     }
+    void computeFastBounds(const SkRect& src, SkRect* dst) const SK_OVERRIDE;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeImageFilter)
index 4a17e15..2b1606a 100644 (file)
 SkComposeImageFilter::~SkComposeImageFilter() {
 }
 
+void SkComposeImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
+    SkImageFilter* outer = getInput(0);
+    SkImageFilter* inner = getInput(1);
+
+    SkRect tmp;
+    inner->computeFastBounds(src, &tmp);
+    outer->computeFastBounds(tmp, dst);
+}
+
 bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
                                          const SkBitmap& src,
                                          const Context& ctx,
@@ -22,8 +31,20 @@ bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
     SkImageFilter* inner = getInput(1);
 
     SkBitmap tmp;
-    return inner->filterImage(proxy, src, ctx, &tmp, offset) &&
-           outer->filterImage(proxy, tmp, ctx, result, offset);
+    SkIPoint innerOffset = SkIPoint::Make(0, 0);
+    SkIPoint outerOffset = SkIPoint::Make(0, 0);
+    if (!inner->filterImage(proxy, src, ctx, &tmp, &innerOffset))
+        return false;
+
+    SkMatrix outerMatrix(ctx.ctm());
+    outerMatrix.postTranslate(SkIntToScalar(-innerOffset.x()), SkIntToScalar(-innerOffset.y()));
+    Context outerContext(outerMatrix, ctx.clipBounds(), ctx.cache());
+    if (!outer->filterImage(proxy, tmp, outerContext, result, &outerOffset)) {
+        return false;
+    }
+
+    *offset = innerOffset + outerOffset;
+    return true;
 }
 
 bool SkComposeImageFilter::onFilterBounds(const SkIRect& src,
index 55499d7..fd7cfed 100644 (file)
@@ -12,6 +12,7 @@
 #include "SkCanvas.h"
 #include "SkColorFilterImageFilter.h"
 #include "SkColorMatrixFilter.h"
+#include "SkComposeImageFilter.h"
 #include "SkDeviceImageFilterProxy.h"
 #include "SkDisplacementMapEffect.h"
 #include "SkDropShadowImageFilter.h"
@@ -584,6 +585,20 @@ DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
     REPORTER_ASSERT(reporter, bounds == expectedBounds);
 }
 
+DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) {
+    SkAutoTUnref<SkImageFilter> filter1(makeBlur());
+    SkAutoTUnref<SkImageFilter> filter2(makeBlur());
+    SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(filter1.get(), filter2.get()));
+
+    SkRect boundsSrc = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
+    SkRect expectedBounds = SkRect::MakeXYWH(
+        SkIntToScalar(-6), SkIntToScalar(-6), SkIntToScalar(112), SkIntToScalar(112));
+    SkRect boundsDst = SkRect::MakeEmpty();
+    composedFilter->computeFastBounds(boundsSrc, &boundsDst);
+
+    REPORTER_ASSERT(reporter, boundsDst == expectedBounds);
+}
+
 static void draw_blurred_rect(SkCanvas* canvas) {
     SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(SkIntToScalar(8), 0));
     SkPaint filterPaint;
@@ -1068,6 +1083,23 @@ DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
     test_xfermode_cropped_input(&device, reporter);
 }
 
+DEF_TEST(ComposedImageFilterOffset, reporter) {
+    SkBitmap bitmap;
+    bitmap.allocN32Pixels(100, 100);
+    bitmap.eraseARGB(0, 0, 0, 0);
+    SkBitmapDevice device(bitmap);
+    SkDeviceImageFilterProxy proxy(&device, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
+
+    SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20));
+    SkAutoTUnref<SkImageFilter> offsetFilter(SkOffsetImageFilter::Create(0, 0, NULL, &cropRect));
+    SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(makeBlur(), offsetFilter.get()));
+    SkBitmap result;
+    SkIPoint offset;
+    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), NULL);
+    REPORTER_ASSERT(reporter, composedFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
+    REPORTER_ASSERT(reporter, offset.fX == 1 && offset.fY == 0);
+}
+
 #if SK_SUPPORT_GPU
 const SkSurfaceProps gProps = SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);