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