Moving 4 SkImageFilter derived classes from blink to skia

There were 4 classes in blink that derived from SkImageFilter :
- TileImageFilter -> SkTileImageFilter
- OffsetImageFilter -> SkOffsetImageFilter (already existed)
- FloodImageFilter -> SkFloodImageFilter
- CompositeImageFilter -> SkCompositeImageFilter

All functions were copied as is, without modification (except for warnings fixes), except for the offset filter, which was merged into the existing SkOffsetImageFilter class, as a special case when a crop rect is provided. Since the names won't clash with the names in blink, it should be easy to integrate them in blink later and fix issues, if needed.

BUG=
R=senorblanco@google.com, senorblanco@chromium.org, bsalomon@google.com, reed@google.com, mtklein@google.com

Author: sugoi@chromium.org

Review URL: https://chromiumcodereview.appspot.com/24157005

git-svn-id: http://skia.googlecode.com/svn/trunk@11475 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/offsetimagefilter.cpp b/gm/offsetimagefilter.cpp
new file mode 100644
index 0000000..ee4ae35
--- /dev/null
+++ b/gm/offsetimagefilter.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2013 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 "SkOffsetImageFilter.h"
+#include "SkBitmapSource.h"
+
+#define WIDTH 400
+#define HEIGHT 100
+#define MARGIN 12
+
+namespace skiagm {
+
+class OffsetImageFilterGM : public GM {
+public:
+    OffsetImageFilterGM() : fInitialized(false) {
+        this->setBGColor(0xFF000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("offsetimagefilter");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fBitmap.allocPixels();
+        SkBitmapDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xD000D000);
+        paint.setTextSize(SkIntToScalar(96));
+        const char* str = "e";
+        canvas.drawText(str, strlen(str), SkIntToScalar(15), SkIntToScalar(65), paint);
+    }
+
+    void make_checkerboard() {
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fCheckerboard.allocPixels();
+        SkBitmapDevice device(fCheckerboard);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint darkPaint;
+        darkPaint.setColor(0xFF404040);
+        SkPaint lightPaint;
+        lightPaint.setColor(0xFFA0A0A0);
+        for (int y = 0; y < 80; y += 16) {
+          for (int x = 0; x < 80; 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();
+          }
+        }
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(WIDTH, HEIGHT);
+    }
+
+    void drawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint,
+                           SkScalar x, SkScalar y) {
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(x, y,
+            SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())));
+        canvas->drawBitmap(bitmap, x, y, &paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_bitmap();
+            make_checkerboard();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        SkPaint paint;
+
+        int x = 0, y = 0;
+        for (size_t i = 0; i < 4; i++) {
+            SkBitmap* bitmap = (i & 0x01) ? &fCheckerboard : &fBitmap;
+            SkIRect cropRect = SkIRect::MakeXYWH(x + i * 12,
+                                                 y + i * 8,
+                                                 bitmap->width() - i * 8,
+                                                 bitmap->height() - i * 12);
+            SkAutoTUnref<SkImageFilter> tileInput(SkNEW_ARGS(SkBitmapSource, (*bitmap)));
+            SkScalar dx = SkIntToScalar(i*5);
+            SkScalar dy = SkIntToScalar(i*10);
+            SkAutoTUnref<SkImageFilter> filter(SkNEW_ARGS(
+                SkOffsetImageFilter, (dx, dy, tileInput, &cropRect)));
+            paint.setImageFilter(filter);
+            drawClippedBitmap(canvas, *bitmap, paint, SkIntToScalar(x), SkIntToScalar(y));
+            x += bitmap->width() + MARGIN;
+            if (x + bitmap->width() > WIDTH) {
+                x = 0;
+                y += bitmap->height() + MARGIN;
+            }
+        }
+    }
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap, fCheckerboard;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new OffsetImageFilterGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/tileimagefilter.cpp b/gm/tileimagefilter.cpp
new file mode 100644
index 0000000..9a14344
--- /dev/null
+++ b/gm/tileimagefilter.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2013 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 "SkTileImageFilter.h"
+#include "SkBitmapSource.h"
+
+#define WIDTH 400
+#define HEIGHT 100
+#define MARGIN 12
+
+namespace skiagm {
+
+class TileImageFilterGM : public GM {
+public:
+    TileImageFilterGM() : fInitialized(false) {
+        this->setBGColor(0xFF000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("tileimagefilter");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fBitmap.allocPixels();
+        SkBitmapDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xD000D000);
+        paint.setTextSize(SkIntToScalar(96));
+        const char* str = "e";
+        canvas.drawText(str, strlen(str), SkIntToScalar(15), SkIntToScalar(65), paint);
+    }
+
+    void make_checkerboard() {
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fCheckerboard.allocPixels();
+        SkBitmapDevice device(fCheckerboard);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint darkPaint;
+        darkPaint.setColor(0xFF404040);
+        SkPaint lightPaint;
+        lightPaint.setColor(0xFFA0A0A0);
+        for (int y = 0; y < 80; y += 16) {
+          for (int x = 0; x < 80; 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();
+          }
+        }
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(WIDTH, HEIGHT);
+    }
+
+    void drawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint,
+                           SkScalar x, SkScalar y) {
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(x, y,
+            SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())));
+        canvas->drawBitmap(bitmap, x, y, &paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_bitmap();
+            make_checkerboard();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        SkPaint paint;
+
+        int x = 0, y = 0;
+        for (size_t i = 0; i < 4; i++) {
+            SkBitmap* bitmap = (i & 0x01) ? &fCheckerboard : &fBitmap;
+            SkRect srcRect = SkRect::MakeXYWH(SkIntToScalar(bitmap->width()/4),
+                                              SkIntToScalar(bitmap->height()/4),
+                                              SkIntToScalar(bitmap->width()/(i+1)),
+                                              SkIntToScalar(bitmap->height()/(i+1)));
+            SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(i * 8),
+                                              SkIntToScalar(i * 4),
+                                              SkIntToScalar(bitmap->width() - i * 4),
+                                              SkIntToScalar(bitmap->height()) - i * 8);
+            SkAutoTUnref<SkImageFilter> tileInput(SkNEW_ARGS(SkBitmapSource, (*bitmap)));
+            SkAutoTUnref<SkImageFilter> filter(SkNEW_ARGS(
+                SkTileImageFilter, (srcRect, dstRect, tileInput)));
+            paint.setImageFilter(filter);
+            drawClippedBitmap(canvas, *bitmap, paint, SkIntToScalar(x), SkIntToScalar(y));
+            x += bitmap->width() + MARGIN;
+            if (x + bitmap->width() > WIDTH) {
+                x = 0;
+                y += bitmap->height() + MARGIN;
+            }
+        }
+    }
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap, fCheckerboard;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new TileImageFilterGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/xfermodeimagefilter.cpp b/gm/xfermodeimagefilter.cpp
index 94d60e4..ef2958a 100644
--- a/gm/xfermodeimagefilter.cpp
+++ b/gm/xfermodeimagefilter.cpp
@@ -189,6 +189,41 @@
                                           SkIntToScalar(fBitmap.height() + 4)));
         canvas->drawPaint(paint);
         canvas->restore();
