Apply the CTM to filter parameters for SkBlurImageFilter, SkDropShadowImageFilter, SkDisplacementMapEffect and SkMorphologyImageFilter. I had resisted this (and may later put in an assert that the CTM contains no rotation, skew or perspective), but it just makes the filters play better with Skia generally.

This revealed that the displacement map was not handling clipping or upstream cropping at all well (the color would "swim" through the displacement at the edge of the clip rect). Fixed by passing through the correct offsets to the bitmap accesses in both raster and GPU paths. Same for morphology.

R=sugoi@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13127 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/imagefiltersscaled.cpp b/gm/imagefiltersscaled.cpp
new file mode 100644
index 0000000..f5275ca
--- /dev/null
+++ b/gm/imagefiltersscaled.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkColor.h"
+#include "SkBitmapSource.h"
+#include "SkBlurImageFilter.h"
+#include "SkDisplacementMapEffect.h"
+#include "SkDropShadowImageFilter.h"
+#include "SkGradientShader.h"
+#include "SkMorphologyImageFilter.h"
+#include "SkScalar.h"
+
+namespace skiagm {
+
+class ImageFiltersScaledGM : public GM {
+public:
+    ImageFiltersScaledGM() : fInitialized(false) {
+        this->setBGColor(0x00000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("imagefiltersscaled");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(860, 500);
+    }
+
+    void make_checkerboard() {
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 64, 64);
+        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 < 64; y += 16) {
+          for (int x = 0; x < 64; x += 16) {
+            canvas.save();
+            canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
+            canvas.drawRect(SkRect::MakeXYWH(0, 0, 8, 8), darkPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 0, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(0, 8, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 8, 8, 8), darkPaint);
+            canvas.restore();
+          }
+        }
+    }
+
+    void make_gradient_circle(int width, int height) {
+        SkScalar x = SkIntToScalar(width / 2);
+        SkScalar y = SkIntToScalar(height / 2);
+        SkScalar radius = SkScalarMul(SkMinScalar(x, y), SkIntToScalar(4) / SkIntToScalar(5));
+        fGradientCircle.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+        fGradientCircle.allocPixels();
+        SkBitmapDevice device(fGradientCircle);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkColor colors[2];
+        colors[0] = SK_ColorWHITE;
+        colors[1] = SK_ColorBLACK;
+        SkAutoTUnref<SkShader> shader(
+            SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, NULL, 2,
+                                           SkShader::kClamp_TileMode)
+        );
+        SkPaint paint;
+        paint.setShader(shader);
+        canvas.drawCircle(x, y, radius, paint);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            this->make_checkerboard();
+            this->make_gradient_circle(64, 64);
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+
+        SkAutoTUnref<SkImageFilter> gradient(new SkBitmapSource(fGradientCircle));
+        SkAutoTUnref<SkImageFilter> checkerboard(new SkBitmapSource(fCheckerboard));
+
+        SkImageFilter* filters[] = {
+            new SkBlurImageFilter(SkIntToScalar(4), SkIntToScalar(4)),
+            new SkDropShadowImageFilter(SkIntToScalar(5), SkIntToScalar(10), SkIntToScalar(3),
+                                        SK_ColorYELLOW),
+            new SkDisplacementMapEffect(SkDisplacementMapEffect::kR_ChannelSelectorType,
+                                        SkDisplacementMapEffect::kR_ChannelSelectorType,
+                                        SkIntToScalar(12),
+                                        gradient.get(),
+                                        checkerboard.get()),
+            new SkDilateImageFilter(1, 1, checkerboard.get()),
+            new SkErodeImageFilter(1, 1, checkerboard.get()),
+        };
+
+        SkVector scales[] = {
+            SkVector::Make(SkScalarInvert(2), SkScalarInvert(2)),
+            SkVector::Make(SkIntToScalar(1), SkIntToScalar(1)),
+            SkVector::Make(SkIntToScalar(1), SkIntToScalar(2)),
+            SkVector::Make(SkIntToScalar(2), SkIntToScalar(1)),
+            SkVector::Make(SkIntToScalar(2), SkIntToScalar(2)),
+        };
+
+        SkRect r = SkRect::MakeWH(SkIntToScalar(64), SkIntToScalar(64));
+        SkScalar margin = SkIntToScalar(16);
+        SkRect bounds = r;
+        bounds.outset(margin, margin);
+
+        for (size_t j = 0; j < SK_ARRAY_COUNT(scales); ++j) {
+            canvas->save();
+            for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
+                SkPaint paint;
+                paint.setColor(SK_ColorBLUE);
+                paint.setImageFilter(filters[i]);
+                paint.setAntiAlias(true);
+                canvas->save();
+                canvas->scale(scales[j].fX, scales[j].fY);
+                canvas->clipRect(bounds);
+                canvas->drawCircle(r.centerX(), r.centerY(),
+                                   SkScalarDiv(r.width()*2, SkIntToScalar(5)), paint);
+                canvas->restore();
+                canvas->translate(r.width() * scales[j].fX + margin, 0);
+            }
+            canvas->restore();
+            canvas->translate(0, r.height() * scales[j].fY + margin);
+        }
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
+            filters[i]->unref();
+        }
+    }
+
+private:
+    bool fInitialized;
+    SkBitmap fCheckerboard;
+    SkBitmap fGradientCircle;
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ImageFiltersScaledGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 8069419..0e53fb9 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -84,6 +84,7 @@
     '../gm/imagefiltersbase.cpp',
     '../gm/imagefilterscropped.cpp',
     '../gm/imagefiltersgraph.cpp',
