diff options
author | 2011-04-18 19:59:38 +0000 | |
---|---|---|
committer | 2011-04-18 19:59:38 +0000 | |
commit | 13659f1f8d2e705c565203d45870b1afcd47cf98 (patch) | |
tree | 37bcae73b9ebb93f7d33fefc0ed30c858b3c4564 /src/core/SkClampRange.cpp | |
parent | 8b484419e80762ccdecca9ac2c10c1c29368cf2f (diff) |
handle overflow
note: gradient caller doesn't so we can still draw wrong when the caller
converts its initial fx from float->fixed. Perhaps SkClampRange should offer
a float interface as well.
git-svn-id: http://skia.googlecode.com/svn/trunk@1149 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/core/SkClampRange.cpp')
-rw-r--r-- | src/core/SkClampRange.cpp | 91 |
1 files changed, 73 insertions, 18 deletions
diff --git a/src/core/SkClampRange.cpp b/src/core/SkClampRange.cpp index be41067f23..32ea96429e 100644 --- a/src/core/SkClampRange.cpp +++ b/src/core/SkClampRange.cpp @@ -20,7 +20,7 @@ * returns [0..count] for the number of steps (<= count) for which x0 <= edge * given each step is followed by x0 += dx */ -static int chop(SkFixed x0, SkFixed edge, SkFixed x1, SkFixed dx, int count) { +static int chop(int64_t x0, SkFixed edge, int64_t x1, int64_t dx, int count) { SkASSERT(dx > 0); SkASSERT(count >= 0); @@ -30,36 +30,52 @@ static int chop(SkFixed x0, SkFixed edge, SkFixed x1, SkFixed dx, int count) { if (x1 <= edge) { return count; } - int n = (edge - x0 + dx - 1) / dx; + int64_t n = (edge - x0 + dx - 1) / dx; SkASSERT(n >= 0); SkASSERT(n <= count); - return n; + return (int)n; } -void SkClampRange::init(SkFixed fx, SkFixed dx, int count, int v0, int v1) { +static bool overflows_fixed(int64_t x) { + return x < -SK_FixedMax || x > SK_FixedMax; +} + +void SkClampRange::initFor1(SkFixed fx) { + fCount0 = fCount1 = fCount2 = 0; + if (fx <= 0) { + fCount0 = 1; + } else if (fx < 0xFFFF) { + fCount1 = 1; + fFx1 = fx; + } else { + fCount2 = 1; + } +} + +void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) { SkASSERT(count > 0); fV0 = v0; fV1 = v1; + fOverflowed = false; - // check for over/underflow - { - int64_t eex = (int64_t)fx + count * (int64_t)dx; - if (eex > SK_FixedMax) { - - } else if (eex < -SK_FixedMax) { - } + // special case 1 == count, as it is slightly common for skia + // and avoids us ever calling divide or 64bit multiply + if (1 == count) { + this->initFor1(fx0); + return; } - // remember our original fx - const SkFixed fx0 = fx; + int64_t fx = fx0; + int64_t dx = dx0; // start with ex equal to the last computed value - SkFixed ex = fx + (count - 1) * dx; + int64_t ex = fx + (count - 1) * dx; + fOverflowed = overflows_fixed(ex); - if ((unsigned)(fx | ex) <= 0xFFFF) { + if ((uint64_t)(fx | ex) <= 0xFFFF) { fCount0 = fCount2 = 0; fCount1 = count; - fFx1 = fx; + fFx1 = fx0; return; } if (fx <= 0 && ex <= 0) { @@ -73,9 +89,41 @@ void SkClampRange::init(SkFixed fx, SkFixed dx, int count, int v0, int v1) { return; } + int extraCount = 0; + // now make ex be 1 past the last computed value ex += dx; - + fOverflowed = overflows_fixed(ex); + // now check for over/under flow + if (fOverflowed) { + int originalCount = count; + int64_t ccount; + bool swap = dx < 0; + if (swap) { + dx = -dx; + fx = -fx; + } + ccount = (SK_FixedMax - fx + dx - 1) / dx; + if (swap) { + dx = -dx; + fx = -fx; + } + SkASSERT(ccount > 0 && ccount <= SK_FixedMax); + + count = (int)ccount; + if (0 == count) { + this->initFor1(fx0); + if (dx > 0) { + fCount2 += originalCount - 1; + } else { + fCount0 += originalCount - 1; + } + return; + } + extraCount = originalCount - count; + ex = fx + dx * count; + } + bool doSwap = dx < 0; if (doSwap) { @@ -85,6 +133,7 @@ void SkClampRange::init(SkFixed fx, SkFixed dx, int count, int v0, int v1) { dx = -dx; } + fCount0 = chop(fx, 0, ex, dx, count); count -= fCount0; fx += fCount0 * dx; @@ -112,7 +161,13 @@ void SkClampRange::init(SkFixed fx, SkFixed dx, int count, int v0, int v1) { } if (fCount1 > 0) { - fFx1 = fx0 + fCount0 * dx; + fFx1 = fx0 + fCount0 * (int)dx; + } + + if (dx > 0) { + fCount2 += extraCount; + } else { + fCount0 += extraCount; } } |