+        x += fBitmap.width() + MARGIN;
+        if (x + fBitmap.width() > WIDTH) {
+            x = 0;
+            y += fBitmap.height() + MARGIN;
+        }
+        // Test cropping
+        static const size_t nbSamples = 3;
+        SkXfermode::Mode sampledModes[nbSamples] = {SkXfermode::kOverlay_Mode,
+                                                    SkXfermode::kSrcOver_Mode,
+                                                    SkXfermode::kPlus_Mode};
+        int offsets[nbSamples][4] = {{ 10,  10, -16, -16},
+                                     { 10,  10,  10,  10},
+                                     {-10, -10,  -6,  -6}};
+        for (size_t i = 0; i < nbSamples; ++i) {
+            SkIRect cropRect = SkIRect::MakeXYWH(x + offsets[i][0],
+                                                 y + offsets[i][1],
+                                                 fBitmap.width()  + offsets[i][2],
+                                                 fBitmap.height() + offsets[i][3]);
+            mode.reset(SkXfermode::Create(sampledModes[i]));
+            filter.reset(SkNEW_ARGS(SkXfermodeImageFilter,
+                                    (mode, offsetBackground, offsetForeground, &cropRect)));
+            paint.setImageFilter(filter);
+            canvas->save();
+            canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x),
+                                              SkIntToScalar(y),
+                                              SkIntToScalar(fBitmap.width() + 4),
+                                              SkIntToScalar(fBitmap.height() + 4)));
+            canvas->drawPaint(paint);
+            canvas->restore();
+            x += fBitmap.width() + MARGIN;
+            if (x + fBitmap.width() > WIDTH) {
+                x = 0;
+                y += fBitmap.height() + MARGIN;
+            }
+        }
     }
 private:
     typedef GM INHERITED;
