Implement offset for GPU filter path. Although we can't yet use this in Blink for FEOffset, due to SVG semantics, we can use it to support crop rect (upcoming patch).

This patch adds the parameter to the filterImageGPU() signature, plumbs through the code on the GPU side, and implements support for it in SkXfermodeImageFilter for both raster and GPU.

Of the remaining filters with GPU implementations, Blur, Morphology, Bicubic and Displacement work fine; they're commutative wrt offset and can simply pass it up the chain. Blend is not, but will be removed shortly anyway (has been replaced with SkXfermodeImageFilter in Blink).

R=reed@google.com, bsalomon@google.com

Author: senorblanco@chromium.org

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

git-svn-id: http://skia.googlecode.com/svn/trunk@9977 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index 9f16b4a..33caf75 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -98,11 +98,12 @@
     return this->asNewEffect(NULL, NULL);
 }
 
-bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result,
+                                   SkIPoint* offset) {
 #if SK_SUPPORT_GPU
     SkBitmap input;
     SkASSERT(fInputCount == 1);
-    if (!SkImageFilterUtils::GetInputResultGPU(this->getInput(0), proxy, src, &input)) {
+    if (!SkImageFilterUtils::GetInputResultGPU(this->getInput(0), proxy, src, &input, offset)) {
         return false;
     }
     GrTexture* srcTexture = input.getTexture();
diff --git a/src/core/SkImageFilterUtils.cpp b/src/core/SkImageFilterUtils.cpp
index d6e109c..c88d5fe 100644
--- a/src/core/SkImageFilterUtils.cpp
+++ b/src/core/SkImageFilterUtils.cpp
@@ -20,15 +20,16 @@
     return true;
 }
 
-bool SkImageFilterUtils::GetInputResultGPU(SkImageFilter* filter, SkImageFilter::Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+bool SkImageFilterUtils::GetInputResultGPU(SkImageFilter* filter, SkImageFilter::Proxy* proxy,
+                                           const SkBitmap& src, SkBitmap* result,
+                                           SkIPoint* offset) {
     if (!filter) {
         *result = src;
         return true;
     } else if (filter->canFilterImageGPU()) {
-        return filter->filterImageGPU(proxy, src, result);
+        return filter->filterImageGPU(proxy, src, result, offset);
     } else {
-        SkIPoint offset;
-        if (filter->filterImage(proxy, src, SkMatrix(), result, &offset)) {
+        if (filter->filterImage(proxy, src, SkMatrix(), result, offset)) {
             if (!result->getTexture()) {
                 GrContext* context = ((GrTexture *) src.getTexture())->getContext();
                 GrTexture* resultTex = GrLockAndRefCachedBitmapTexture(context,
diff --git a/src/effects/SkBicubicImageFilter.cpp b/src/effects/SkBicubicImageFilter.cpp
index 2d0a17a..a97a768 100644
--- a/src/effects/SkBicubicImageFilter.cpp
+++ b/src/effects/SkBicubicImageFilter.cpp
@@ -341,9 +341,9 @@
     return GrBicubicEffect::Create(textures[texIdx], coefficients);
 }
 
-bool SkBicubicImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+bool SkBicubicImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result, SkIPoint* offset) {
     SkBitmap srcBM;
-    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &srcBM)) {
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &srcBM, offset)) {
         return false;
     }
     GrTexture* srcTexture = srcBM.getTexture();
diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp
index 4f61ed8..7b98644 100644
--- a/src/effects/SkBlendImageFilter.cpp
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -68,10 +68,13 @@
     SkImageFilter* backgroundInput = getBackgroundInput();
     SkImageFilter* foregroundInput = getForegroundInput();
     SkASSERT(NULL != backgroundInput);
-    if (!backgroundInput->filterImage(proxy, src, ctm, &background, offset)) {
+    SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
+    if (!backgroundInput->filterImage(proxy, src, ctm, &background, &backgroundOffset)) {
         return false;
     }
-    if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, offset)) {
+    SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
+    if (foregroundInput &&
+        !foregroundInput->filterImage(proxy, src, ctm, &foreground, &foregroundOffset)) {
         return false;
     }
     SkAutoLockPixels alp_foreground(foreground), alp_background(background);
@@ -83,9 +86,9 @@
     SkCanvas canvas(*dst);
     SkPaint paint;
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-    canvas.drawBitmap(background, 0, 0, &paint);
+    canvas.drawBitmap(background, backgroundOffset.fX, backgroundOffset.fY, &paint);
     paint.setXfermodeMode(modeToXfermode(fMode));
-    canvas.drawBitmap(foreground, 0, 0, &paint);
+    canvas.drawBitmap(foreground, foregroundOffset.fX, foregroundOffset.fY, &paint);
     return true;
 }
 
@@ -124,8 +127,11 @@
 public:
     static GrEffectRef* Create(SkBlendImageFilter::Mode mode,
                                GrTexture* foreground,
-                               GrTexture* background) {
-        AutoEffectUnref effect(SkNEW_ARGS(GrBlendEffect, (mode, foreground, background)));
+                               const SkIPoint& foregroundOffset,
+                               GrTexture* background,
+                               const SkIPoint& backgroundOffset) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrBlendEffect, (mode, foreground, foregroundOffset,
+                                                          background, backgroundOffset)));
         return CreateEffectRef(effect);
     }
 