+    '../gm/imagefiltersscaled.cpp',
     '../gm/internal_links.cpp',
     '../gm/lcdtext.cpp',
     '../gm/linepaths.cpp',
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index 5efef0b..7f3f532 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -165,10 +165,13 @@
         return false;
     }
 
+    SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
+    ctm.mapVectors(&sigma, &localSigma, 1);
+
     int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
     int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
-    getBox3Params(fSigma.width(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
-    getBox3Params(fSigma.height(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
+    getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
+    getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
 
     if (kernelSizeX < 0 || kernelSizeY < 0) {
         return false;
@@ -235,13 +238,15 @@
     if (!this->applyCropRect(&rect, ctm)) {
         return false;
     }
+    SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
+    ctm.mapVectors(&sigma, &localSigma, 1);
     SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(),
                                                              source,
                                                              false,
                                                              SkRect::Make(rect),
                                                              true,
-                                                             fSigma.width(),
-                                                             fSigma.height()));
+                                                             sigma.x(),
+                                                             sigma.y()));
     offset->fX = rect.fLeft;
     offset->fY = rect.fTop;
     return SkImageFilterUtils::WrapTexture(tex, rect.width(), rect.height(), result);
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index e9a9acb..8264ec8 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -47,22 +47,28 @@
 
 template<SkDisplacementMapEffect::ChannelSelectorType typeX,
          SkDisplacementMapEffect::ChannelSelectorType typeY>
-void computeDisplacement(SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src, const SkIRect& bounds)
+void computeDisplacement(const SkVector& scale, SkBitmap* dst,
+                         SkBitmap* displ, const SkIPoint& offset,
+                         SkBitmap* src,
+                         const SkIRect& bounds)
 {
     static const SkScalar Inv8bit = SkScalarDiv(SK_Scalar1, 255.0f);
     const int srcW = src->width();
     const int srcH = src->height();
-    const SkScalar scaleForColor = SkScalarMul(scale, Inv8bit);
-    const SkScalar scaleAdj = SK_ScalarHalf - SkScalarMul(scale, SK_ScalarHalf);
+    const SkVector scaleForColor = SkVector::Make(SkScalarMul(scale.fX, Inv8bit),
+                                                  SkScalarMul(scale.fY, Inv8bit));
+    const SkVector scaleAdj = SkVector::Make(SK_ScalarHalf - SkScalarMul(scale.fX, SK_ScalarHalf),
+                                             SK_ScalarHalf - SkScalarMul(scale.fY, SK_ScalarHalf));
     const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
     SkPMColor* dstPtr = dst->getAddr32(0, 0);
     for (int y = bounds.top(); y < bounds.bottom(); ++y) {
-        const SkPMColor* displPtr = displ->getAddr32(bounds.left(), y);
+        const SkPMColor* displPtr = displ->getAddr32(bounds.left() + offset.fX,
+                                                     y + offset.fY);
         for (int x = bounds.left(); x < bounds.right(); ++x, ++displPtr) {
-            const SkScalar displX = SkScalarMul(scaleForColor,
-                SkIntToScalar(getValue<typeX>(*displPtr, table))) + scaleAdj;
-            const SkScalar displY = SkScalarMul(scaleForColor,
-                SkIntToScalar(getValue<typeY>(*displPtr, table))) + scaleAdj;
+            const SkScalar displX = SkScalarMul(scaleForColor.fX,
+                SkIntToScalar(getValue<typeX>(*displPtr, table))) + scaleAdj.fX;
+            const SkScalar displY = SkScalarMul(scaleForColor.fY,
+                SkIntToScalar(getValue<typeY>(*displPtr, table))) + scaleAdj.fY;
             // Truncate the displacement values
             const int srcX = x + SkScalarTruncToInt(displX);
             const int srcY = y + SkScalarTruncToInt(displY);
@@ -74,24 +80,27 @@
 
 template<SkDisplacementMapEffect::ChannelSelectorType typeX>
 void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
-                         SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src, const SkIRect& bounds)
+                         const SkVector& scale, SkBitmap* dst,
+                         SkBitmap* displ, const SkIPoint& offset,
+                         SkBitmap* src,
+                         const SkIRect& bounds)
 {
     switch (yChannelSelector) {
       case SkDisplacementMapEffect::kR_ChannelSelectorType:
         computeDisplacement<typeX, SkDisplacementMapEffect::kR_ChannelSelectorType>(
-            scale, dst, displ, src, bounds);
+            scale, dst, displ, offset, src, bounds);
         break;
       case SkDisplacementMapEffect::kG_ChannelSelectorType:
         computeDisplacement<typeX, SkDisplacementMapEffect::kG_ChannelSelectorType>(
-            scale, dst, displ, src, bounds);
+            scale, dst, displ, offset, src, bounds);
         break;
       case SkDisplacementMapEffect::kB_ChannelSelectorType:
         computeDisplacement<typeX, SkDisplacementMapEffect::kB_ChannelSelectorType>(
-            scale, dst, displ, src, bounds);
+            scale, dst, displ, offset, src, bounds);
         break;
       case SkDisplacementMapEffect::kA_ChannelSelectorType:
         computeDisplacement<typeX, SkDisplacementMapEffect::kA_ChannelSelectorType>(
-            scale, dst, displ, src, bounds);
+            scale, dst, displ, offset, src, bounds);
         break;
       case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
       default:
