Implement support for a Context parameter in image filters

Some upcoming work (support for expanding crop rects) requires
the clip bounds to be available during filter traversal. This change
replaces the SkMatrix parameter in the onFilterImage() traversals
with a Context parameter. It contains the CTM, as well as the clip
bounds.

BUG=skia:
R=reed@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13803 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/imagefiltersbase.cpp b/gm/imagefiltersbase.cpp
index 86a7f81..b31fbeb 100644
--- a/gm/imagefiltersbase.cpp
+++ b/gm/imagefiltersbase.cpp
@@ -25,7 +25,7 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(FailImageFilter)
 protected:
     FailImageFilter() : INHERITED(0) {}
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const {
         return false;
     }
@@ -51,7 +51,7 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(IdentityImageFilter)
 protected:
     IdentityImageFilter() : INHERITED(0) {}
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const {
         *result = src;
         return true;
diff --git a/gm/imagefiltersgraph.cpp b/gm/imagefiltersgraph.cpp
index fb957e7..bc34e65 100644
--- a/gm/imagefiltersgraph.cpp
+++ b/gm/imagefiltersgraph.cpp
@@ -29,19 +29,19 @@
     SimpleOffsetFilter(SkScalar dx, SkScalar dy, SkImageFilter* input)
     : SkImageFilter(input), fDX(dx), fDY(dy) {}
 
-    virtual bool onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+    virtual bool onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                SkBitmap* dst, SkIPoint* offset) const SK_OVERRIDE {
         SkBitmap source = src;
         SkImageFilter* input = getInput(0);
         SkIPoint srcOffset = SkIPoint::Make(0, 0);
-        if (NULL != input && !input->filterImage(proxy, src, ctm, &source, &srcOffset)) {
+        if (NULL != input && !input->filterImage(proxy, src, ctx, &source, &srcOffset)) {
             return false;
         }
 
         SkIRect bounds;
         source.getBounds(&bounds);
 
-        if (!this->applyCropRect(&bounds, ctm)) {
+        if (!this->applyCropRect(&bounds, ctx.ctm())) {
             return false;
         }
 
diff --git a/include/core/SkBitmapDevice.h b/include/core/SkBitmapDevice.h
index 25c35ce..afbefc1 100644
--- a/include/core/SkBitmapDevice.h
+++ b/include/core/SkBitmapDevice.h
@@ -248,7 +248,7 @@
      *  If the device does not recognize or support this filter,
      *  it just returns false and leaves result and offset unchanged.
      */
-    virtual bool filterImage(const SkImageFilter*, const SkBitmap&, const SkMatrix&,
+    virtual bool filterImage(const SkImageFilter*, const SkBitmap&, const SkImageFilter::Context&,
                              SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
 
 private:
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 39304fa..2d9b0fc 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -15,6 +15,7 @@
 #include "SkCanvas.h"
 #include "SkColor.h"
 #include "SkDeviceProperties.h"
+#include "SkImageFilter.h"
 
 // getDeviceCapabilities() is not called by skia, but this flag keeps it around
 // for clients that have "override" annotations on their subclass. These overrides
@@ -375,7 +376,8 @@
      *  If the device does not recognize or support this filter,
      *  it just returns false and leaves result and offset unchanged.
      */
-    virtual bool filterImage(const SkImageFilter*, const SkBitmap&, const SkMatrix&,
+    virtual bool filterImage(const SkImageFilter*, const SkBitmap&,
+                             const SkImageFilter::Context& ctx,
                              SkBitmap* result, SkIPoint* offset) = 0;
 
     // This is equal kBGRA_Premul_Config8888 or kRGBA_Premul_Config8888 if
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 91b98f4..4f33f54 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -9,12 +9,12 @@
 #define SkImageFilter_DEFINED
 
 #include "SkFlattenable.h"
+#include "SkMatrix.h"
 #include "SkRect.h"
 
 class SkBitmap;
 class SkColorFilter;
 class SkBaseDevice;
-class SkMatrix;
 struct SkIPoint;
 class SkShader;
 class GrEffectRef;
@@ -49,6 +49,18 @@
         uint32_t fFlags;
     };
 
+    class Context {
+    public:
+        Context(const SkMatrix& ctm, const SkIRect& clipBounds) :
+            fCTM(ctm), fClipBounds(clipBounds) {
+        }
+        const SkMatrix& ctm() const { return fCTM; }
+        const SkIRect& clipBounds() const { return fClipBounds; }
+    private:
+        SkMatrix fCTM;
+        SkIRect  fClipBounds;
+    };
+
     class Proxy {
     public:
         virtual ~Proxy() {};
@@ -59,7 +71,7 @@
         // returns true if the proxy handled the filter itself. if this returns
         // false then the filter's code will be called.
         virtual bool filterImage(const SkImageFilter*, const SkBitmap& src,
-                                 const SkMatrix& ctm,
+                                 const Context&,
                                  SkBitmap* result, SkIPoint* offset) = 0;
     };
 
@@ -76,7 +88,7 @@
      *  If the result image cannot be created, return false, in which case both
      *  the result and offset parameters will be ignored by the caller.
      */
-    bool filterImage(Proxy*, const SkBitmap& src, const SkMatrix& ctm,
+    bool filterImage(Proxy*, const SkBitmap& src, const Context&,
                      SkBitmap* result, SkIPoint* offset) const;
 
     /**
@@ -104,7 +116,7 @@
      *  relative to the src when it is drawn. The default implementation does
      *  single-pass processing using asNewEffect().
      */
-    virtual bool filterImageGPU(Proxy*, const SkBitmap& src, const SkMatrix& ctm,
+    virtual bool filterImageGPU(Proxy*, const SkBitmap& src, const Context&,
                                 SkBitmap* result, SkIPoint* offset) const;
 
     /**
@@ -156,7 +168,7 @@
      * Recursively evaluate this filter on the GPU. If the filter has no GPU
      * implementation, it will be processed in software and uploaded to the GPU.
      */
-    bool getInputResultGPU(SkImageFilter::Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+    bool getInputResultGPU(SkImageFilter::Proxy* proxy, const SkBitmap& src, const Context&,
                            SkBitmap* result, SkIPoint* offset) const;
 #endif
 
@@ -200,7 +212,7 @@
      *  case both the result and offset parameters will be ignored by the
      *  caller.
      */
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const;
     // Given the bounds of the destination rect to be filled in device
     // coordinates (first parameter), and the CTM, compute (conservatively)
diff --git a/include/effects/SkBicubicImageFilter.h b/include/effects/SkBicubicImageFilter.h
index 9b65975..7c1e923 100644
--- a/include/effects/SkBicubicImageFilter.h
+++ b/include/effects/SkBicubicImageFilter.h
@@ -38,12 +38,12 @@
     SkBicubicImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* loc) 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,
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                 SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #endif
 
diff --git a/include/effects/SkBitmapSource.h b/include/effects/SkBitmapSource.h
index 9740e0e..76f51f5 100644
--- a/include/effects/SkBitmapSource.h
+++ b/include/effects/SkBitmapSource.h
@@ -27,7 +27,7 @@
 protected:
     explicit SkBitmapSource(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
     virtual bool onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const SK_OVERRIDE;
 
diff --git a/include/effects/SkBlurImageFilter.h b/include/effects/SkBlurImageFilter.h
index 44bf393..e7d0db3 100644
--- a/include/effects/SkBlurImageFilter.h
+++ b/include/effects/SkBlurImageFilter.h
@@ -28,13 +28,13 @@
     explicit SkBlurImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const 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,
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                 SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 
 #ifdef SK_SUPPORT_LEGACY_PUBLICEFFECTCONSTRUCTORS
diff --git a/include/effects/SkColorFilterImageFilter.h b/include/effects/SkColorFilterImageFilter.h
index d127f36..6c88361 100644
--- a/include/effects/SkColorFilterImageFilter.h
+++ b/include/effects/SkColorFilterImageFilter.h
@@ -25,7 +25,7 @@
     SkColorFilterImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
 
     virtual bool asColorFilter(SkColorFilter**) const SK_OVERRIDE;
diff --git a/include/effects/SkComposeImageFilter.h b/include/effects/SkComposeImageFilter.h
index 7a982d8..61a32d3 100644
--- a/include/effects/SkComposeImageFilter.h
+++ b/include/effects/SkComposeImageFilter.h
@@ -23,7 +23,7 @@
 protected:
     explicit SkComposeImageFilter(SkReadBuffer& buffer);
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const SK_OVERRIDE;
 
diff --git a/include/effects/SkDisplacementMapEffect.h b/include/effects/SkDisplacementMapEffect.h
index 59549dc..4b4ea60 100644
--- a/include/effects/SkDisplacementMapEffect.h
+++ b/include/effects/SkDisplacementMapEffect.h
@@ -36,7 +36,7 @@
 
     virtual bool onFilterImage(Proxy* proxy,
                                const SkBitmap& src,
-                               const SkMatrix& ctm,
+                               const Context& ctx,
                                SkBitmap* dst,
                                SkIPoint* offset) const SK_OVERRIDE;
     virtual void computeFastBounds(const SkRect& src, SkRect* dst) const SK_OVERRIDE;
@@ -46,7 +46,7 @@
 
 #if SK_SUPPORT_GPU
     virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
-    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                 SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #endif
 
diff --git a/include/effects/SkDropShadowImageFilter.h b/include/effects/SkDropShadowImageFilter.h
index 5804386..05eb6a0 100644
--- a/include/effects/SkDropShadowImageFilter.h
+++ b/include/effects/SkDropShadowImageFilter.h
@@ -28,7 +28,7 @@
 protected:
     explicit SkDropShadowImageFilter(SkReadBuffer&);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    virtual bool onFilterImage(Proxy*, const SkBitmap& source, const SkMatrix&, SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
+    virtual bool onFilterImage(Proxy*, const SkBitmap& source, const Context&, SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
     virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
                                 SkIRect* dst) const SK_OVERRIDE;
 
diff --git a/include/effects/SkMagnifierImageFilter.h b/include/effects/SkMagnifierImageFilter.h
index cfe1f09..14bccdb 100644
--- a/include/effects/SkMagnifierImageFilter.h
+++ b/include/effects/SkMagnifierImageFilter.h
@@ -24,7 +24,7 @@
     explicit SkMagnifierImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #if SK_SUPPORT_GPU
     virtual bool asNewEffect(GrEffectRef** effect, GrTexture* texture, const SkMatrix& matrix, const SkIRect& bounds) const SK_OVERRIDE;
diff --git a/include/effects/SkMatrixConvolutionImageFilter.h b/include/effects/SkMatrixConvolutionImageFilter.h
index 524d05d..5b02fba 100644
--- a/include/effects/SkMatrixConvolutionImageFilter.h
+++ b/include/effects/SkMatrixConvolutionImageFilter.h
@@ -71,13 +71,13 @@
     SkMatrixConvolutionImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
 
 #if SK_SUPPORT_GPU
     virtual bool asNewEffect(GrEffectRef** effect,
                              GrTexture*,
-                             const SkMatrix& matrix,
+                             const SkMatrix& ctm,
                              const SkIRect& bounds) const SK_OVERRIDE;
 #endif
 
diff --git a/include/effects/SkMergeImageFilter.h b/include/effects/SkMergeImageFilter.h
index 54170a3..f3f0d67 100644
--- a/include/effects/SkMergeImageFilter.h
+++ b/include/effects/SkMergeImageFilter.h
@@ -33,7 +33,7 @@
     SkMergeImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
 
 #ifdef SK_SUPPORT_LEGACY_PUBLICEFFECTCONSTRUCTORS
diff --git a/include/effects/SkMorphologyImageFilter.h b/include/effects/SkMorphologyImageFilter.h
index edf1040..0c1cc85 100644
--- a/include/effects/SkMorphologyImageFilter.h
+++ b/include/effects/SkMorphologyImageFilter.h
@@ -32,14 +32,14 @@
     SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input,
                             const CropRect* cropRect);
     bool filterImageGeneric(Proc procX, Proc procY,
-                            Proxy*, const SkBitmap& src, const SkMatrix&,
+                            Proxy*, const SkBitmap& src, const Context&,
                             SkBitmap* result, SkIPoint* offset) const;
     SkMorphologyImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 #if SK_SUPPORT_GPU
     virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
     bool filterImageGPUGeneric(bool dilate, Proxy* proxy, const SkBitmap& src,
-                               const SkMatrix& ctm, SkBitmap* result,
+                               const Context& ctm, SkBitmap* result,
                                SkIPoint* offset) const;
 #endif
 
@@ -58,10 +58,10 @@
         return SkNEW_ARGS(SkDilateImageFilter, (radiusX, radiusY, input, cropRect));
     }
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #if SK_SUPPORT_GPU
-    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context&,
                                 SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #endif
 
@@ -90,10 +90,10 @@
         return SkNEW_ARGS(SkErodeImageFilter, (radiusX, radiusY, input, cropRect));
     }
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #if SK_SUPPORT_GPU
-    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context&,
                                 SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #endif
 
diff --git a/include/effects/SkOffsetImageFilter.h b/include/effects/SkOffsetImageFilter.h
index 93fc273..8d9db64 100644
--- a/include/effects/SkOffsetImageFilter.h
+++ b/include/effects/SkOffsetImageFilter.h
@@ -26,7 +26,7 @@
     SkOffsetImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const SK_OVERRIDE;
 
diff --git a/include/effects/SkPictureImageFilter.h b/include/effects/SkPictureImageFilter.h
index 1eda5dc..37da208 100644
--- a/include/effects/SkPictureImageFilter.h
+++ b/include/effects/SkPictureImageFilter.h
@@ -40,7 +40,7 @@
      */
     explicit SkPictureImageFilter(SkReadBuffer&);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 
 #ifdef SK_SUPPORT_LEGACY_PUBLICEFFECTCONSTRUCTORS
diff --git a/include/effects/SkRectShaderImageFilter.h b/include/effects/SkRectShaderImageFilter.h
index 23c538e..817c44e 100644
--- a/include/effects/SkRectShaderImageFilter.h
+++ b/include/effects/SkRectShaderImageFilter.h
@@ -37,7 +37,7 @@
     SkRectShaderImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
 
 private:
diff --git a/include/effects/SkResizeImageFilter.h b/include/effects/SkResizeImageFilter.h
index 9cb7bf3..fb55eea 100644
--- a/include/effects/SkResizeImageFilter.h
+++ b/include/effects/SkResizeImageFilter.h
@@ -43,7 +43,7 @@
     SkResizeImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
     virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
                                 SkIRect* dst) const SK_OVERRIDE;
diff --git a/include/effects/SkTestImageFilters.h b/include/effects/SkTestImageFilters.h
index 24f6349..4b20936 100644
--- a/include/effects/SkTestImageFilters.h
+++ b/include/effects/SkTestImageFilters.h
@@ -18,7 +18,7 @@
     SkDownSampleImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
 
 private:
diff --git a/include/effects/SkTileImageFilter.h b/include/effects/SkTileImageFilter.h
index 4bc2a20..6c0fa68 100644
--- a/include/effects/SkTileImageFilter.h
+++ b/include/effects/SkTileImageFilter.h
@@ -24,7 +24,7 @@
         return SkNEW_ARGS(SkTileImageFilter, (srcRect, dstRect, input));
     }
 
-    virtual bool onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+    virtual bool onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                SkBitmap* dst, SkIPoint* offset) const SK_OVERRIDE;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTileImageFilter)
diff --git a/include/effects/SkXfermodeImageFilter.h b/include/effects/SkXfermodeImageFilter.h
index b3872a0..cacd9b0 100644
--- a/include/effects/SkXfermodeImageFilter.h
+++ b/include/effects/SkXfermodeImageFilter.h
@@ -33,12 +33,12 @@
 
     virtual bool onFilterImage(Proxy* proxy,
                                const SkBitmap& src,
-                               const SkMatrix& ctm,
+                               const Context& ctx,
                                SkBitmap* dst,
                                SkIPoint* offset) const SK_OVERRIDE;
 #if SK_SUPPORT_GPU
     virtual bool canFilterImageGPU() const SK_OVERRIDE { return !cropRectIsSet(); }
-    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                 SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #endif
 
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 94ca8d4..f8ee781 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -140,7 +140,8 @@
     virtual void makeRenderTargetCurrent();
 
     virtual bool canHandleImageFilter(const SkImageFilter*) SK_OVERRIDE;
-    virtual bool filterImage(const SkImageFilter*, const SkBitmap&, const SkMatrix&,
+    virtual bool filterImage(const SkImageFilter*, const SkBitmap&,
+                             const SkImageFilter::Context&,
                              SkBitmap*, SkIPoint*) SK_OVERRIDE;
 
     class SkAutoCachedTexture; // used internally
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 724f17f..96f4e40 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -164,7 +164,7 @@
 }
 
 bool SkBitmapDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src,
-                                 const SkMatrix& ctm, SkBitmap* result,
+                                 const SkImageFilter::Context& ctx, SkBitmap* result,
                                  SkIPoint* offset) {
     return false;
 }
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 1443f5e..4ac7b29 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1164,7 +1164,9 @@
             const SkBitmap& src = srcDev->accessBitmap(false);
             SkMatrix matrix = *iter.fMatrix;
             matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
-            if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
+            SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
+            SkImageFilter::Context ctx(matrix, clipBounds);
+            if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
                 SkPaint tmpUnfiltered(*paint);
                 tmpUnfiltered.setImageFilter(NULL);
                 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
@@ -1202,7 +1204,9 @@
             SkIPoint offset = SkIPoint::Make(0, 0);
             SkMatrix matrix = *iter.fMatrix;
             matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
-            if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
+            SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+            SkImageFilter::Context ctx(matrix, clipBounds);
+            if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
                 SkPaint tmpUnfiltered(*paint);
                 tmpUnfiltered.setImageFilter(NULL);
                 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
diff --git a/src/core/SkDeviceImageFilterProxy.h b/src/core/SkDeviceImageFilterProxy.h
index 6c968e9..5ee5634 100644
--- a/src/core/SkDeviceImageFilterProxy.h
+++ b/src/core/SkDeviceImageFilterProxy.h
@@ -21,9 +21,9 @@
         return fDevice->canHandleImageFilter(filter);
     }
     virtual bool filterImage(const SkImageFilter* filter, const SkBitmap& src,
-                             const SkMatrix& ctm,
+                             const SkImageFilter::Context& ctx,
                              SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
-        return fDevice->filterImage(filter, src, ctm, result, offset);
+        return fDevice->filterImage(filter, src, ctx, result, offset);
     }
 
 private:
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index 4a380c5..6613f09 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -93,7 +93,7 @@
 }
 
 bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
