add more checks for computing clamp counts, remove dead code

BUG=448299

Review URL: https://codereview.chromium.org/886473003
This commit is contained in:
reed 2015-01-28 11:44:48 -08:00 committed by Commit bot
parent 3bd12efdcf
commit 9d91eb3136
4 changed files with 74 additions and 10 deletions

View File

@ -1,4 +1,3 @@
/* /*
* Copyright 2011 Google Inc. * Copyright 2011 Google Inc.
* *
@ -6,8 +5,33 @@
* found in the LICENSE file. * found in the LICENSE file.
*/ */
#include "SkClampRange.h" #include "SkClampRange.h"
#include "SkMath.h"
static int SkCLZ64(uint64_t value) {
int count = 0;
if (value >> 32) {
value >>= 32;
} else {
count += 32;
}
return count + SkCLZ(SkToU32(value));
}
static bool sk_64_smul_check(int64_t a, int64_t b, int64_t* result) {
// Do it the slow way until we have some assembly.
int64_t ua = SkTAbs(a);
int64_t ub = SkTAbs(b);
int zeros = SkCLZ64(ua) + SkCLZ64(ub);
// this is a conservative check: it may return false when in fact it would not have overflowed.
// Hackers Delight uses 34 as its convervative check, but that is for 32x32 multiplies.
// Since we are looking at 64x64 muls, we add 32 to the check.
if (zeros < (32 + 34)) {
return false;
}
*result = a * b;
return true;
}
/* /*
* returns [0..count] for the number of steps (<= count) for which x0 <= edge * returns [0..count] for the number of steps (<= count) for which x0 <= edge
@ -58,6 +82,14 @@ void SkClampRange::init(SkGradFixed fx0, SkGradFixed dx0, int count, int v0, int
int64_t dx = dx0; int64_t dx = dx0;
// start with ex equal to the last computed value // start with ex equal to the last computed value
int64_t count_times_dx;
if (!sk_64_smul_check(count - 1, dx, &count_times_dx)) {
// we can't represent the computed end in 32.32, so just draw something (first color)
fCount1 = fCount2 = 0;
fCount0 = count;
return;
}
int64_t ex = fx + (count - 1) * dx; int64_t ex = fx + (count - 1) * dx;
if ((uint64_t)(fx | ex) <= kFracMax_SkGradFixed) { if ((uint64_t)(fx | ex) <= kFracMax_SkGradFixed) {
@ -77,8 +109,6 @@ void SkClampRange::init(SkGradFixed fx0, SkGradFixed dx0, int count, int v0, int
return; return;
} }
int extraCount = 0;
// now make ex be 1 past the last computed value // now make ex be 1 past the last computed value
ex += dx; ex += dx;
@ -93,11 +123,15 @@ void SkClampRange::init(SkGradFixed fx0, SkGradFixed dx0, int count, int v0, int
fCount0 = chop(fx, 0, ex, dx, count); fCount0 = chop(fx, 0, ex, dx, count);
SkASSERT(fCount0 >= 0);
SkASSERT(fCount0 <= count);
count -= fCount0; count -= fCount0;
fx += fCount0 * dx; fx += fCount0 * dx;
SkASSERT(fx >= 0); SkASSERT(fx >= 0);
SkASSERT(fCount0 == 0 || (fx - dx) < 0); SkASSERT(fCount0 == 0 || (fx - dx) < 0);
fCount1 = chop(fx, kFracMax_SkGradFixed, ex, dx, count); fCount1 = chop(fx, kFracMax_SkGradFixed, ex, dx, count);
SkASSERT(fCount1 >= 0);
SkASSERT(fCount1 <= count);
count -= fCount1; count -= fCount1;
fCount2 = count; fCount2 = count;
@ -121,10 +155,4 @@ void SkClampRange::init(SkGradFixed fx0, SkGradFixed dx0, int count, int v0, int
if (fCount1 > 0) { if (fCount1 > 0) {
fFx1 = fx0 + fCount0 * dx; fFx1 = fx0 + fCount0 * dx;
} }
if (dx > 0) {
fCount2 += extraCount;
} else {
fCount0 += extraCount;
}
} }

View File

@ -35,6 +35,15 @@ struct SkClampRange {
void init(SkGradFixed fx, SkGradFixed dx, int count, int v0, int v1); void init(SkGradFixed fx, SkGradFixed dx, int count, int v0, int v1);
void validate(int count) const {
#ifdef SK_DEBUG
SkASSERT(fCount0 >= 0);
SkASSERT(fCount1 >= 0);
SkASSERT(fCount2 >= 0);
SkASSERT(fCount0 + fCount1 + fCount2 == count);
#endif
}
private: private:
void initFor1(SkGradFixed fx); void initFor1(SkGradFixed fx);
}; };

View File

@ -149,6 +149,7 @@ void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
int toggle, int count) { int toggle, int count) {
SkClampRange range; SkClampRange range;
range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
range.validate(count);
if ((count = range.fCount0) > 0) { if ((count = range.fCount0) > 0) {
sk_memset32_dither(dstC, sk_memset32_dither(dstC,
@ -332,6 +333,7 @@ void shadeSpan16_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
int toggle, int count) { int toggle, int count) {
SkClampRange range; SkClampRange range;
range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
range.validate(count);
if ((count = range.fCount0) > 0) { if ((count = range.fCount0) > 0) {
dither_memset16(dstC, dither_memset16(dstC,

View File

@ -12,6 +12,30 @@
#include "SkTemplates.h" #include "SkTemplates.h"
#include "Test.h" #include "Test.h"
// https://code.google.com/p/chromium/issues/detail?id=448299
// Giant (inverse) matrix causes overflow when converting/computing using 32.32
// Before the fix, we would assert (and then crash).
static void test_big_grad(skiatest::Reporter* reporter) {
const SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
const SkPoint pts[] = {{ 15, 14.7112684f }, { 0.709064007f, 12.6108112f }};
SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
SkPaint paint;
paint.setShader(s)->unref();
SkBitmap bm;
bm.allocN32Pixels(2000, 1);
SkCanvas c(bm);
const SkScalar affine[] = {
1.06608627e-06f, 4.26434525e-07f, 6.2855f, 2.6611f, 273.4393f, 244.0046f
};
SkMatrix matrix;
matrix.setAffine(affine);
c.concat(matrix);
c.drawPaint(paint);
}
struct GradRec { struct GradRec {
int fColorCount; int fColorCount;
const SkColor* fColors; const SkColor* fColors;
@ -192,4 +216,5 @@ static void TestGradientShaders(skiatest::Reporter* reporter) {
DEF_TEST(Gradient, reporter) { DEF_TEST(Gradient, reporter) {
TestGradientShaders(reporter); TestGradientShaders(reporter);
TestConstantGradient(reporter); TestConstantGradient(reporter);
test_big_grad(reporter);
} }