Fix for crash on large image blur sigma values.

This was crashing on the GPU path, due to a failed texture allocation.
The belt-and-suspenders fix is to:

1) Limit the GPU path to only allocate up to maxTextureSize.
2) Limit both the raster and GPU paths to reasonable blur sizes (box blur
   kernel size of 1000, resulting in a sigma limit of 532).

R=bsalomon@google.com
BUG=skia:

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13923 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index ffebe54..363691a 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -16,6 +16,13 @@
 #include "GrContext.h"
 #endif
 
+// This rather arbitrary-looking value results in a maximum box blur kernel size
+// of 1000 pixels on the raster path, which matches the WebKit and Firefox
+// implementations. Since the GPU path does not compute a box blur, putting
+// the limit on sigma ensures consistent behaviour between the GPU and
+// raster paths.
+#define MAX_SIGMA SkIntToScalar(532)
+
 SkBlurImageFilter::SkBlurImageFilter(SkReadBuffer& buffer)
   : INHERITED(1, buffer) {
     fSigma.fWidth = buffer.readScalar();
@@ -164,6 +171,8 @@
 
     SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
     ctx.ctm().mapVectors(&sigma, &localSigma, 1);
+    sigma.fX = SkMinScalar(sigma.fX, MAX_SIGMA);
+    sigma.fY = SkMinScalar(sigma.fY, MAX_SIGMA);
 
     int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
     int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
@@ -263,6 +272,8 @@
     GrTexture* source = input.getTexture();
     SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
     ctx.ctm().mapVectors(&sigma, &localSigma, 1);
+    sigma.fX = SkMinScalar(sigma.fX, MAX_SIGMA);
+    sigma.fY = SkMinScalar(sigma.fY, MAX_SIGMA);
     offset->fX = rect.fLeft;
     offset->fY = rect.fTop;
     rect.offset(-srcOffset);
diff --git a/src/effects/SkGpuBlurUtils.cpp b/src/effects/SkGpuBlurUtils.cpp
index 6133db1..9da08e6 100644
--- a/src/effects/SkGpuBlurUtils.cpp
+++ b/src/effects/SkGpuBlurUtils.cpp
@@ -28,11 +28,15 @@
     rect->fBottom = SkScalarMul(rect->fBottom, yScale);
 }
 
-static float adjust_sigma(float sigma, int *scaleFactor, int *radius) {
+static float adjust_sigma(float sigma, int maxTextureSize, int *scaleFactor, int *radius) {
     *scaleFactor = 1;
     while (sigma > MAX_BLUR_SIGMA) {
         *scaleFactor *= 2;
         sigma *= 0.5f;
+        if (*scaleFactor > maxTextureSize) {
+            *scaleFactor = maxTextureSize;
+            sigma = MAX_BLUR_SIGMA;
+        }
     }
     *radius = static_cast<int>(ceilf(sigma * 3.0f));
     SkASSERT(*radius <= GrConvolutionEffect::kMaxKernelRadius);
@@ -129,8 +133,9 @@
     SkIRect clearRect;
     int scaleFactorX, radiusX;
     int scaleFactorY, radiusY;
-    sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX);
-    sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY);
+    int maxTextureSize = context->getMaxTextureSize();
+    sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX);
+    sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY);
 
     SkRect srcRect(rect);
     scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index 97d3d0f..993a1e2 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -295,6 +295,29 @@
     canvas.drawPicture(picture);
 }
 
+void test_huge_blur(SkBaseDevice* device, skiatest::Reporter* reporter) {
+    SkCanvas canvas(device);
+
+    SkBitmap bitmap;
+    bitmap.allocN32Pixels(100, 100);
+    bitmap.eraseARGB(0, 0, 0, 0);
+
+    // Check that a blur with an insane radius does not crash or assert.
+    SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(SkIntToScalar(1<<30), SkIntToScalar(1<<30)));
+
+    SkPaint paint;
+    paint.setImageFilter(blur);
+    canvas.drawSprite(bitmap, 0, 0, &paint);
+}
+
+DEF_TEST(HugeBlurImageFilter, reporter) {
+    SkBitmap temp;
+    temp.allocN32Pixels(100, 100);
+    SkBitmapDevice device(temp);
+    test_huge_blur(&device, reporter);
+}
+
+
 #if SK_SUPPORT_GPU
 DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) {
     GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
@@ -303,4 +326,12 @@
                                                          0));
     test_crop_rects(device, reporter);
 }
+
+DEF_GPUTEST(HugeBlurImageFilterGPU, reporter, factory) {
+    GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
+    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
+                                                         SkImageInfo::MakeN32Premul(100, 100),
+                                                         0));
+    test_huge_blur(device, reporter);
+}
 #endif