diff options
author | reed <reed@google.com> | 2014-12-18 12:43:08 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-12-18 12:43:08 -0800 |
commit | caf7e9313b52f78b53ff7d478f9cc41a1f6a85ff (patch) | |
tree | 9cbdb92fb9e9630ec762024a3c84d4767851f1c8 /src/effects | |
parent | 695025fe1d638955d399cd8ff33f6fc7ca5e935e (diff) |
improve precision of gradients (disabled for now)
BUG=skia:2898
TBR=caryclark
Review URL: https://codereview.chromium.org/815623004
Diffstat (limited to 'src/effects')
-rw-r--r-- | src/effects/gradients/SkClampRange.cpp | 36 | ||||
-rw-r--r-- | src/effects/gradients/SkClampRange.h | 27 | ||||
-rw-r--r-- | src/effects/gradients/SkLinearGradient.cpp | 52 |
3 files changed, 72 insertions, 43 deletions
diff --git a/src/effects/gradients/SkClampRange.cpp b/src/effects/gradients/SkClampRange.cpp index 398b02434f..cf302cd2a9 100644 --- a/src/effects/gradients/SkClampRange.cpp +++ b/src/effects/gradients/SkClampRange.cpp @@ -13,7 +13,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(int64_t x0, SkFixed edge, int64_t x1, int64_t dx, int count) { +static int chop(int64_t x0, SkGradFixed edge, int64_t x1, int64_t dx, int count) { SkASSERT(dx > 0); SkASSERT(count >= 0); @@ -29,15 +29,17 @@ static int chop(int64_t x0, SkFixed edge, int64_t x1, int64_t dx, int count) { return (int)n; } -static bool overflows_fixed(int64_t x) { +#ifdef SK_SUPPORT_LEGACY_GRADIENT_PRECISION +static bool overflows_gradfixed(int64_t x) { return x < -SK_FixedMax || x > SK_FixedMax; } +#endif -void SkClampRange::initFor1(SkFixed fx) { +void SkClampRange::initFor1(SkGradFixed fx) { fCount0 = fCount1 = fCount2 = 0; if (fx <= 0) { fCount0 = 1; - } else if (fx < 0xFFFF) { + } else if (fx < kFracMax_SkGradFixed) { fCount1 = 1; fFx1 = fx; } else { @@ -45,7 +47,7 @@ void SkClampRange::initFor1(SkFixed fx) { } } -void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) { +void SkClampRange::init(SkGradFixed fx0, SkGradFixed dx0, int count, int v0, int v1) { SkASSERT(count > 0); fV0 = v0; @@ -60,10 +62,11 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) { int64_t fx = fx0; int64_t dx = dx0; + // start with ex equal to the last computed value int64_t ex = fx + (count - 1) * dx; - if ((uint64_t)(fx | ex) <= 0xFFFF) { + if ((uint64_t)(fx | ex) <= kFracMax_SkGradFixed) { fCount0 = fCount2 = 0; fCount1 = count; fFx1 = fx0; @@ -74,7 +77,7 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) { fCount0 = count; return; } - if (fx >= 0xFFFF && ex >= 0xFFFF) { + if (fx >= kFracMax_SkGradFixed && ex >= kFracMax_SkGradFixed) { fCount0 = fCount1 = 0; fCount2 = count; return; @@ -84,8 +87,10 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) { // now make ex be 1 past the last computed value ex += dx; + +#ifdef SK_SUPPORT_LEGACY_GRADIENT_PRECISION // now check for over/under flow - if (overflows_fixed(ex)) { + if (overflows_gradfixed(ex)) { int originalCount = count; int64_t ccount; bool swap = dx < 0; @@ -93,7 +98,13 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) { dx = -dx; fx = -fx; } - ccount = (SK_FixedMax - fx + dx - 1) / dx; + + int shift = 0; + if (sizeof(SkGradFixed) == 8) { + shift = 16; + } + + ccount = ((SK_FixedMax << shift) - fx + dx - 1) / dx; if (swap) { dx = -dx; fx = -fx; @@ -113,6 +124,7 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) { extraCount = originalCount - count; ex = fx + dx * count; } +#endif bool doSwap = dx < 0; @@ -129,7 +141,7 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) { fx += fCount0 * dx; SkASSERT(fx >= 0); SkASSERT(fCount0 == 0 || (fx - dx) < 0); - fCount1 = chop(fx, 0xFFFF, ex, dx, count); + fCount1 = chop(fx, kFracMax_SkGradFixed, ex, dx, count); count -= fCount1; fCount2 = count; @@ -137,9 +149,9 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) { fx += fCount1 * dx; SkASSERT(fx <= ex); if (fCount2 > 0) { - SkASSERT(fx >= 0xFFFF); + SkASSERT(fx >= kFracMax_SkGradFixed); if (fCount1 > 0) { - SkASSERT(fx - dx < 0xFFFF); + SkASSERT(fx - dx < kFracMax_SkGradFixed); } } #endif diff --git a/src/effects/gradients/SkClampRange.h b/src/effects/gradients/SkClampRange.h index 09386d7e71..a71009d654 100644 --- a/src/effects/gradients/SkClampRange.h +++ b/src/effects/gradients/SkClampRange.h @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -6,14 +5,30 @@ * found in the LICENSE file. */ - #ifndef SkClampRange_DEFINED #define SkClampRange_DEFINED #include "SkFixed.h" +#include "SkScalar.h" + +#define SK_SUPPORT_LEGACY_GRADIENT_PRECISION + +#ifdef SK_SUPPORT_LEGACY_GRADIENT_PRECISION + #define SkGradFixed SkFixed + #define SkScalarToGradFixed SkScalarToFixed + #define SkFixedToGradFixed(x) (x) + #define SkGradFixedToFixed(x) (x) + #define kFracMax_SkGradFixed 0xFFFF +#else + #define SkGradFixed SkFixed3232 + #define SkScalarToGradFixed SkScalarToFixed3232 + #define SkFixedToGradFixed SkFixedToFixed3232 + #define SkGradFixedToFixed(x) (SkFixed)((x) >> 16) + #define kFracMax_SkGradFixed 0xFFFFFFFFLL +#endif /** - * Iteration fixed fx by dx, clamping as you go to [0..0xFFFF], this class + * Iteration fixed fx by dx, clamping as you go to [0..kFracMax_SkGradFixed], this class * computes the (up to) 3 spans there are: * * range0: use constant value V0 @@ -24,14 +39,14 @@ struct SkClampRange { int fCount0; // count for fV0 int fCount1; // count for interpolating (fV0...fV1) int fCount2; // count for fV1 - SkFixed fFx1; // initial fx value for the fCount1 range. + SkGradFixed fFx1; // initial fx value for the fCount1 range. // only valid if fCount1 > 0 int fV0, fV1; - void init(SkFixed fx, SkFixed dx, int count, int v0, int v1); + void init(SkGradFixed fx, SkGradFixed dx, int count, int v0, int v1); private: - void initFor1(SkFixed fx); + void initFor1(SkGradFixed fx); }; #endif diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp index 4e7a6c729d..885a1b56c8 100644 --- a/src/effects/gradients/SkLinearGradient.cpp +++ b/src/effects/gradients/SkLinearGradient.cpp @@ -104,7 +104,7 @@ SkLinearGradient::LinearGradientContext::LinearGradientContext( #define NO_CHECK_ITER \ do { \ - unsigned fi = fx >> SkGradientShaderBase::kCache32Shift; \ + unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \ SkASSERT(fi <= 0xFF); \ fx += dx; \ *dstC++ = cache[toggle + fi]; \ @@ -113,21 +113,21 @@ SkLinearGradient::LinearGradientContext::LinearGradientContext( namespace { -typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx, +typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx, SkPMColor* dstC, const SkPMColor* cache, int toggle, int count); // Linear interpolation (lerp) is unnecessary if there are no sharp // discontinuities in the gradient - which must be true if there are // only 2 colors - but it's cheap. -void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx, +void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx, SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, int toggle, int count) { // We're a vertical gradient, so no change in a span. // If colors change sharply across the gradient, dithering is // insufficient (it subsamples the color space) and we need to lerp. - unsigned fullIndex = proc(fx); + unsigned fullIndex = proc(SkGradFixedToFixed(fx)); unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift; unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1); @@ -143,7 +143,7 @@ void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx, sk_memset32_dither(dstC, lerp, dlerp, count); } -void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx, +void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx, SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, int toggle, int count) { @@ -180,12 +180,12 @@ void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx, } } -void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx, +void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx, SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, int toggle, int count) { do { - unsigned fi = mirror_8bits(fx >> 8); + unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8); SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[toggle + fi]; @@ -193,12 +193,12 @@ void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx, } while (--count != 0); } -void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx, +void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx, SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, int toggle, int count) { do { - unsigned fi = repeat_8bits(fx >> 8); + unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8); SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[toggle + fi]; @@ -223,15 +223,16 @@ void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* if (fDstToIndexClass != kPerspective_MatrixClass) { dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkFixed dx, fx = SkScalarToFixed(srcPt.fX); + SkGradFixed dx, fx = SkScalarToGradFixed(srcPt.fX); if (fDstToIndexClass == kFixedStepInX_MatrixClass) { SkFixed dxStorage[1]; (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); - dx = dxStorage[0]; + // todo: do we need a real/high-precision value for dx here? + dx = SkFixedToGradFixed(dxStorage[0]); } else { SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - dx = SkScalarToFixed(fDstToIndex.getScaleX()); + dx = SkScalarToGradFixed(fDstToIndex.getScaleX()); } LinearShadeProc shadeProc = shadeSpan_linear_repeat; @@ -301,7 +302,7 @@ static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, #define NO_CHECK_ITER_16 \ do { \ - unsigned fi = fx >> SkGradientShaderBase::kCache16Shift; \ + unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift; \ SkASSERT(fi < SkGradientShaderBase::kCache16Count); \ fx += dx; \ *dstC++ = cache[toggle + fi]; \ @@ -310,22 +311,22 @@ static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, namespace { -typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx, +typedef void (*LinearShade16Proc)(TileProc proc, SkGradFixed dx, SkGradFixed fx, uint16_t* dstC, const uint16_t* cache, int toggle, int count); -void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx, +void shadeSpan16_linear_vertical(TileProc proc, SkGradFixed dx, SkGradFixed fx, uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, int toggle, int count) { // we're a vertical gradient, so no change in a span - unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift; + unsigned fi = proc(SkGradFixedToFixed(fx)) >> SkGradientShaderBase::kCache16Shift; SkASSERT(fi < SkGradientShaderBase::kCache16Count); dither_memset16(dstC, cache[toggle + fi], cache[next_dither_toggle16(toggle) + fi], count); } -void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx, +void shadeSpan16_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx, uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, int toggle, int count) { @@ -362,12 +363,12 @@ void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx, } } -void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx, +void shadeSpan16_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx, uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, int toggle, int count) { do { - unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift, + unsigned fi = mirror_bits(SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift, SkGradientShaderBase::kCache16Bits); SkASSERT(fi < SkGradientShaderBase::kCache16Count); fx += dx; @@ -376,12 +377,12 @@ void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx, } while (--count != 0); } -void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx, +void shadeSpan16_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx, uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache, int toggle, int count) { do { - unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift, + unsigned fi = repeat_bits(SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift, SkGradientShaderBase::kCache16Bits); SkASSERT(fi < SkGradientShaderBase::kCache16Count); fx += dx; @@ -410,19 +411,20 @@ void SkLinearGradient::LinearGradientContext::shadeSpan16(int x, int y, if (fDstToIndexClass != kPerspective_MatrixClass) { dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkFixed dx, fx = SkScalarToFixed(srcPt.fX); + SkGradFixed dx, fx = SkScalarToGradFixed(srcPt.fX); if (fDstToIndexClass == kFixedStepInX_MatrixClass) { SkFixed dxStorage[1]; (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); - dx = dxStorage[0]; + // todo: do we need a real/high-precision value for dx here? + dx = SkFixedToGradFixed(dxStorage[0]); } else { SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - dx = SkScalarToFixed(fDstToIndex.getScaleX()); + dx = SkScalarToGradFixed(fDstToIndex.getScaleX()); } LinearShade16Proc shadeProc = shadeSpan16_linear_repeat; - if (fixed_nearly_zero(dx)) { + if (fixed_nearly_zero(SkGradFixedToFixed(dx))) { shadeProc = shadeSpan16_linear_vertical; } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) { shadeProc = shadeSpan16_linear_clamp; |