@@ -101,24 +110,27 @@
 
 void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
                          SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
-                         SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src, const SkIRect& bounds)
+                         const SkVector& scale, SkBitmap* dst,
+                         SkBitmap* displ, const SkIPoint& offset,
+                         SkBitmap* src,
+                         const SkIRect& bounds)
 {
     switch (xChannelSelector) {
       case SkDisplacementMapEffect::kR_ChannelSelectorType:
         computeDisplacement<SkDisplacementMapEffect::kR_ChannelSelectorType>(
-            yChannelSelector, scale, dst, displ, src, bounds);
+            yChannelSelector, scale, dst, displ, offset, src, bounds);
         break;
       case SkDisplacementMapEffect::kG_ChannelSelectorType:
         computeDisplacement<SkDisplacementMapEffect::kG_ChannelSelectorType>(
-            yChannelSelector, scale, dst, displ, src, bounds);
+            yChannelSelector, scale, dst, displ, offset, src, bounds);
         break;
       case SkDisplacementMapEffect::kB_ChannelSelectorType:
         computeDisplacement<SkDisplacementMapEffect::kB_ChannelSelectorType>(
-            yChannelSelector, scale, dst, displ, src, bounds);
+            yChannelSelector, scale, dst, displ, offset, src, bounds);
         break;
       case SkDisplacementMapEffect::kA_ChannelSelectorType:
         computeDisplacement<SkDisplacementMapEffect::kA_ChannelSelectorType>(
-            yChannelSelector, scale, dst, displ, src, bounds);
+            yChannelSelector, scale, dst, displ, offset, src, bounds);
         break;
       case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
       default:
