improve precision of gradients (disabled for now)

BUG=skia:2898
TBR=caryclark

Review URL: https://codereview.chromium.org/815623004
This commit is contained in:
reed 2014-12-18 12:43:08 -08:00 committed by Commit bot
parent 695025fe1d
commit caf7e9313b
4 changed files with 73 additions and 43 deletions

View File

@ -13,6 +13,7 @@
# If these become 'permanent', they should be moved into skia_common.gypi
#
'skia_for_chromium_defines': [
'SK_SUPPORT_LEGACY_GRADIENT_PRECISION',
],
},
}

View File

@ -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

View File

@ -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

View File

@ -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;