@@ -133,6 +139,8 @@
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
     SkBlendImageFilter::Mode mode() const { return fMode; }
+    const SkMatrix& foregroundMatrix() const { return fForegroundMatrix; }
+    const SkMatrix& backgroundMatrix() const { return fBackgroundMatrix; }
 
     typedef GrGLBlendEffect GLEffect;
     static const char* Name() { return "Blend"; }
@@ -142,22 +150,31 @@
 private:
     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
 
-    GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground, GrTexture* background);
+    GrBlendEffect(SkBlendImageFilter::Mode mode,
+                  GrTexture* foreground, const SkIPoint& foregroundOffset,
+                  GrTexture* background, const SkIPoint& backgroundOffset);
     GrTextureAccess             fForegroundAccess;
+    SkMatrix                    fForegroundMatrix;
     GrTextureAccess             fBackgroundAccess;
+    SkMatrix                    fBackgroundMatrix;
     SkBlendImageFilter::Mode    fMode;
 
     typedef GrEffect INHERITED;
 };
 
-bool SkBlendImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+bool SkBlendImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result,
+                                        SkIPoint* offset) {
     SkBitmap backgroundBM;
-    if (!SkImageFilterUtils::GetInputResultGPU(getBackgroundInput(), proxy, src, &backgroundBM)) {
+    SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
+    if (!SkImageFilterUtils::GetInputResultGPU(getBackgroundInput(), proxy, src, &backgroundBM,
+                                               &backgroundOffset)) {
         return false;
     }
     GrTexture* background = backgroundBM.getTexture();
     SkBitmap foregroundBM;
-    if (!SkImageFilterUtils::GetInputResultGPU(getForegroundInput(), proxy, src, &foregroundBM)) {
+    SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
+    if (!SkImageFilterUtils::GetInputResultGPU(getForegroundInput(), proxy, src, &foregroundBM,
+                                               &foregroundOffset)) {
         return false;
     }
     GrTexture* foreground = foregroundBM.getTexture();
@@ -176,7 +193,7 @@
 
     GrPaint paint;
     paint.colorStage(0)->setEffect(
-        GrBlendEffect::Create(fMode, foreground, background))->unref();
+        GrBlendEffect::Create(fMode, foreground, foregroundOffset, background, backgroundOffset))->unref();
     SkRect srcRect;
     src.getBounds(&srcRect);
     context->drawRect(paint, srcRect);
@@ -187,12 +204,20 @@
 
 GrBlendEffect::GrBlendEffect(SkBlendImageFilter::Mode mode,
                              GrTexture* foreground,
-                             GrTexture* background)
+                             const SkIPoint& foregroundOffset,
+                             GrTexture* background,
+                             const SkIPoint& backgroundOffset)
     : fForegroundAccess(foreground)
     , fBackgroundAccess(background)
     , fMode(mode) {
     this->addTextureAccess(&fForegroundAccess);
     this->addTextureAccess(&fBackgroundAccess);
+    fForegroundMatrix = GrEffect::MakeDivByTextureWHMatrix(foreground);
+    fForegroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX),
+                                   SkIntToScalar(-foregroundOffset.fY));
+    fBackgroundMatrix = GrEffect::MakeDivByTextureWHMatrix(background);
+    fBackgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX),
+                                   SkIntToScalar(-backgroundOffset.fY));
 }
 
 GrBlendEffect::~GrBlendEffect() {
@@ -288,11 +313,11 @@
     GrTexture* fgTex = blend.texture(0);
     GrTexture* bgTex = blend.texture(1);
     fForegroundEffectMatrix.setData(uman,
-                                    GrEffect::MakeDivByTextureWHMatrix(fgTex),
+                                    blend.foregroundMatrix(),
                                     drawEffect,
                                     fgTex);
     fBackgroundEffectMatrix.setData(uman,
-                                    GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                    blend.backgroundMatrix(),
                                     drawEffect,
                                     bgTex);
 
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index 36d0afd..e603244 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -119,7 +119,8 @@
     }
 }
 