-                                const SkMatrix& ctm,
+                                const Context& context,
                                 SkBitmap* result, SkIPoint* offset) const {
     SkASSERT(result);
     SkASSERT(offset);
@@ -101,8 +101,8 @@
      *  Give the proxy first shot at the filter. If it returns false, ask
      *  the filter to do it.
      */
-    return (proxy && proxy->filterImage(this, src, ctm, result, offset)) ||
-           this->onFilterImage(proxy, src, ctm, result, offset);
+    return (proxy && proxy->filterImage(this, src, context, result, offset)) ||
+           this->onFilterImage(proxy, src, context, result, offset);
 }
 
 bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
@@ -134,7 +134,7 @@
     }
 }
 
-bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&,
+bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const Context&,
                                   SkBitmap*, SkIPoint*) const {
     return false;
 }
@@ -143,21 +143,21 @@
     return this->asNewEffect(NULL, NULL, SkMatrix::I(), SkIRect());
 }
 
-bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                    SkBitmap* result, SkIPoint* offset) const {
 #if SK_SUPPORT_GPU
     SkBitmap input = src;
     SkASSERT(fInputCount == 1);
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
     if (this->getInput(0) &&
-        !this->getInput(0)->getInputResultGPU(proxy, src, ctm, &input, &srcOffset)) {
+        !this->getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
         return false;
     }
     GrTexture* srcTexture = input.getTexture();
     SkIRect bounds;
     src.getBounds(&bounds);
     bounds.offset(srcOffset);
-    if (!this->applyCropRect(&bounds, ctm)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
     SkRect srcRect = SkRect::Make(bounds);
@@ -179,7 +179,7 @@
     offset->fX = bounds.left();
     offset->fY = bounds.top();
     bounds.offset(-srcOffset);
-    SkMatrix matrix(ctm);
+    SkMatrix matrix(ctx.ctm());
     matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
     this->asNewEffect(&effect, srcTexture, matrix, bounds);
     SkASSERT(effect);
@@ -253,7 +253,7 @@
 }
 
 bool SkImageFilter::getInputResultGPU(SkImageFilter::Proxy* proxy,
-                                      const SkBitmap& src, const SkMatrix& ctm,
+                                      const SkBitmap& src, const Context& ctx,
                                       SkBitmap* result, SkIPoint* offset) const {
     // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity
     // matrix with no clip and that the matrix, clip, and render target set before this function was
@@ -261,9 +261,9 @@
     GrContext* context = src.getTexture()->getContext();
     GrContext::AutoWideOpenIdentityDraw awoid(context, NULL);
     if (this->canFilterImageGPU()) {
-        return this->filterImageGPU(proxy, src, ctm, result, offset);
+        return this->filterImageGPU(proxy, src, ctx, result, offset);
     } else {
-        if (this->filterImage(proxy, src, ctm, result, offset)) {
+        if (this->filterImage(proxy, src, ctx, result, offset)) {
             if (!result->getTexture()) {
                 SkImageInfo info;
                 if (!result->asImageInfo(&info)) {
diff --git a/src/effects/SkAlphaThresholdFilter.cpp b/src/effects/SkAlphaThresholdFilter.cpp
index 1163f67..f938ba6 100644
--- a/src/effects/SkAlphaThresholdFilter.cpp
+++ b/src/effects/SkAlphaThresholdFilter.cpp
@@ -21,7 +21,7 @@
     explicit SkAlphaThresholdFilterImpl(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #if SK_SUPPORT_GPU
     virtual bool asNewEffect(GrEffectRef** effect, GrTexture* texture,
@@ -304,7 +304,7 @@
 }
 
 bool SkAlphaThresholdFilterImpl::onFilterImage(Proxy*, const SkBitmap& src,
-                                               const SkMatrix& matrix, SkBitmap* dst,
+                                               const Context& ctx, SkBitmap* dst,
                                                SkIPoint* offset) const {
     SkASSERT(src.colorType() == kPMColor_SkColorType);
 
@@ -313,7 +313,7 @@
     }
 
     SkMatrix localInverse;
-    if (!matrix.invert(&localInverse)) {
+    if (!ctx.ctm().invert(&localInverse)) {
         return false;
     }
 
diff --git a/src/effects/SkBicubicImageFilter.cpp b/src/effects/SkBicubicImageFilter.cpp
index acf48f0..eabf066 100644
--- a/src/effects/SkBicubicImageFilter.cpp
+++ b/src/effects/SkBicubicImageFilter.cpp
@@ -82,12 +82,12 @@
 
 bool SkBicubicImageFilter::onFilterImage(Proxy* proxy,
                                          const SkBitmap& source,
-                                         const SkMatrix& matrix,
+                                         const Context& ctx,
                                          SkBitmap* result,
                                          SkIPoint* offset) const {
     SkBitmap src = source;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, &srcOffset)) {
+    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
         return false;
     }
 
@@ -168,10 +168,10 @@
 
 #if SK_SUPPORT_GPU
 
-bool SkBicubicImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+bool SkBicubicImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                           SkBitmap* result, SkIPoint* offset) const {
     SkBitmap srcBM = src;
-    if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctm, &srcBM, offset)) {
+    if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &srcBM, offset)) {
         return false;
     }
     GrTexture* srcTexture = srcBM.getTexture();
diff --git a/src/effects/SkBitmapSource.cpp b/src/effects/SkBitmapSource.cpp
index dd44be7..6834e9e 100644
--- a/src/effects/SkBitmapSource.cpp
+++ b/src/effects/SkBitmapSource.cpp
@@ -42,11 +42,11 @@
     buffer.writeRect(fDstRect);
 }
 
-bool SkBitmapSource::onFilterImage(Proxy* proxy, const SkBitmap&, const SkMatrix& matrix,
+bool SkBitmapSource::onFilterImage(Proxy* proxy, const SkBitmap&, const Context& ctx,
                                    SkBitmap* result, SkIPoint* offset) const {
     SkRect bounds, dstRect;
     fBitmap.getBounds(&bounds);
-    matrix.mapRect(&dstRect, fDstRect);
+    ctx.ctm().mapRect(&dstRect, fDstRect);
     if (fSrcRect == bounds && dstRect == bounds) {
         // No regions cropped out or resized; return entire bitmap.
         *result = fBitmap;
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index c5e3a78..adcb28f 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -134,11 +134,11 @@
 }
 
 bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
-                                      const SkBitmap& source, const SkMatrix& ctm,
+                                      const SkBitmap& source, const Context& ctx,
                                       SkBitmap* dst, SkIPoint* offset) const {
     SkBitmap src = source;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, &srcOffset)) {
+    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
         return false;
     }
 
@@ -154,7 +154,7 @@
     SkIRect srcBounds, dstBounds;
     src.getBounds(&srcBounds);
     srcBounds.offset(srcOffset);
-    if (!this->applyCropRect(&srcBounds, ctm)) {
+    if (!this->applyCropRect(&srcBounds, ctx.ctm())) {
         return false;
     }
 
@@ -165,7 +165,7 @@
     }
 
     SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
-    ctm.mapVectors(&sigma, &localSigma, 1);
+    ctx.ctm().mapVectors(&sigma, &localSigma, 1);
 
     int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
     int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
@@ -250,23 +250,23 @@
     return true;
 }
 
-bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                        SkBitmap* result, SkIPoint* offset) const {
 #if SK_SUPPORT_GPU
     SkBitmap input = src;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctm, &input, &srcOffset)) {
+    if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
         return false;
     }
     GrTexture* source = input.getTexture();
     SkIRect rect;
     src.getBounds(&rect);
     rect.offset(srcOffset);
-    if (!this->applyCropRect(&rect, ctm)) {
+    if (!this->applyCropRect(&rect, ctx.ctm())) {
         return false;
     }
     SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
-    ctm.mapVectors(&sigma, &localSigma, 1);
+    ctx.ctm().mapVectors(&sigma, &localSigma, 1);
     offset->fX = rect.fLeft;
     offset->fY = rect.fTop;
     rect.offset(-srcOffset);
diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp
index c825691..0de7330 100755
--- a/src/effects/SkColorFilterImageFilter.cpp
+++ b/src/effects/SkColorFilterImageFilter.cpp
@@ -99,19 +99,19 @@
 }
 
 bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
-                                             const SkMatrix& matrix,
+                                             const Context& ctx,
                                              SkBitmap* result,
                                              SkIPoint* offset) const {
     SkBitmap src = source;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, &srcOffset)) {
+    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
         return false;
     }
 
     SkIRect bounds;
     src.getBounds(&bounds);
     bounds.offset(srcOffset);