@@ -185,9 +197,10 @@
                                             SkIPoint* offset) {
     SkBitmap displ = src, color = src;
     SkImageFilter* colorInput = getColorInput();
-    SkImageFilter* displacementInput = getDisplacementInput();
-    if ((colorInput && !colorInput->filterImage(proxy, src, ctm, &color, offset)) ||
-        (displacementInput && !displacementInput->filterImage(proxy, src, ctm, &displ, offset))) {
+    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))) {
         return false;
     }
     if ((displ.config() != SkBitmap::kARGB_8888_Config) ||
@@ -201,11 +214,13 @@
     }
     SkIRect bounds;
     color.getBounds(&bounds);
+    bounds.offset(colorOffset);
     if (!this->applyCropRect(&bounds, ctm)) {
         return false;
     }
     SkIRect displBounds;
     displ.getBounds(&displBounds);
+    displBounds.offset(displOffset);
     if (!this->applyCropRect(&displBounds, ctm)) {
         return false;
     }
@@ -219,7 +234,13 @@
         return false;
     }
 
-    computeDisplacement(fXChannelSelector, fYChannelSelector, fScale, dst, &displ, &color, bounds);
+    SkVector scale = SkVector::Make(fScale, fScale);
+    ctm.mapVectors(&scale, 1);
+    SkIRect colorBounds = bounds;
+    colorBounds.offset(-colorOffset);
+
+    computeDisplacement(fXChannelSelector, fYChannelSelector, scale, dst,
+                        &displ, colorOffset - displOffset, &color, colorBounds);
 
     offset->fX = bounds.left();
     offset->fY = bounds.top();
@@ -261,11 +282,14 @@
 public:
     static GrEffectRef* Create(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
                                SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
-                               SkScalar scale, GrTexture* displacement, GrTexture* color) {
+                               SkVector scale,
+                               GrTexture* displacement, const SkMatrix& offsetMatrix,
+                               GrTexture* color) {
         AutoEffectUnref effect(SkNEW_ARGS(GrDisplacementMapEffect, (xChannelSelector,
                                                                     yChannelSelector,
                                                                     scale,
                                                                     displacement,
+                                                                    offsetMatrix,
                                                                     color)));
         return CreateEffectRef(effect);
     }
@@ -277,7 +301,7 @@
         { return fXChannelSelector; }
     SkDisplacementMapEffect::ChannelSelectorType yChannelSelector() const
         { return fYChannelSelector; }
-    SkScalar scale() const { return fScale; }
+    const SkVector& scale() const { return fScale; }
 
     typedef GrGLDisplacementMapEffect GLEffect;
     static const char* Name() { return "DisplacementMap"; }
@@ -289,7 +313,9 @@
 
     GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
                             SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
-                            SkScalar scale, GrTexture* displacement, GrTexture* color);
+                            const SkVector& scale,
+                            GrTexture* displacement, const SkMatrix& offsetMatrix,
+                            GrTexture* color);
 
     GR_DECLARE_EFFECT_TEST;
 
@@ -299,7 +325,7 @@
     GrTextureAccess             fColorAccess;
     SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
     SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
-    SkScalar fScale;
+    SkVector fScale;
 
     typedef GrEffect INHERITED;
 };
@@ -324,8 +350,8 @@
 
     GrTextureDesc desc;
     desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
-    desc.fWidth = src.width();
-    desc.fHeight = src.height();
+    desc.fWidth = colorBM.width();
+    desc.fHeight = colorBM.height();
     desc.fConfig = kSkia8888_GrPixelConfig;
 
     GrAutoScratchTexture ast(context, desc);
@@ -333,29 +359,43 @@
 
     GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
 
-    GrPaint paint;
-    paint.addColorEffect(
-        GrDisplacementMapEffect::Create(fXChannelSelector,
-                                        fYChannelSelector,
-                                        fScale,
-                                        displacement,
-                                        color))->unref();
+    SkVector scale = SkVector::Make(fScale, fScale);
+    ctm.mapVectors(&scale, 1);
     SkIRect bounds;
