This patch implements a crop rect for SkImageFilter. It has been implemented for SkColorFilterImageFilter and SkBlurImageFilter as examples.

In order to preserve the immutability of SkImageFilters, the crop rect is passed as a constructor parameter. If NULL (the default), the bounds of the input image are used, as before.

This also tightens up the boundary handling for SkImageBlurFilter on the GPU backend. Where we were previously using clamping semantics, we now respect decal semantics (so we don't oversaturate the edges). This brings the GPU and raster backends into closer alignment, but will require some new baselines for the GPU tests.

At a minimum, the following tests will need new baselines: imageblur, imagefiltersbase, imagefilterscropped, spritebitmap.

R=reed@google.com

Committed: https://code.google.com/p/skia/source/detail?r=10251

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

git-svn-id: http://skia.googlecode.com/svn/trunk@10338 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkGpuBlurUtils.cpp b/src/effects/SkGpuBlurUtils.cpp
index 4796488..260b2e2 100644
--- a/src/effects/SkGpuBlurUtils.cpp
+++ b/src/effects/SkGpuBlurUtils.cpp
@@ -11,6 +11,7 @@
 
 #if SK_SUPPORT_GPU
 #include "effects/GrConvolutionEffect.h"
+#include "effects/GrTextureDomainEffect.h"
 #include "GrContext.h"
 #endif
 
@@ -40,24 +41,40 @@
 
 static void convolve_gaussian(GrContext* context,
                               GrTexture* texture,
-                              const SkRect& rect,
+                              const SkRect& srcRect,
+                              const SkRect& dstRect,
+                              bool cropToSrcRect,
                               float sigma,
                               int radius,
                               Gr1DKernelEffect::Direction direction) {
     GrPaint paint;
+    paint.reset();
+    float cropRect[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
+    if (cropToSrcRect) {
+        if (direction == Gr1DKernelEffect::kX_Direction) {
+            cropRect[0] = SkScalarToFloat(srcRect.left()) / texture->width();
+            cropRect[1] = SkScalarToFloat(srcRect.right()) / texture->width();
+        } else {
+            cropRect[2] = SkScalarToFloat(srcRect.top()) / texture->height();
+            cropRect[3] = SkScalarToFloat(srcRect.bottom()) / texture->height();
+        }
+    }
 
     SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian(texture,
                                                                        direction,
                                                                        radius,
-                                                                       sigma));
+                                                                       sigma,
+                                                                       cropToSrcRect,
+                                                                       cropRect));
     paint.addColorEffect(conv);
-    context->drawRect(paint, rect);
+    context->drawRectToRect(paint, dstRect, srcRect);
 }
 
 GrTexture* GaussianBlur(GrContext* context,
                         GrTexture* srcTexture,
                         bool canClobberSrc,
                         const SkRect& rect,
+                        bool cropToRect,
                         float sigmaX,
                         float sigmaY) {
     GrAssert(NULL != context);
@@ -79,7 +96,7 @@
     scale_rect(&srcRect, static_cast<float>(scaleFactorX),
                          static_cast<float>(scaleFactorY));
 
-    GrContext::AutoClip acs(context, srcRect);
+    GrContext::AutoClip acs(context, SkRect::MakeWH(srcRect.width(), srcRect.height()));
 
     GrAssert(kBGRA_8888_GrPixelConfig == srcTexture->config() ||
              kRGBA_8888_GrPixelConfig == srcTexture->config() ||
@@ -104,10 +121,25 @@
         matrix.setIDiv(srcTexture->width(), srcTexture->height());
         context->setRenderTarget(dstTexture->asRenderTarget());
         SkRect dstRect(srcRect);
+        if (cropToRect && i == 1) {
+            dstRect.offset(-dstRect.fLeft, -dstRect.fTop);
+            SkRect domain;
+            matrix.mapRect(&domain, rect);
+            domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f,
+                         i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f);
+            SkAutoTUnref<GrEffectRef> effect(GrTextureDomainEffect::Create(
+                srcTexture,
+                matrix,
+                domain,
+                GrTextureDomainEffect::kDecal_WrapMode,
+                true));
+            paint.addColorEffect(effect);
+        } else {
+            GrTextureParams params(SkShader::kClamp_TileMode, true);
+            paint.addColorTextureEffect(srcTexture, matrix, params);
+        }
         scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
                              i < scaleFactorY ? 0.5f : 1.0f);
-        GrTextureParams params(SkShader::kClamp_TileMode, true);
-        paint.addColorTextureEffect(srcTexture, matrix, params);
         context->drawRectToRect(paint, dstRect, srcRect);
         srcRect = dstRect;
         srcTexture = dstTexture;
@@ -126,9 +158,11 @@
             context->clear(&clearRect, 0x0);
         }
         context->setRenderTarget(dstTexture->asRenderTarget());
-        convolve_gaussian(context, srcTexture, srcRect, sigmaX, radiusX,
-                          Gr1DKernelEffect::kX_Direction);
+        SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
+        convolve_gaussian(context, srcTexture, srcRect, dstRect, cropToRect,
+                          sigmaX, radiusX, Gr1DKernelEffect::kX_Direction);
         srcTexture = dstTexture;
+        srcRect = dstRect;
         SkTSwap(dstTexture, tempTexture);
     }
 
@@ -142,9 +176,11 @@
         }
 
         context->setRenderTarget(dstTexture->asRenderTarget());
-        convolve_gaussian(context, srcTexture, srcRect, sigmaY, radiusY,
-                          Gr1DKernelEffect::kY_Direction);
+        SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
+        convolve_gaussian(context, srcTexture, srcRect, dstRect, cropToRect,
+                          sigmaY, radiusY, Gr1DKernelEffect::kY_Direction);
         srcTexture = dstTexture;
+        srcRect = dstRect;
         SkTSwap(dstTexture, tempTexture);
     }