-static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, int *highOffset)
+static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset,
+                          int *highOffset)
 {
     float pi = SkScalarToFloat(SK_ScalarPI);
     int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f));
@@ -192,10 +193,11 @@
     return true;
 }
 
-bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result,
+                                       SkIPoint* offset) {
 #if SK_SUPPORT_GPU
     SkBitmap input;
-    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &input)) {
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &input, offset)) {
         return false;
     }
     GrTexture* source = input.getTexture();
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index 7c99c02..8d287b2 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -275,14 +275,19 @@
     typedef GrEffect INHERITED;
 };
 
-bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result,
+                                             SkIPoint* offset) {
     SkBitmap colorBM;
-    if (!SkImageFilterUtils::GetInputResultGPU(getColorInput(), proxy, src, &colorBM)) {
+    SkIPoint colorOffset = SkIPoint::Make(0, 0);
+    if (!SkImageFilterUtils::GetInputResultGPU(getColorInput(), proxy, src, &colorBM,
+                                               &colorOffset)) {
         return false;
     }
     GrTexture* color = colorBM.getTexture();
     SkBitmap displacementBM;
-    if (!SkImageFilterUtils::GetInputResultGPU(getDisplacementInput(), proxy, src, &displacementBM)) {
+    SkIPoint displacementOffset = SkIPoint::Make(0, 0);
+    if (!SkImageFilterUtils::GetInputResultGPU(getDisplacementInput(), proxy, src, &displacementBM,
+                                               &displacementOffset)) {
         return false;
     }
     GrTexture* displacement = displacementBM.getTexture();
@@ -308,7 +313,9 @@
                                         color))->unref();
     SkRect srcRect;
     src.getBounds(&srcRect);
-    context->drawRect(paint, srcRect);
+    SkRect dstRect = srcRect;
+    dstRect.offset(SkIntToScalar(colorOffset.fX), SkIntToScalar(colorOffset.fY));
+    context->drawRectToRect(paint, srcRect, dstRect);
     return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result);
 }
 
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index cbee850..cb17c90 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -502,9 +502,10 @@
 
 };
 
-bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result,
+                                         SkIPoint* offset) {
     SkBitmap inputBM;
-    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &inputBM)) {
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &inputBM, offset)) {
         return false;
     }
     GrTexture* input = inputBM.getTexture();