-    src.getBounds(&bounds);
+    colorBM.getBounds(&bounds);
+    bounds.offset(colorOffset);
     if (!this->applyCropRect(&bounds, ctm)) {
         return false;
     }
     SkIRect displBounds;
     displacementBM.getBounds(&displBounds);
+    displBounds.offset(displacementOffset);
     if (!this->applyCropRect(&displBounds, ctm)) {
         return false;
     }
     if (!bounds.intersect(displBounds)) {
         return false;
     }
-    SkRect srcRect = SkRect::Make(bounds);
-    SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
-    context->drawRectToRect(paint, dstRect, srcRect);
+
+    GrPaint paint;
+    SkMatrix offsetMatrix = GrEffect::MakeDivByTextureWHMatrix(displacement);
+    offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displacementOffset.fX),
+                              SkIntToScalar(colorOffset.fY - displacementOffset.fY));
+
+    paint.addColorEffect(
+        GrDisplacementMapEffect::Create(fXChannelSelector,
+                                        fYChannelSelector,
+                                        scale,
+                                        displacement,
+                                        offsetMatrix,
+                                        color))->unref();
+    SkIRect colorBounds = bounds;
+    colorBounds.offset(-colorOffset);
+    SkMatrix matrix;
+    matrix.setTranslate(-SkIntToScalar(colorBounds.x()),
+                        -SkIntToScalar(colorBounds.y()));
+    context->concatMatrix(matrix);
+    context->drawRect(paint, SkRect::Make(colorBounds));
     offset->fX = bounds.left();
     offset->fY = bounds.top();
     return SkImageFilterUtils::WrapTexture(dst, bounds.width(), bounds.height(), result);
@@ -366,10 +406,11 @@
 GrDisplacementMapEffect::GrDisplacementMapEffect(
                              SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
                              SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
-                             SkScalar scale,
+                             const SkVector& scale,
                              GrTexture* displacement,
+                             const SkMatrix& offsetMatrix,
                              GrTexture* color)
-    : fDisplacementTransform(kLocal_GrCoordSet, displacement)
+    : fDisplacementTransform(kLocal_GrCoordSet, offsetMatrix, displacement)
     , fDisplacementAccess(displacement)
     , fColorTransform(kLocal_GrCoordSet, color)
     , fColorAccess(color)
@@ -428,10 +469,12 @@
     SkDisplacementMapEffect::ChannelSelectorType yChannelSelector =
         static_cast<SkDisplacementMapEffect::ChannelSelectorType>(
         random->nextRangeU(1, kMaxComponent));
-    SkScalar scale = random->nextRangeScalar(0, 100.0f);
+    SkVector scale = SkVector::Make(random->nextRangeScalar(0, 100.0f),
+                                    random->nextRangeScalar(0, 100.0f));
 
     return GrDisplacementMapEffect::Create(xChannelSelector, yChannelSelector, scale,
-                                           textures[texIdxDispl], textures[texIdxColor]);
+                                           textures[texIdxDispl], SkMatrix::I(),
+                                           textures[texIdxColor]);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -528,8 +571,8 @@
     const GrDisplacementMapEffect& displacementMap =
         drawEffect.castEffect<GrDisplacementMapEffect>();
     GrTexture* colorTex = displacementMap.texture(1);
-    SkScalar scaleX = SkScalarDiv(displacementMap.scale(), SkIntToScalar(colorTex->width()));
-    SkScalar scaleY = SkScalarDiv(displacementMap.scale(), SkIntToScalar(colorTex->height()));
+    SkScalar scaleX = SkScalarDiv(displacementMap.scale().fX, SkIntToScalar(colorTex->width()));
+    SkScalar scaleY = SkScalarDiv(displacementMap.scale().fY, SkIntToScalar(colorTex->height()));
     uman.set2f(fScaleUni, SkScalarToFloat(scaleX),
                colorTex->origin() == kTopLeft_GrSurfaceOrigin ?
                SkScalarToFloat(scaleY) : SkScalarToFloat(-scaleY));