diff --git a/gyp/effects.gypi b/gyp/effects.gypi
index 9105ddd..19e55e8 100644
--- a/gyp/effects.gypi
+++ b/gyp/effects.gypi
@@ -55,6 +55,7 @@
     '<(skia_src_path)/effects/SkTableColorFilter.cpp',
     '<(skia_src_path)/effects/SkTableMaskFilter.cpp',
     '<(skia_src_path)/effects/SkTestImageFilters.cpp',
+    '<(skia_src_path)/effects/SkTileImageFilter.cpp',
     '<(skia_src_path)/effects/SkTransparentShader.cpp',
     '<(skia_src_path)/effects/SkXfermodeImageFilter.cpp',
 
@@ -113,6 +114,7 @@
     '<(skia_include_path)/effects/SkStippleMaskFilter.h',
     '<(skia_include_path)/effects/SkTableColorFilter.h',
     '<(skia_include_path)/effects/SkTableMaskFilter.h',
+    '<(skia_include_path)/effects/SkTileImageFilter.h',
     '<(skia_include_path)/effects/SkTransparentShader.h',
     '<(skia_include_path)/effects/SkMagnifierImageFilter.h',
   ],
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 7eb97fa..bace62f 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -86,6 +86,7 @@
     '../gm/morphology.cpp',
     '../gm/nested.cpp',
     '../gm/ninepatchstretch.cpp',
+    '../gm/offsetimagefilter.cpp',
     '../gm/optimizations.cpp',
     '../gm/ovals.cpp',
     '../gm/patheffects.cpp',
@@ -125,6 +126,7 @@
     '../gm/texdata.cpp',
     '../gm/thinrects.cpp',
     '../gm/thinstrokedrects.cpp',
+    '../gm/tileimagefilter.cpp',
     '../gm/tilemodes.cpp',
     '../gm/tilemodes_scaled.cpp',
     '../gm/tinybitmap.cpp',
diff --git a/include/effects/SkOffsetImageFilter.h b/include/effects/SkOffsetImageFilter.h
index 62b5fa6..0b8c900 100644
--- a/include/effects/SkOffsetImageFilter.h
+++ b/include/effects/SkOffsetImageFilter.h
@@ -12,8 +12,11 @@
 #include "SkPoint.h"
 
 class SK_API SkOffsetImageFilter : public SkImageFilter {
+    typedef SkImageFilter INHERITED;
+
 public:
-    SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input = NULL);
+    SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input = NULL,
+                        const SkIRect* cropRect = NULL);
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkOffsetImageFilter)
 
 protected:
@@ -26,8 +29,6 @@
 
 private:
     SkVector fOffset;
-
-    typedef SkImageFilter INHERITED;
 };
 
 #endif