@@ -515,9 +516,10 @@
     return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result);
 }
 
-bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result,
+                                        SkIPoint* offset) {
     SkBitmap inputBM;
-    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &inputBM)) {
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &inputBM, offset)) {
         return false;
     }
     GrTexture* input = inputBM.getTexture();
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
index c26ca18..a318ceb 100644
--- a/src/effects/SkXfermodeImageFilter.cpp
+++ b/src/effects/SkXfermodeImageFilter.cpp
@@ -19,7 +19,9 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkXfermodeImageFilter::SkXfermodeImageFilter(SkXfermode* mode, SkImageFilter* background, SkImageFilter* foreground)
+SkXfermodeImageFilter::SkXfermodeImageFilter(SkXfermode* mode,
+                                             SkImageFilter* background,
+                                             SkImageFilter* foreground)
   : INHERITED(background, foreground), fMode(mode) {
     SkSafeRef(fMode);
 }
@@ -46,10 +48,14 @@
     SkBitmap background = src, foreground = src;
     SkImageFilter* backgroundInput = getInput(0);
     SkImageFilter* foregroundInput = getInput(1);
-    if (backgroundInput && !backgroundInput->filterImage(proxy, src, ctm, &background, offset)) {
+    SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
+    if (backgroundInput &&
+        !backgroundInput->filterImage(proxy, src, ctm, &background, &backgroundOffset)) {
         return false;
     }
-    if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, offset)) {
+    SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
+    if (foregroundInput &&
+        !foregroundInput->filterImage(proxy, src, ctm, &foreground, &foregroundOffset)) {
         return false;
     }
     dst->setConfig(background.config(), background.width(), background.height());
@@ -59,20 +65,32 @@
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     canvas.drawBitmap(background, 0, 0, &paint);
     paint.setXfermode(fMode);
-    canvas.drawBitmap(foreground, 0, 0, &paint);
+    canvas.drawBitmap(foreground,
+                      SkIntToScalar(foregroundOffset.fX - backgroundOffset.fX),
+                      SkIntToScalar(foregroundOffset.fY - backgroundOffset.fY),
+                      &paint);
+    offset->fX += backgroundOffset.fX;
+    offset->fY += backgroundOffset.fY;
     return true;
 }
 
 #if SK_SUPPORT_GPU
 
-bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy,
+                                           const SkBitmap& src,
+                                           SkBitmap* result,
+                                           SkIPoint* offset) {
     SkBitmap background;
-    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &background)) {
+    SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &background,
+                                               &backgroundOffset)) {
         return false;
     }
     GrTexture* backgroundTex = background.getTexture();
     SkBitmap foreground;
-    if (!SkImageFilterUtils::GetInputResultGPU(getInput(1), proxy, src, &foreground)) {
+    SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(1), proxy, src, &foreground,
+                                               &foregroundOffset)) {
         return false;
     }
     GrTexture* foregroundTex = foreground.getTexture();
@@ -96,23 +114,31 @@
         return false;
     }
 
+    SkMatrix foregroundMatrix = GrEffect::MakeDivByTextureWHMatrix(foregroundTex);
+    foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX),
+                                  SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY));
+
+
     GrPaint paint;
     SkRect srcRect;
     src.getBounds(&srcRect);
     if (NULL != xferEffect) {
         paint.colorStage(0)->setEffect(
-            GrSimpleTextureEffect::Create(foregroundTex, GrEffect::MakeDivByTextureWHMatrix(foregroundTex)))->unref();
+            GrSimpleTextureEffect::Create(foregroundTex, foregroundMatrix))->unref();
         paint.colorStage(1)->setEffect(xferEffect)->unref();
         context->drawRect(paint, srcRect);
     } else {
+        SkMatrix backgroundMatrix = GrEffect::MakeDivByTextureWHMatrix(backgroundTex);
         paint.colorStage(0)->setEffect(
-            GrSimpleTextureEffect::Create(backgroundTex, GrEffect::MakeDivByTextureWHMatrix(backgroundTex)))->unref();
+            GrSimpleTextureEffect::Create(backgroundTex, backgroundMatrix))->unref();
         context->drawRect(paint, srcRect);
         paint.setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
         paint.colorStage(0)->setEffect(
-            GrSimpleTextureEffect::Create(foregroundTex, GrEffect::MakeDivByTextureWHMatrix(foregroundTex)))->unref();
+            GrSimpleTextureEffect::Create(foregroundTex, foregroundMatrix))->unref();
         context->drawRect(paint, srcRect);
     }