diff --git a/src/effects/SkDropShadowImageFilter.cpp b/src/effects/SkDropShadowImageFilter.cpp
index 60294e4..6109378 100644
--- a/src/effects/SkDropShadowImageFilter.cpp
+++ b/src/effects/SkDropShadowImageFilter.cpp
@@ -77,14 +77,18 @@
     }
     SkCanvas canvas(device.get());
 
-    SkAutoTUnref<SkImageFilter> blurFilter(new SkBlurImageFilter(fSigmaX, fSigmaY));
+    SkVector sigma, localSigma = SkVector::Make(fSigmaX, fSigmaY);
+    matrix.mapVectors(&sigma, &localSigma, 1);
+    SkAutoTUnref<SkImageFilter> blurFilter(new SkBlurImageFilter(sigma.fX, sigma.fY));
     SkAutoTUnref<SkColorFilter> colorFilter(SkColorFilter::CreateModeFilter(fColor, SkXfermode::kSrcIn_Mode));
     SkPaint paint;
     paint.setImageFilter(blurFilter.get());
     paint.setColorFilter(colorFilter.get());
     paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+    SkVector offsetVec, localOffsetVec = SkVector::Make(fDx, fDy);
+    matrix.mapVectors(&offsetVec, &localOffsetVec, 1);
     canvas.translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop));
-    canvas.drawBitmap(src, fDx, fDy, &paint);
+    canvas.drawBitmap(src, offsetVec.fX, offsetVec.fY, &paint);
     canvas.drawBitmap(src, 0, 0);
     *result = device->accessBitmap(false);
     offset->fX = bounds.fLeft;
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index 1f8bca1..09d9f5d 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -28,7 +28,10 @@
                     (fRadius.fHeight >= 0));
 }
 
-SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input, const CropRect* cropRect)
+SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX,
+                                                 int radiusY,
+                                                 SkImageFilter* input,
+                                                 const CropRect* cropRect)
     : INHERITED(input, cropRect), fRadius(SkISize::Make(radiusX, radiusY)) {
 }
 
@@ -194,15 +197,21 @@
         return false;
     }
 
-    int width = radius().width();
-    int height = radius().height();
+    SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
+                                     SkIntToScalar(this->radius().height()));
+    ctm.mapVectors(&radius, 1);
+    int width = SkScalarFloorToInt(radius.fX);
+    int height = SkScalarFloorToInt(radius.fY);
 
     if (width < 0 || height < 0) {
         return false;
     }
 
+    SkIRect srcBounds = bounds;
+    srcBounds.offset(-srcOffset);
+
     if (width == 0 && height == 0) {
-        src.extractSubset(dst, bounds);
+        src.extractSubset(dst, srcBounds);
         offset->fX = bounds.left();
         offset->fY = bounds.top();
         return true;
@@ -214,18 +223,17 @@
         return false;
     }
 
-    offset->fX = bounds.left();
-    offset->fY = bounds.top();
-    bounds.offset(-srcOffset);
     if (width > 0 && height > 0) {
-        erodeX(src, &temp, width, bounds);
-        SkIRect tmpBounds = SkIRect::MakeWH(bounds.width(), bounds.height());
+        erodeX(src, &temp, width, srcBounds);
+        SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
         erodeY(temp, dst, height, tmpBounds);
     } else if (width > 0) {
-        erodeX(src, dst, width, bounds);
+        erodeX(src, dst, width, srcBounds);
     } else if (height > 0) {
-        erodeY(src, dst, height, bounds);
+        erodeY(src, dst, height, srcBounds);
     }
+    offset->fX = bounds.left();
+    offset->fY = bounds.top();
     return true;
 }
 
@@ -259,15 +267,21 @@
         return false;
     }
 
-    int width = radius().width();
-    int height = radius().height();
+    SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
+                                     SkIntToScalar(this->radius().height()));
+    ctm.mapVectors(&radius, 1);
+    int width = SkScalarFloorToInt(radius.fX);
+    int height = SkScalarFloorToInt(radius.fY);
 
     if (width < 0 || height < 0) {
         return false;
     }
 