diff --git a/include/effects/SkTileImageFilter.h b/include/effects/SkTileImageFilter.h
new file mode 100644
index 0000000..f11a75a
--- /dev/null
+++ b/include/effects/SkTileImageFilter.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkTileImageFilter_DEFINED
+#define SkTileImageFilter_DEFINED
+
+#include "SkImageFilter.h"
+
+class SkTileImageFilter : public SkImageFilter {
+    typedef SkImageFilter INHERITED;
+
+public:
+    /** Tile image filter constructor
+        @param srcRect  Defines the pixels to tile
+        @param dstRect  Defines the pixels where tiles are drawn
+        @param input    Input from which the subregion defined by srcRect will be tiled
+    */
+    SkTileImageFilter(const SkRect& srcRect, const SkRect& dstRect, SkImageFilter* input)
+        : INHERITED(input), fSrcRect(srcRect), fDstRect(dstRect) {}
+
+    virtual bool onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+                               SkBitmap* dst, SkIPoint* offset) SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTileImageFilter)
+
+protected:
+    explicit SkTileImageFilter(SkFlattenableReadBuffer& buffer);
+
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE;
+
+private:
+    SkRect fSrcRect;
+    SkRect fDstRect;
+};
+
+#endif
diff --git a/include/effects/SkXfermodeImageFilter.h b/include/effects/SkXfermodeImageFilter.h
index 89a2773..4a5d7fa 100644
--- a/include/effects/SkXfermodeImageFilter.h
+++ b/include/effects/SkXfermodeImageFilter.h
@@ -22,7 +22,7 @@
 
 public:
     SkXfermodeImageFilter(SkXfermode* mode, SkImageFilter* background,
-                          SkImageFilter* foreground = NULL);
+                          SkImageFilter* foreground = NULL, const SkIRect* cropRect = NULL);
 
     virtual ~SkXfermodeImageFilter();
 
@@ -34,7 +34,7 @@
                                SkBitmap* dst,
                                SkIPoint* offset) SK_OVERRIDE;
 #if SK_SUPPORT_GPU
-    virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    virtual bool canFilterImageGPU() const SK_OVERRIDE { return cropRect().isLargest(); }
     virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
                                 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
 #endif
diff --git a/src/effects/SkOffsetImageFilter.cpp b/src/effects/SkOffsetImageFilter.cpp
index ad5e49d..3b98160 100644
--- a/src/effects/SkOffsetImageFilter.cpp
+++ b/src/effects/SkOffsetImageFilter.cpp
@@ -7,24 +7,51 @@
 
 #include "SkOffsetImageFilter.h"
 #include "SkBitmap.h"
-#include "SkMatrix.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
 #include "SkFlattenableBuffers.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
 
 bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
                                         const SkMatrix& matrix,
                                         SkBitmap* result,
                                         SkIPoint* loc) {
+    SkImageFilter* input = getInput(0);
     SkBitmap src = source;
-    if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, loc)) {
-        return false;
+    if (cropRect().isLargest()) {
+        if (input && !input->filterImage(proxy, source, matrix, &src, loc)) {
+            return false;
+        }
+
+        SkVector vec;
+        matrix.mapVectors(&vec, &fOffset, 1);
+
+        loc->fX += SkScalarRoundToInt(vec.fX);
+        loc->fY += SkScalarRoundToInt(vec.fY);
+        *result = src;
+    } else {
+        SkIPoint srcOffset = SkIPoint::Make(0, 0);
+        if (input && !input->filterImage(proxy, source, matrix, &src, &srcOffset)) {
+            return false;
+        }
+
+        SkIRect bounds;
+        src.getBounds(&bounds);
+
+        if (!applyCropRect(&bounds, matrix)) {
+            return false;
+        }
+
+        SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
+        SkCanvas canvas(device);
+        SkPaint paint;
+        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+        canvas.drawBitmap(src, fOffset.fX - bounds.left(), fOffset.fY - bounds.top(), &paint);
+        *result = device->accessBitmap(false);
+        loc->fX += bounds.left();
+        loc->fY += bounds.top();
     }
-
-    SkVector vec;
-    matrix.mapVectors(&vec, &fOffset, 1);
-
-    loc->fX += SkScalarRoundToInt(vec.fX);
-    loc->fY += SkScalarRoundToInt(vec.fY);
-    *result = src;
     return true;
 }
 
@@ -43,8 +70,8 @@
     buffer.writePoint(fOffset);
 }
 
-SkOffsetImageFilter::SkOffsetImageFilter(SkScalar dx, SkScalar dy,
-                                         SkImageFilter* input) : INHERITED(input) {
+SkOffsetImageFilter::SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input,
+                                         const SkIRect* cropRect) : INHERITED(input, cropRect) {
     fOffset.set(dx, dy);
 }
 