-    if (!this->applyCropRect(&bounds, matrix)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
 
diff --git a/src/effects/SkComposeImageFilter.cpp b/src/effects/SkComposeImageFilter.cpp
index 842ff48..645d633 100644
--- a/src/effects/SkComposeImageFilter.cpp
+++ b/src/effects/SkComposeImageFilter.cpp
@@ -15,7 +15,7 @@
 
 bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
                                          const SkBitmap& src,
-                                         const SkMatrix& ctm,
+                                         const Context& ctx,
                                          SkBitmap* result,
                                          SkIPoint* offset) const {
     SkImageFilter* outer = getInput(0);
@@ -26,12 +26,12 @@
     }
 
     if (!outer || !inner) {
-        return (outer ? outer : inner)->filterImage(proxy, src, ctm, result, offset);
+        return (outer ? outer : inner)->filterImage(proxy, src, ctx, result, offset);
     }
 
     SkBitmap tmp;
-    return inner->filterImage(proxy, src, ctm, &tmp, offset) &&
-           outer->filterImage(proxy, tmp, ctm, result, offset);
+    return inner->filterImage(proxy, src, ctx, &tmp, offset) &&
+           outer->filterImage(proxy, tmp, ctx, result, offset);
 }
 
 bool SkComposeImageFilter::onFilterBounds(const SkIRect& src,
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index 15e93c1..a1c18c6 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -194,15 +194,15 @@
 
 bool SkDisplacementMapEffect::onFilterImage(Proxy* proxy,
                                             const SkBitmap& src,
-                                            const SkMatrix& ctm,
+                                            const Context& ctx,
                                             SkBitmap* dst,
                                             SkIPoint* offset) const {
     SkBitmap displ = src, color = src;
     const SkImageFilter* colorInput = getColorInput();
     const SkImageFilter* displInput = getDisplacementInput();
     SkIPoint colorOffset = SkIPoint::Make(0, 0), displOffset = SkIPoint::Make(0, 0);
-    if ((colorInput && !colorInput->filterImage(proxy, src, ctm, &color, &colorOffset)) ||
-        (displInput && !displInput->filterImage(proxy, src, ctm, &displ, &displOffset))) {
+    if ((colorInput && !colorInput->filterImage(proxy, src, ctx, &color, &colorOffset)) ||
+        (displInput && !displInput->filterImage(proxy, src, ctx, &displ, &displOffset))) {
         return false;
     }
     if ((displ.colorType() != kPMColor_SkColorType) ||
@@ -217,13 +217,13 @@
     SkIRect bounds;
     color.getBounds(&bounds);
     bounds.offset(colorOffset);
-    if (!this->applyCropRect(&bounds, ctm)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
     SkIRect displBounds;
     displ.getBounds(&displBounds);
     displBounds.offset(displOffset);
-    if (!this->applyCropRect(&displBounds, ctm)) {
+    if (!this->applyCropRect(&displBounds, ctx.ctm())) {
         return false;
     }
     if (!bounds.intersect(displBounds)) {
@@ -236,7 +236,7 @@
     }
 
     SkVector scale = SkVector::Make(fScale, fScale);
-    ctm.mapVectors(&scale, 1);
+    ctx.ctm().mapVectors(&scale, 1);
     SkIRect colorBounds = bounds;
     colorBounds.offset(-colorOffset);
 
@@ -348,11 +348,11 @@
     typedef GrEffect INHERITED;
 };
 
-bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                              SkBitmap* result, SkIPoint* offset) const {
     SkBitmap colorBM = src;
     SkIPoint colorOffset = SkIPoint::Make(0, 0);
-    if (getColorInput() && !getColorInput()->getInputResultGPU(proxy, src, ctm, &colorBM,
+    if (getColorInput() && !getColorInput()->getInputResultGPU(proxy, src, ctx, &colorBM,
                                                                &colorOffset)) {
         return false;
     }
@@ -360,7 +360,7 @@
     SkBitmap displacementBM = src;
     SkIPoint displacementOffset = SkIPoint::Make(0, 0);
     if (getDisplacementInput() &&
-        !getDisplacementInput()->getInputResultGPU(proxy, src, ctm, &displacementBM,
+        !getDisplacementInput()->getInputResultGPU(proxy, src, ctx, &displacementBM,
                                                    &displacementOffset)) {
         return false;
     }
@@ -379,17 +379,17 @@
     GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
 
     SkVector scale = SkVector::Make(fScale, fScale);
-    ctm.mapVectors(&scale, 1);
+    ctx.ctm().mapVectors(&scale, 1);
     SkIRect bounds;
     colorBM.getBounds(&bounds);
     bounds.offset(colorOffset);
-    if (!this->applyCropRect(&bounds, ctm)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
     SkIRect displBounds;
     displacementBM.getBounds(&displBounds);
     displBounds.offset(displacementOffset);
-    if (!this->applyCropRect(&displBounds, ctm)) {
+    if (!this->applyCropRect(&displBounds, ctx.ctm())) {
         return false;
     }
     if (!bounds.intersect(displBounds)) {
diff --git a/src/effects/SkDropShadowImageFilter.cpp b/src/effects/SkDropShadowImageFilter.cpp
index 7c9631c..94055ce 100644
--- a/src/effects/SkDropShadowImageFilter.cpp
+++ b/src/effects/SkDropShadowImageFilter.cpp
@@ -58,17 +58,17 @@
     buffer.writeColor(fColor);
 }
 
-bool SkDropShadowImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const SkMatrix& matrix, SkBitmap* result, SkIPoint* offset) const
+bool SkDropShadowImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* result, SkIPoint* offset) const
 {
     SkBitmap src = source;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, &srcOffset))
+    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset))
         return false;
 
     SkIRect bounds;
     src.getBounds(&bounds);
     bounds.offset(srcOffset);
-    if (!this->applyCropRect(&bounds, matrix)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
 
@@ -79,7 +79,7 @@
     SkCanvas canvas(device.get());
 
     SkVector sigma, localSigma = SkVector::Make(fSigmaX, fSigmaY);
-    matrix.mapVectors(&sigma, &localSigma, 1);
+    ctx.ctm().mapVectors(&sigma, &localSigma, 1);
     sigma.fX = SkMaxScalar(0, sigma.fX);
     sigma.fY = SkMaxScalar(0, sigma.fY);
     SkAutoTUnref<SkImageFilter> blurFilter(SkBlurImageFilter::Create(sigma.fX, sigma.fY));
@@ -89,7 +89,7 @@
     paint.setColorFilter(colorFilter.get());
     paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
     SkVector offsetVec, localOffsetVec = SkVector::Make(fDx, fDy);
-    matrix.mapVectors(&offsetVec, &localOffsetVec, 1);
+    ctx.ctm().mapVectors(&offsetVec, &localOffsetVec, 1);
     canvas.translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop));
     canvas.drawBitmap(src, offsetVec.fX, offsetVec.fY, &paint);
     canvas.drawBitmap(src, 0, 0);
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index ec33c29..206be1b 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -271,7 +271,7 @@
 protected:
     explicit SkDiffuseLightingImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #if SK_SUPPORT_GPU
     virtual bool asNewEffect(GrEffectRef** effect, GrTexture*, const SkMatrix& matrix, const SkIRect& bounds) const SK_OVERRIDE;
@@ -293,7 +293,7 @@
 protected:
     explicit SkSpecularLightingImageFilter(SkReadBuffer& buffer);
     virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 #if SK_SUPPORT_GPU
     virtual bool asNewEffect(GrEffectRef** effect, GrTexture*, const SkMatrix& matrix, const SkIRect& bounds) const SK_OVERRIDE;
@@ -933,13 +933,13 @@
 
 bool SkDiffuseLightingImageFilter::onFilterImage(Proxy* proxy,
                                                  const SkBitmap& source,
-                                                 const SkMatrix& ctm,
+                                                 const Context& ctx,
                                                  SkBitmap* dst,
                                                  SkIPoint* offset) const {
     SkImageFilter* input = getInput(0);
     SkBitmap src = source;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (input && !input->filterImage(proxy, source, ctm, &src, &srcOffset)) {
+    if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) {
         return false;
     }
 
@@ -954,7 +954,7 @@
     SkIRect bounds;
     src.getBounds(&bounds);
     bounds.offset(srcOffset);
-    if (!this->applyCropRect(&bounds, ctm)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
 
@@ -967,7 +967,7 @@
         return false;
     }
 
-    SkAutoTUnref<SkLight> transformedLight(light()->transform(ctm));
+    SkAutoTUnref<SkLight> transformedLight(light()->transform(ctx.ctm()));
 
     DiffuseLightingType lightingType(fKD);
     offset->fX = bounds.left();
@@ -1026,13 +1026,13 @@
 
 bool SkSpecularLightingImageFilter::onFilterImage(Proxy* proxy,
                                                   const SkBitmap& source,
-                                                  const SkMatrix& ctm,
+                                                  const Context& ctx,
                                                   SkBitmap* dst,
                                                   SkIPoint* offset) const {
     SkImageFilter* input = getInput(0);
     SkBitmap src = source;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (input && !input->filterImage(proxy, source, ctm, &src, &srcOffset)) {
+    if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) {
         return false;
     }
 
@@ -1047,7 +1047,7 @@
     SkIRect bounds;
     src.getBounds(&bounds);
     bounds.offset(srcOffset);
-    if (!this->applyCropRect(&bounds, ctm)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
 
@@ -1065,7 +1065,7 @@
     offset->fX = bounds.left();
     offset->fY = bounds.top();
     bounds.offset(-srcOffset);
-    SkAutoTUnref<SkLight> transformedLight(light()->transform(ctm));
+    SkAutoTUnref<SkLight> transformedLight(light()->transform(ctx.ctm()));
     switch (transformedLight->type()) {
         case SkLight::kDistant_LightType:
             lightBitmap<SpecularLightingType, SkDistantLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp
index 931ed7e..51ade99 100644
--- a/src/effects/SkMagnifierImageFilter.cpp
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -280,7 +280,7 @@
 }
 
 bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src,
-                                           const SkMatrix&, SkBitmap* dst,
+                                           const Context&, SkBitmap* dst,
                                            SkIPoint* offset) const {
     SkASSERT(src.colorType() == kPMColor_SkColorType);
     SkASSERT(fSrcRect.width() < src.width());
diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp
index 679764a..1df1ff9 100644
--- a/src/effects/SkMatrixConvolutionImageFilter.cpp
+++ b/src/effects/SkMatrixConvolutionImageFilter.cpp
@@ -251,12 +251,12 @@
 
 bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
                                                    const SkBitmap& source,
-                                                   const SkMatrix& matrix,
+                                                   const Context& ctx,
                                                    SkBitmap* result,
                                                    SkIPoint* offset) const {
     SkBitmap src = source;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, &srcOffset)) {
+    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
         return false;
     }
 
@@ -267,7 +267,7 @@
     SkIRect bounds;
     src.getBounds(&bounds);
     bounds.offset(srcOffset);
-    if (!this->applyCropRect(&bounds, matrix)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
 
diff --git a/src/effects/SkMergeImageFilter.cpp b/src/effects/SkMergeImageFilter.cpp
index 5f66244..ff36e5b 100755
--- a/src/effects/SkMergeImageFilter.cpp
+++ b/src/effects/SkMergeImageFilter.cpp
@@ -66,7 +66,7 @@
 }
 
 bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
-                                       const SkMatrix& ctm,
+                                       const Context& ctx,
                                        SkBitmap* result, SkIPoint* offset) const {
     if (countInputs() < 1) {
         return false;
@@ -74,7 +74,7 @@
 
     SkIRect bounds;
     src.getBounds(&bounds);
-    if (!this->applyCropRect(&bounds, ctm)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
 
@@ -95,7 +95,7 @@
         SkIPoint pos = SkIPoint::Make(0, 0);
         SkImageFilter* filter = getInput(i);
         if (filter) {
-            if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
+            if (!filter->filterImage(proxy, src, ctx, &tmp, &pos)) {
                 return false;
             }
             srcPtr = &tmp;
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index a8f9da4..90940b9 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -140,12 +140,12 @@
                                                  SkMorphologyImageFilter::Proc procY,
                                                  Proxy* proxy,
                                                  const SkBitmap& source,
-                                                 const SkMatrix& ctm,
+                                                 const Context& ctx,
                                                  SkBitmap* dst,
                                                  SkIPoint* offset) const {
     SkBitmap src = source;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, &srcOffset)) {
+    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
         return false;
     }
 
@@ -156,7 +156,7 @@
     SkIRect bounds;
     src.getBounds(&bounds);
     bounds.offset(srcOffset);
-    if (!this->applyCropRect(&bounds, ctm)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
 
@@ -173,7 +173,7 @@
 
     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
                                      SkIntToScalar(this->radius().height()));
-    ctm.mapVectors(&radius, 1);
+    ctx.ctm().mapVectors(&radius, 1);
     int width = SkScalarFloorToInt(radius.fX);
     int height = SkScalarFloorToInt(radius.fY);
 
@@ -212,7 +212,7 @@
 }
 
 bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
-                                       const SkBitmap& source, const SkMatrix& ctm,
+                                       const SkBitmap& source, const Context& ctx,
                                        SkBitmap* dst, SkIPoint* offset) const {
     Proc erodeXProc = SkMorphologyGetPlatformProc(kErodeX_SkMorphologyProcType);
     if (!erodeXProc) {
@@ -222,11 +222,11 @@
     if (!erodeYProc) {
         erodeYProc = erode<kY>;
     }
-    return this->filterImageGeneric(erodeXProc, erodeYProc, proxy, source, ctm, dst, offset);
+    return this->filterImageGeneric(erodeXProc, erodeYProc, proxy, source, ctx, dst, offset);
 }
 
 bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
-                                        const SkBitmap& source, const SkMatrix& ctm,
+                                        const SkBitmap& source, const Context& ctx,
                                         SkBitmap* dst, SkIPoint* offset) const {
     Proc dilateXProc = SkMorphologyGetPlatformProc(kDilateX_SkMorphologyProcType);
     if (!dilateXProc) {
@@ -236,7 +236,7 @@
     if (!dilateYProc) {
         dilateYProc = dilate<kY>;
     }
-    return this->filterImageGeneric(dilateXProc, dilateYProc, proxy, source, ctm, dst, offset);
+    return this->filterImageGeneric(dilateXProc, dilateYProc, proxy, source, ctx, dst, offset);
 }
 
 void SkMorphologyImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
@@ -538,23 +538,23 @@
 bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate,
                                                     Proxy* proxy,
                                                     const SkBitmap& src,
-                                                    const SkMatrix& ctm,
+                                                    const Context& ctx,
                                                     SkBitmap* result,
                                                     SkIPoint* offset) const {
     SkBitmap input = src;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctm, &input, &srcOffset)) {
+    if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
         return false;
     }
     SkIRect bounds;
     input.getBounds(&bounds);
     bounds.offset(srcOffset);
-    if (!this->applyCropRect(&bounds, ctm)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
                                      SkIntToScalar(this->radius().height()));
-    ctm.mapVectors(&radius, 1);
+    ctx.ctm().mapVectors(&radius, 1);
     int width = SkScalarFloorToInt(radius.fX);
     int height = SkScalarFloorToInt(radius.fY);
 
@@ -581,14 +581,14 @@
     return true;
 }
 
-bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                          SkBitmap* result, SkIPoint* offset) const {
-    return this->filterImageGPUGeneric(true, proxy, src, ctm, result, offset);
+    return this->filterImageGPUGeneric(true, proxy, src, ctx, result, offset);
 }
 
-bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                         SkBitmap* result, SkIPoint* offset) const {
-    return this->filterImageGPUGeneric(false, proxy, src, ctm, result, offset);
+    return this->filterImageGPUGeneric(false, proxy, src, ctx, result, offset);
 }
 
 #endif
