Fix matrix adjustment passed to filter processing.
authorsenorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 2 Apr 2014 19:20:05 +0000 (19:20 +0000)
committersenorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 2 Apr 2014 19:20:05 +0000 (19:20 +0000)
When adjusting the CTM for filter use, we were subtracting off the
destination coordinates of the drawDevice() or drawSprite(). This is
not quite correct: we should subtract off the coordinates relative to
the device origin instead. This occurs when one filtered saveLayer() is
drawn inside another saveLayer(), both with non-zero origin.

This fixes layout test svg/batik/text/smallFonts.svg in Blink, and is
exercised by the provided unit test.

BUG=skia:
R=bsalomon@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@14029 2bbb7eff-a529-9590-31e7-b0007b416f81

src/core/SkCanvas.cpp
tests/ImageFilterTest.cpp

index 1ca9312..c16ac9a 100644 (file)
@@ -1256,7 +1256,7 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
             SkIPoint offset = SkIPoint::Make(0, 0);
             const SkBitmap& src = srcDev->accessBitmap(false);
             SkMatrix matrix = *iter.fMatrix;
-            matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+            matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
             SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
             SkImageFilter::Context ctx(matrix, clipBounds);
             if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
@@ -1296,7 +1296,7 @@ void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
             SkBitmap dst;
             SkIPoint offset = SkIPoint::Make(0, 0);
             SkMatrix matrix = *iter.fMatrix;
-            matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+            matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
             SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
             SkImageFilter::Context ctx(matrix, clipBounds);
             if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
index cfba1fa..4c8ddc6 100644 (file)
@@ -19,6 +19,7 @@
 #include "SkFlattenableBuffers.h"
 #include "SkLightingImageFilter.h"
 #include "SkMatrixConvolutionImageFilter.h"
+#include "SkMatrixImageFilter.h"
 #include "SkMergeImageFilter.h"
 #include "SkMorphologyImageFilter.h"
 #include "SkOffsetImageFilter.h"
@@ -363,6 +364,57 @@ static void test_xfermode_cropped_input(SkBaseDevice* device, skiatest::Reporter
     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
 }
 
+DEF_TEST(ImageFilterNestedSaveLayer, reporter) {
+    SkBitmap temp;
+    temp.allocN32Pixels(50, 50);
+    SkBitmapDevice device(temp);
+    SkCanvas canvas(&device);
+    canvas.clear(0x0);
+
+    SkBitmap bitmap;
+    bitmap.allocN32Pixels(10, 10);
+    bitmap.eraseColor(SK_ColorGREEN);
+
+    SkMatrix matrix;
+    matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
+    matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
+    SkAutoTUnref<SkImageFilter> matrixFilter(
+        SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel));
+
+    // Test that saveLayer() with a filter nested inside another saveLayer() applies the
+    // correct offset to the filter matrix.
+    SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30);
+    canvas.saveLayer(&bounds1, NULL);
+    SkPaint filterPaint;
+    filterPaint.setImageFilter(matrixFilter);
+    SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10);
+    canvas.saveLayer(&bounds2, &filterPaint);
+    SkPaint greenPaint;
+    greenPaint.setColor(SK_ColorGREEN);
+    canvas.drawRect(bounds2, greenPaint);
+    canvas.restore();
+    canvas.restore();
+    SkPaint strokePaint;
+    strokePaint.setStyle(SkPaint::kStroke_Style);
+    strokePaint.setColor(SK_ColorRED);
+
+    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
+    uint32_t pixel;
+    canvas.readPixels(info, &pixel, 4, 25, 25);
+    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
+
+    // Test that drawSprite() with a filter nested inside a saveLayer() applies the
+    // correct offset to the filter matrix.
+    canvas.clear(0x0);
+    canvas.readPixels(info, &pixel, 4, 25, 25);
+    canvas.saveLayer(&bounds1, NULL);
+    canvas.drawSprite(bitmap, 20, 20, &filterPaint);
+    canvas.restore();
+
+    canvas.readPixels(info, &pixel, 4, 25, 25);
+    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
+}
+
 DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
     SkBitmap temp;
     temp.allocN32Pixels(100, 100);