diff --git a/src/effects/SkTileImageFilter.cpp b/src/effects/SkTileImageFilter.cpp
new file mode 100644
index 0000000..f2c586d
--- /dev/null
+++ b/src/effects/SkTileImageFilter.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTileImageFilter.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkFlattenableBuffers.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+                                      SkBitmap* dst, SkIPoint* offset) {
+    SkBitmap source = src;
+    SkImageFilter* input = getInput(0);
+    SkIPoint localOffset = SkIPoint::Make(0, 0);
+    if (input && !input->filterImage(proxy, src, ctm, &source, &localOffset)) {
+        return false;
+    }
+
+    int w = SkScalarTruncToInt(fDstRect.width());
+    int h = SkScalarTruncToInt(fDstRect.height());
+    if (!fSrcRect.width() || !fSrcRect.height() || !w || !h) {
+        return false;
+    }
+
+    SkIRect srcRect;
+    fSrcRect.roundOut(&srcRect);
+    SkBitmap subset;
+    if (!source.extractSubset(&subset, srcRect)) {
+        return false;
+    }
+
+    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(w, h));
+    SkIRect bounds;
+    source.getBounds(&bounds);
+    SkCanvas canvas(device);
+    SkPaint paint;
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+
+    SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(subset, 
+                                  SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
+    paint.setShader(shader);
+    SkRect dstRect = fDstRect;
+    dstRect.offset(SkIntToScalar(localOffset.fX), SkIntToScalar(localOffset.fY));
+    canvas.drawRect(dstRect, paint);
+    *dst = device->accessBitmap(false);
+    return true;
+}
+
+SkTileImageFilter::SkTileImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    buffer.readRect(&fSrcRect);
+    buffer.readRect(&fDstRect);
+}
+
+void SkTileImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeRect(fSrcRect);
+    buffer.writeRect(fDstRect);
+}
+
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
index 898bad1..8491292 100644
--- a/src/effects/SkXfermodeImageFilter.cpp
+++ b/src/effects/SkXfermodeImageFilter.cpp
@@ -7,6 +7,7 @@
 
 #include "SkXfermodeImageFilter.h"
 #include "SkCanvas.h"
+#include "SkDevice.h"
 #include "SkColorPriv.h"
 #include "SkFlattenableBuffers.h"
 #include "SkXfermode.h"
@@ -21,8 +22,9 @@
 
 SkXfermodeImageFilter::SkXfermodeImageFilter(SkXfermode* mode,
                                              SkImageFilter* background,
-                                             SkImageFilter* foreground)
-  : INHERITED(background, foreground), fMode(mode) {
+                                             SkImageFilter* foreground,
+                                             const SkIRect* cropRect)
+  : INHERITED(background, foreground, cropRect), fMode(mode) {
     SkSafeRef(fMode);
 }
 
@@ -58,19 +60,29 @@
         !foregroundInput->filterImage(proxy, src, ctm, &foreground, &foregroundOffset)) {
         return false;
     }
-    dst->setConfig(background.config(), background.width(), background.height());
-    dst->allocPixels();
-    SkCanvas canvas(*dst);
+
+    SkIRect bounds;
+    background.getBounds(&bounds);
+    if (!applyCropRect(&bounds, ctm)) {
+        return false;
+    }
+    backgroundOffset.fX -= bounds.left();
+    backgroundOffset.fY -= bounds.top();
+    foregroundOffset.fX -= bounds.left();
+    foregroundOffset.fY -= bounds.top();
+
+    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
+    SkCanvas canvas(device);
     SkPaint paint;
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-    canvas.drawBitmap(background, 0, 0, &paint);
+    canvas.drawBitmap(background, SkIntToScalar(backgroundOffset.fX),
+                      SkIntToScalar(backgroundOffset.fY), &paint);
     paint.setXfermode(fMode);
-    canvas.drawBitmap(foreground,
-                      SkIntToScalar(foregroundOffset.fX - backgroundOffset.fX),
-                      SkIntToScalar(foregroundOffset.fY - backgroundOffset.fY),
-                      &paint);
-    offset->fX += backgroundOffset.fX;
-    offset->fY += backgroundOffset.fY;
+    canvas.drawBitmap(foreground, SkIntToScalar(foregroundOffset.fX),
+                      SkIntToScalar(foregroundOffset.fY), &paint);
+    *dst = device->accessBitmap(false);
+    offset->fX += bounds.left();
+    offset->fY += bounds.top();
     return true;
 }