+    offset->fX += backgroundOffset.fX;
+    offset->fY += backgroundOffset.fY;
     return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result);
 }
 
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 766158f..d9de0ca 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1377,7 +1377,7 @@
 
 static bool filter_texture(SkDevice* device, GrContext* context,
                            GrTexture* texture, SkImageFilter* filter,
-                           int w, int h, SkBitmap* result) {
+                           int w, int h, SkBitmap* result, SkIPoint* offset) {
     GrAssert(filter);
     SkDeviceImageFilterProxy proxy(device);
 
@@ -1385,7 +1385,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), result);
+        return filter->filterImageGPU(&proxy, wrap_texture(texture), result, offset);
     } else {
         return false;
     }
@@ -1419,9 +1419,10 @@
         GrSimpleTextureEffect::Create(texture, SkMatrix::I()))->unref();
 
     SkImageFilter* filter = paint.getImageFilter();
+    SkIPoint offset = SkIPoint::Make(0, 0);
     if (NULL != filter) {
         SkBitmap filterBitmap;
-        if (filter_texture(this, fContext, texture, filter, w, h, &filterBitmap)) {
+        if (filter_texture(this, fContext, texture, filter, w, h, &filterBitmap, &offset)) {
             grPaint.colorStage(kBitmapEffectIdx)->setEffect(
                 GrSimpleTextureEffect::Create(filterBitmap.getTexture(), SkMatrix::I()))->unref();
             texture = filterBitmap.getTexture();
@@ -1435,8 +1436,10 @@
                                             SkIntToScalar(top),
                                             SkIntToScalar(w),
                                             SkIntToScalar(h)),
-                            GrRect::MakeWH(SK_Scalar1 * w / texture->width(),
-                                        SK_Scalar1 * h / texture->height()));
+                            GrRect::MakeXYWH(offset.fX,
+                                             offset.fY,
+                                             SK_Scalar1 * w / texture->width(),
+                                             SK_Scalar1 * h / texture->height()));
 }
 
 void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
@@ -1498,12 +1501,15 @@
     SkImageFilter* filter = paint.getImageFilter();
     if (NULL != filter) {
         SkBitmap filterBitmap;
-        if (filter_texture(this, fContext, devTex, filter, w, h, &filterBitmap)) {
+        SkIPoint offset = SkIPoint::Make(0, 0);
+        if (filter_texture(this, fContext, devTex, filter, w, h, &filterBitmap, &offset)) {
             grPaint.colorStage(kBitmapEffectIdx)->setEffect(
                 GrSimpleTextureEffect::Create(filterBitmap.getTexture(), SkMatrix::I()))->unref();
             devTex = filterBitmap.getTexture();
             w = filterBitmap.width();
             h = filterBitmap.height();
+            x += offset.fX;
+            y += offset.fY;
         }
     }
 
@@ -1542,7 +1548,7 @@
     // must be pushed upstack.
     SkAutoCachedTexture act(this, src, NULL, &texture);
 
-    return filter_texture(this, fContext, texture, filter, src.width(), src.height(), result);
+    return filter_texture(this, fContext, texture, filter, src.width(), src.height(), result, offset);
 }
 
 ///////////////////////////////////////////////////////////////////////////////