+    SkIRect srcBounds = bounds;
+    srcBounds.offset(-srcOffset);
+
     if (width == 0 && height == 0) {
-        src.extractSubset(dst, bounds);
+        src.extractSubset(dst, srcBounds);
         offset->fX = bounds.left();
         offset->fY = bounds.top();
         return true;
@@ -279,18 +293,17 @@
         return false;
     }
 
-    offset->fX = bounds.left();
-    offset->fY = bounds.top();
-    bounds.offset(-srcOffset);
     if (width > 0 && height > 0) {
-        dilateX(src, &temp, width, bounds);
-        SkIRect tmpBounds = SkIRect::MakeWH(bounds.width(), bounds.height());
+        dilateX(src, &temp, width, srcBounds);
+        SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
         dilateY(temp, dst, height, tmpBounds);
     } else if (width > 0) {
-        dilateX(src, dst, width, bounds);
+        dilateX(src, dst, width, srcBounds);
     } else if (height > 0) {
-        dilateY(src, dst, height, bounds);
+        dilateY(src, dst, height, srcBounds);
     }
+    offset->fX = bounds.left();
+    offset->fY = bounds.top();
     return true;
 }
 
@@ -569,29 +582,37 @@
 bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
                                          SkBitmap* result, SkIPoint* offset) {
     SkBitmap input;
-    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) {
+    SkIPoint srcOffset = SkIPoint::Make(0, 0);
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, &srcOffset)) {
         return false;
     }
     SkIRect bounds;
-    src.getBounds(&bounds);
+    input.getBounds(&bounds);
+    bounds.offset(srcOffset);
     if (!this->applyCropRect(&bounds, ctm)) {
         return false;
     }
-    int width = radius().width();
-    int height = radius().height();
+    SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
+                                     SkIntToScalar(this->radius().height()));
+    ctm.mapVectors(&radius, 1);
+    int width = SkScalarFloorToInt(radius.fX);
+    int height = SkScalarFloorToInt(radius.fY);
 
     if (width < 0 || height < 0) {
         return false;
     }
 
+    SkIRect srcBounds = bounds;
+    srcBounds.offset(-srcOffset);
     if (width == 0 && height == 0) {
-        src.extractSubset(result, bounds);
+        input.extractSubset(result, srcBounds);
         offset->fX = bounds.left();
         offset->fY = bounds.top();
         return true;
     }
 
-    if (!apply_morphology(input, bounds, GrMorphologyEffect::kDilate_MorphologyType, radius(), result)) {
+    if (!apply_morphology(input, srcBounds, GrMorphologyEffect::kDilate_MorphologyType,
+                          SkISize::Make(width, height), result)) {
         return false;
     }
     offset->fX = bounds.left();
@@ -602,29 +623,38 @@
 bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
                                         SkBitmap* result, SkIPoint* offset) {
     SkBitmap input;
-    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) {
+    SkIPoint srcOffset = SkIPoint::Make(0, 0);
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, &srcOffset)) {
         return false;
     }
     SkIRect bounds;
-    src.getBounds(&bounds);
+    input.getBounds(&bounds);
+    bounds.offset(srcOffset);
     if (!this->applyCropRect(&bounds, ctm)) {
         return false;
     }
-    int width = radius().width();
-    int height = radius().height();
+    SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
+                                     SkIntToScalar(this->radius().height()));
+    ctm.mapVectors(&radius, 1);
+    int width = SkScalarFloorToInt(radius.fX);
+    int height = SkScalarFloorToInt(radius.fY);
 
     if (width < 0 || height < 0) {
         return false;
     }
 
+    SkIRect srcBounds = bounds;
+    srcBounds.offset(-srcOffset);
+
     if (width == 0 && height == 0) {
-        src.extractSubset(result, bounds);
+        input.extractSubset(result, srcBounds);
         offset->fX = bounds.left();
         offset->fY = bounds.top();
         return true;
     }
 
-    if (!apply_morphology(input, bounds, GrMorphologyEffect::kErode_MorphologyType, radius(), result)) {
+    if (!apply_morphology(input, srcBounds, GrMorphologyEffect::kErode_MorphologyType,
+        SkISize::Make(width, height), result)) {
         return false;
     }
     offset->fX = bounds.left();