diff --git a/src/effects/SkOffsetImageFilter.cpp b/src/effects/SkOffsetImageFilter.cpp
index 12c5af6..98eb05b 100644
--- a/src/effects/SkOffsetImageFilter.cpp
+++ b/src/effects/SkOffsetImageFilter.cpp
@@ -15,7 +15,7 @@
 #include "SkPaint.h"
 
 bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
-                                        const SkMatrix& matrix,
+                                        const Context& ctx,
                                         SkBitmap* result,
                                         SkIPoint* offset) const {
     SkImageFilter* input = getInput(0);
@@ -26,18 +26,18 @@
 #else
     if (!cropRectIsSet()) {
 #endif
-        if (input && !input->filterImage(proxy, source, matrix, &src, &srcOffset)) {
+        if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) {
             return false;
         }
 
         SkVector vec;
-        matrix.mapVectors(&vec, &fOffset, 1);
+        ctx.ctm().mapVectors(&vec, &fOffset, 1);
 
         offset->fX = srcOffset.fX + SkScalarRoundToInt(vec.fX);
         offset->fY = srcOffset.fY + SkScalarRoundToInt(vec.fY);
         *result = src;
     } else {
-        if (input && !input->filterImage(proxy, source, matrix, &src, &srcOffset)) {
+        if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) {
             return false;
         }
 
@@ -45,7 +45,7 @@
         src.getBounds(&bounds);
         bounds.offset(srcOffset);
 
-        if (!applyCropRect(&bounds, matrix)) {
+        if (!applyCropRect(&bounds, ctx.ctm())) {
             return false;
         }
 
@@ -59,7 +59,7 @@
         canvas.translate(SkIntToScalar(srcOffset.fX - bounds.fLeft),
                          SkIntToScalar(srcOffset.fY - bounds.fTop));
         SkVector vec;
-        matrix.mapVectors(&vec, &fOffset, 1);
+        ctx.ctm().mapVectors(&vec, &fOffset, 1);
         canvas.drawBitmap(src, vec.x(), vec.y(), &paint);
         *result = device->accessBitmap(false);
         offset->fX = bounds.fLeft;
diff --git a/src/effects/SkPictureImageFilter.cpp b/src/effects/SkPictureImageFilter.cpp
index 8db9914..0b36b1d 100644
--- a/src/effects/SkPictureImageFilter.cpp
+++ b/src/effects/SkPictureImageFilter.cpp
@@ -58,8 +58,8 @@
     buffer.writeRect(fCropRect);
 }
 
-bool SkPictureImageFilter::onFilterImage(Proxy* proxy, const SkBitmap&, const SkMatrix& matrix,
-                                   SkBitmap* result, SkIPoint* offset) const {
+bool SkPictureImageFilter::onFilterImage(Proxy* proxy, const SkBitmap&, const Context& ctx,
+                                         SkBitmap* result, SkIPoint* offset) const {
     if (!fPicture) {
         offset->fX = offset->fY = 0;
         return true;
@@ -67,7 +67,7 @@
 
     SkRect floatBounds;
     SkIRect bounds;
-    matrix.mapRect(&floatBounds, fCropRect);
+    ctx.ctm().mapRect(&floatBounds, fCropRect);
     floatBounds.roundOut(&bounds);
 
     if (bounds.isEmpty()) {
@@ -84,7 +84,7 @@
     SkPaint paint;
 
     canvas.translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop));
-    canvas.concat(matrix);
+    canvas.concat(ctx.ctm());
     canvas.drawPicture(*fPicture);
 
     *result = device.get()->accessBitmap(false);
diff --git a/src/effects/SkRectShaderImageFilter.cpp b/src/effects/SkRectShaderImageFilter.cpp
index 27b39d7..dad0e02 100644
--- a/src/effects/SkRectShaderImageFilter.cpp
+++ b/src/effects/SkRectShaderImageFilter.cpp
@@ -52,12 +52,12 @@
 
 bool SkRectShaderImageFilter::onFilterImage(Proxy* proxy,
                                             const SkBitmap& source,
-                                            const SkMatrix& ctm,
+                                            const Context& ctx,
                                             SkBitmap* result,
                                             SkIPoint* offset) const {
     SkIRect bounds;
     source.getBounds(&bounds);
-    if (!this->applyCropRect(&bounds, ctm)) {
+    if (!this->applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
 
@@ -69,7 +69,7 @@
     SkCanvas canvas(device.get());
     SkPaint paint;
     paint.setShader(fShader);
-    SkMatrix matrix(ctm);
+    SkMatrix matrix(ctx.ctm());
     matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
     fShader->setLocalMatrix(matrix);
     SkRect rect = SkRect::MakeWH(SkIntToScalar(bounds.width()), SkIntToScalar(bounds.height()));
diff --git a/src/effects/SkResizeImageFilter.cpp b/src/effects/SkResizeImageFilter.cpp
index 51c1d6d..87c30c6 100644
--- a/src/effects/SkResizeImageFilter.cpp
+++ b/src/effects/SkResizeImageFilter.cpp
@@ -42,12 +42,12 @@
 
 bool SkResizeImageFilter::onFilterImage(Proxy* proxy,
                                          const SkBitmap& source,
-                                         const SkMatrix& ctm,
+                                         const Context& ctx,
                                          SkBitmap* result,
                                          SkIPoint* offset) const {
     SkBitmap src = source;
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, &srcOffset)) {
+    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
         return false;
     }
 
@@ -57,11 +57,11 @@
     srcBounds.offset(srcOffset);
     SkRect srcRect = SkRect::Make(srcBounds);
     SkMatrix matrix;
-    if (!ctm.invert(&matrix)) {
+    if (!ctx.ctm().invert(&matrix)) {
         return false;
     }
     matrix.postScale(fSx, fSy);
-    matrix.postConcat(ctm);
+    matrix.postConcat(ctx.ctm());
     matrix.mapRect(&dstRect, srcRect);
     dstRect.roundOut(&dstBounds);
 
diff --git a/src/effects/SkTestImageFilters.cpp b/src/effects/SkTestImageFilters.cpp
index 6927932..da88316 100755
--- a/src/effects/SkTestImageFilters.cpp
+++ b/src/effects/SkTestImageFilters.cpp
@@ -22,7 +22,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkDownSampleImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
-                                            const SkMatrix&,
+                                            const Context&,
                                             SkBitmap* result, SkIPoint*) const {
     SkScalar scale = fScale;
     if (scale > SK_Scalar1 || scale <= 0) {
diff --git a/src/effects/SkTileImageFilter.cpp b/src/effects/SkTileImageFilter.cpp
index f17e2ea..b35d852 100644
--- a/src/effects/SkTileImageFilter.cpp
+++ b/src/effects/SkTileImageFilter.cpp
@@ -16,17 +16,17 @@
 #include "SkShader.h"
 #include "SkValidationUtils.h"
 
-bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
+bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const SkImageFilter::Context& ctx,
                                       SkBitmap* dst, SkIPoint* offset) const {
     SkBitmap source = src;
     SkImageFilter* input = getInput(0);
     SkIPoint srcOffset = SkIPoint::Make(0, 0);
-    if (input && !input->filterImage(proxy, src, ctm, &source, &srcOffset)) {
+    if (input && !input->filterImage(proxy, src, ctx, &source, &srcOffset)) {
         return false;
     }
 
     SkRect dstRect;
-    ctm.mapRect(&dstRect, fDstRect);
+    ctx.ctm().mapRect(&dstRect, fDstRect);
     SkIRect dstIRect;
     dstRect.roundOut(&dstIRect);
     int w = dstIRect.width();
@@ -36,7 +36,7 @@
     }
 
     SkRect srcRect;
-    ctm.mapRect(&srcRect, fSrcRect);
+    ctx.ctm().mapRect(&srcRect, fSrcRect);
     SkIRect srcIRect;
     srcRect.roundOut(&srcIRect);
     srcIRect.offset(-srcOffset);
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
index cdc026a..ebfd16c 100644
--- a/src/effects/SkXfermodeImageFilter.cpp
+++ b/src/effects/SkXfermodeImageFilter.cpp
@@ -44,7 +44,7 @@
 
 bool SkXfermodeImageFilter::onFilterImage(Proxy* proxy,
                                             const SkBitmap& src,
-                                            const SkMatrix& ctm,
+                                            const Context& ctx,
                                             SkBitmap* dst,
                                             SkIPoint* offset) const {
     SkBitmap background = src, foreground = src;
@@ -52,12 +52,12 @@
     SkImageFilter* foregroundInput = getInput(1);
     SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
     if (backgroundInput &&
-        !backgroundInput->filterImage(proxy, src, ctm, &background, &backgroundOffset)) {
+        !backgroundInput->filterImage(proxy, src, ctx, &background, &backgroundOffset)) {
         return false;
     }
     SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
     if (foregroundInput &&
-        !foregroundInput->filterImage(proxy, src, ctm, &foreground, &foregroundOffset)) {
+        !foregroundInput->filterImage(proxy, src, ctx, &foreground, &foregroundOffset)) {
         return false;
     }
 
@@ -67,7 +67,7 @@
     foreground.getBounds(&foregroundBounds);
     foregroundBounds.offset(foregroundOffset);
     bounds.join(foregroundBounds);
-    if (!applyCropRect(&bounds, ctm)) {
+    if (!applyCropRect(&bounds, ctx.ctm())) {
         return false;
     }
 
@@ -97,19 +97,19 @@
 
 bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy,
                                            const SkBitmap& src,
-                                           const SkMatrix& ctm,
+                                           const Context& ctx,
                                            SkBitmap* result,
                                            SkIPoint* offset) const {
     SkBitmap background = src;
     SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
-    if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctm, &background,
+    if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &background,
                                                        &backgroundOffset)) {
         return false;
     }
     GrTexture* backgroundTex = background.getTexture();
     SkBitmap foreground = src;
     SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
-    if (getInput(1) && !getInput(1)->getInputResultGPU(proxy, src, ctm, &foreground,
+    if (getInput(1) && !getInput(1)->getInputResultGPU(proxy, src, ctx, &foreground,
                                                        &foregroundOffset)) {
         return false;
     }
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index ed80c50..e872f1a 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1588,8 +1588,8 @@
 
 static bool filter_texture(SkBaseDevice* device, GrContext* context,
                            GrTexture* texture, const SkImageFilter* filter,
-                           int w, int h, const SkMatrix& ctm, SkBitmap* result,
-                           SkIPoint* offset) {
+                           int w, int h, const SkImageFilter::Context& ctx,
+                           SkBitmap* result, SkIPoint* offset) {
     SkASSERT(filter);
     SkDeviceImageFilterProxy proxy(device);
 
@@ -1597,7 +1597,7 @@
         // Save the render target and set it to NULL, so we don't accidentally draw to it in the
         // filter.  Also set the clip wide open and the matrix to identity.
         GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
-        return filter->filterImageGPU(&proxy, wrap_texture(texture), ctm, result, offset);
+        return filter->filterImageGPU(&proxy, wrap_texture(texture), ctx, result, offset);
     } else {
         return false;
     }
@@ -1628,7 +1628,9 @@
         SkIPoint offset = SkIPoint::Make(0, 0);
         SkMatrix matrix(*draw.fMatrix);
         matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
-        if (filter_texture(this, fContext, texture, filter, w, h, matrix, &filteredBitmap,
+        SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+        SkImageFilter::Context ctx(matrix, clipBounds);
+        if (filter_texture(this, fContext, texture, filter, w, h, ctx, &filteredBitmap,
                            &offset)) {
             texture = (GrTexture*) filteredBitmap.getTexture();
             w = filteredBitmap.width();
@@ -1734,7 +1736,9 @@
         SkIPoint offset = SkIPoint::Make(0, 0);
         SkMatrix matrix(*draw.fMatrix);
         matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
-        if (filter_texture(this, fContext, devTex, filter, w, h, matrix, &filteredBitmap,
+        SkIRect clipBounds = SkIRect::MakeWH(devTex->width(), devTex->height());
+        SkImageFilter::Context ctx(matrix, clipBounds);
+        if (filter_texture(this, fContext, devTex, filter, w, h, ctx, &filteredBitmap,
                            &offset)) {
             devTex = filteredBitmap.getTexture();
             w = filteredBitmap.width();
@@ -1771,7 +1775,7 @@
 }
 
 bool SkGpuDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src,
-                              const SkMatrix& ctm,
+                              const SkImageFilter::Context& ctx,
                               SkBitmap* result, SkIPoint* offset) {
     // want explicitly our impl, so guard against a subclass of us overriding it
     if (!this->SkGpuDevice::canHandleImageFilter(filter)) {
@@ -1788,8 +1792,8 @@
     // must be pushed upstack.
     SkAutoCachedTexture act(this, src, NULL, &texture);
 
-    return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctm, result,
-                          offset);
+    return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctx,
+                          result, offset);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index cc7011e..984fe05 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -250,7 +250,7 @@
         return false;
     }
     virtual bool filterImage(const SkImageFilter*, const SkBitmap&,
-                             const SkMatrix&, SkBitmap*, SkIPoint*) SK_OVERRIDE {
+                             const SkImageFilter::Context&, SkBitmap*, SkIPoint*) SK_OVERRIDE {
         return false;
     }
 
diff --git a/src/utils/SkGatherPixelRefsAndRects.h b/src/utils/SkGatherPixelRefsAndRects.h
index 75d0969..a6ff16d 100644
--- a/src/utils/SkGatherPixelRefsAndRects.h
+++ b/src/utils/SkGatherPixelRefsAndRects.h
@@ -307,7 +307,7 @@
     virtual void unlockPixels() SK_OVERRIDE { NothingToDo(); }
     virtual bool allowImageFilter(const SkImageFilter*) SK_OVERRIDE { return false; }
     virtual bool canHandleImageFilter(const SkImageFilter*) SK_OVERRIDE { return false; }
-    virtual bool filterImage(const SkImageFilter*, const SkBitmap&, const SkMatrix&,
+    virtual bool filterImage(const SkImageFilter*, const SkBitmap&, const SkImageFilter::Context&,
                              SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
         return false;
     }
diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp
index 99914a7..23d7afd 100644
--- a/src/utils/SkPictureUtils.cpp
+++ b/src/utils/SkPictureUtils.cpp
@@ -76,7 +76,7 @@
     virtual void unlockPixels() SK_OVERRIDE { nothing_to_do(); }
     virtual bool allowImageFilter(const SkImageFilter*) SK_OVERRIDE { return false; }
     virtual bool canHandleImageFilter(const SkImageFilter*) SK_OVERRIDE { return false; }
-    virtual bool filterImage(const SkImageFilter*, const SkBitmap&, const SkMatrix&,
+    virtual bool filterImage(const SkImageFilter*, const SkBitmap&, const SkImageFilter::Context&,
                              SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
         return false;
     }
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index 36c9a87..f730e1e 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -43,9 +43,9 @@
       : SkImageFilter(0), fReporter(reporter), fExpectedMatrix(expectedMatrix) {
     }
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix& ctm,
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context& ctx,
                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE {
-        REPORTER_ASSERT(fReporter, ctm == fExpectedMatrix);
+        REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
         return true;
     }
 
@@ -190,8 +190,9 @@
             SkDeviceImageFilterProxy proxy(&device);
             SkIPoint loc = SkIPoint::Make(0, 0);
             // An empty input should early return and return false
+            SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeEmpty());
             REPORTER_ASSERT(reporter,
-                            !bicubic->filterImage(&proxy, bitmap, SkMatrix::I(), &result, &loc));
+                            !bicubic->filterImage(&proxy, bitmap, ctx, &result, &loc));
         }
     }
 }
@@ -245,7 +246,8 @@
         SkIPoint offset;
         SkString str;
         str.printf("filter %d", static_cast<int>(i));
-        REPORTER_ASSERT_MESSAGE(reporter, filter->filterImage(&proxy, bitmap, SkMatrix::I(), &result, &offset), str.c_str());
+        SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeEmpty());
+        REPORTER_ASSERT_MESSAGE(reporter, filter->filterImage(&proxy, bitmap, ctx, &result, &offset), str.c_str());
         REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, str.c_str());
     }