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
This commit is contained in:
senorblanco@chromium.org 2014-03-24 20:50:59 +00:00
parent 7b78981b4c
commit 09843fd5c1
3 changed files with 50 additions and 3 deletions

View File

@ -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 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
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 @@ bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const
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);

View File

@ -28,11 +28,15 @@ static void scale_rect(SkRect* rect, float xScale, float yScale) {
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 @@ GrTexture* GaussianBlur(GrContext* context,
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);

View File

@ -295,6 +295,29 @@ DEF_TEST(ImageFilterMatrixTest, reporter) {
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 @@ DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) {
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