Optional gradient dithering

Update raster gradient impls to observe the paint dithering flag.

For now this new behavior is disabled pending internal/external
client updates to mitigate diffs.

BUG=skia:4436
R=reed@google.com,mtklein@google.com,tomhudson@google.com

Review URL: https://codereview.chromium.org/1391303002
This commit is contained in:
fmalita 2015-10-09 10:22:46 -07:00 committed by Commit bot
parent 2c3b218f74
commit 37d8688927
2 changed files with 103 additions and 71 deletions

View File

@ -290,10 +290,17 @@ bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
return true;
}
#define SK_SUPPORT_LEGACY_GRADIENT_DITHERING
SkGradientShaderBase::GradientShaderBaseContext::GradientShaderBaseContext(
const SkGradientShaderBase& shader, const ContextRec& rec)
: INHERITED(shader, rec)
, fCache(shader.refCache(getPaintAlpha()))
#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
, fDither(true)
#else
, fDither(rec.fPaint->isDither())
#endif
, fCache(shader.refCache(getPaintAlpha(), fDither))
{
const SkMatrix& inverse = this->getTotalInverse();
@ -317,8 +324,9 @@ SkGradientShaderBase::GradientShaderBaseContext::GradientShaderBaseContext(
}
SkGradientShaderBase::GradientShaderCache::GradientShaderCache(
U8CPU alpha, const SkGradientShaderBase& shader)
U8CPU alpha, bool dither, const SkGradientShaderBase& shader)
: fCacheAlpha(alpha)
, fCacheDither(dither)
, fShader(shader)
, fCache16Inited(false)
, fCache32Inited(false)
@ -342,7 +350,7 @@ SkGradientShaderBase::GradientShaderCache::~GradientShaderCache() {
paint specifies a non-opaque alpha.
*/
void SkGradientShaderBase::GradientShaderCache::Build16bitCache(
uint16_t cache[], SkColor c0, SkColor c1, int count) {
uint16_t cache[], SkColor c0, SkColor c1, int count, bool dither) {
SkASSERT(count > 1);
SkASSERT(SkColorGetA(c0) == 0xFF);
SkASSERT(SkColorGetA(c1) == 0xFF);
@ -359,17 +367,31 @@ void SkGradientShaderBase::GradientShaderCache::Build16bitCache(
g = SkIntToFixed(g) + 0x8000;
b = SkIntToFixed(b) + 0x8000;
do {
unsigned rr = r >> 16;
unsigned gg = g >> 16;
unsigned bb = b >> 16;
cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
cache += 1;
r += dr;
g += dg;
b += db;
} while (--count != 0);
if (dither) {
do {
unsigned rr = r >> 16;
unsigned gg = g >> 16;
unsigned bb = b >> 16;
cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
cache += 1;
r += dr;
g += dg;
b += db;
} while (--count != 0);
} else {
do {
unsigned rr = r >> 16;
unsigned gg = g >> 16;
unsigned bb = b >> 16;
cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
cache[kCache16Count] = cache[0];
cache += 1;
r += dr;
g += dg;
b += db;
} while (--count != 0);
}
}
/*
@ -392,7 +414,7 @@ typedef uint32_t SkUFixed;
void SkGradientShaderBase::GradientShaderCache::Build32bitCache(
SkPMColor cache[], SkColor c0, SkColor c1,
int count, U8CPU paintAlpha, uint32_t gradFlags) {
int count, U8CPU paintAlpha, uint32_t gradFlags, bool dither) {
SkASSERT(count > 1);
// need to apply paintAlpha to our two endpoints
@ -432,10 +454,15 @@ void SkGradientShaderBase::GradientShaderCache::Build32bitCache(
With this trick, we can add 0 for the first (no-op) and just adjust the
others.
*/
SkUFixed a = SkIntToFixed(a0) + 0x2000;
SkUFixed r = SkIntToFixed(r0) + 0x2000;
SkUFixed g = SkIntToFixed(g0) + 0x2000;
SkUFixed b = SkIntToFixed(b0) + 0x2000;
const SkUFixed bias0 = dither ? 0x2000 : 0x8000;
const SkUFixed bias1 = dither ? 0x8000 : 0;
const SkUFixed bias2 = dither ? 0xC000 : 0;
const SkUFixed bias3 = dither ? 0x4000 : 0;
SkUFixed a = SkIntToFixed(a0) + bias0;
SkUFixed r = SkIntToFixed(r0) + bias0;
SkUFixed g = SkIntToFixed(g0) + bias0;
SkUFixed b = SkIntToFixed(b0) + bias0;
/*
* Our dither-cell (spatially) is
@ -450,18 +477,18 @@ void SkGradientShaderBase::GradientShaderCache::Build32bitCache(
if (0xFF == a0 && 0 == da) {
do {
cache[kCache32Count*0] = SkPackARGB32(0xFF, (r + 0 ) >> 16,
(g + 0 ) >> 16,
(b + 0 ) >> 16);
cache[kCache32Count*1] = SkPackARGB32(0xFF, (r + 0x8000) >> 16,
(g + 0x8000) >> 16,
(b + 0x8000) >> 16);
cache[kCache32Count*2] = SkPackARGB32(0xFF, (r + 0xC000) >> 16,
(g + 0xC000) >> 16,
(b + 0xC000) >> 16);
cache[kCache32Count*3] = SkPackARGB32(0xFF, (r + 0x4000) >> 16,
(g + 0x4000) >> 16,
(b + 0x4000) >> 16);
cache[kCache32Count*0] = SkPackARGB32(0xFF, (r + 0 ) >> 16,
(g + 0 ) >> 16,
(b + 0 ) >> 16);
cache[kCache32Count*1] = SkPackARGB32(0xFF, (r + bias1) >> 16,
(g + bias1) >> 16,
(b + bias1) >> 16);
cache[kCache32Count*2] = SkPackARGB32(0xFF, (r + bias2) >> 16,
(g + bias2) >> 16,
(b + bias2) >> 16);
cache[kCache32Count*3] = SkPackARGB32(0xFF, (r + bias3) >> 16,
(g + bias3) >> 16,
(b + bias3) >> 16);
cache += 1;
r += dr;
g += dg;
@ -469,22 +496,22 @@ void SkGradientShaderBase::GradientShaderCache::Build32bitCache(
} while (--count != 0);
} else if (interpInPremul) {
do {
cache[kCache32Count*0] = SkPackARGB32((a + 0 ) >> 16,
(r + 0 ) >> 16,
(g + 0 ) >> 16,
(b + 0 ) >> 16);
cache[kCache32Count*1] = SkPackARGB32((a + 0x8000) >> 16,
(r + 0x8000) >> 16,
(g + 0x8000) >> 16,
(b + 0x8000) >> 16);
cache[kCache32Count*2] = SkPackARGB32((a + 0xC000) >> 16,
(r + 0xC000) >> 16,
(g + 0xC000) >> 16,
(b + 0xC000) >> 16);
cache[kCache32Count*3] = SkPackARGB32((a + 0x4000) >> 16,
(r + 0x4000) >> 16,
(g + 0x4000) >> 16,
(b + 0x4000) >> 16);
cache[kCache32Count*0] = SkPackARGB32((a + 0 ) >> 16,
(r + 0 ) >> 16,
(g + 0 ) >> 16,
(b + 0 ) >> 16);
cache[kCache32Count*1] = SkPackARGB32((a + bias1) >> 16,
(r + bias1) >> 16,
(g + bias1) >> 16,
(b + bias1) >> 16);
cache[kCache32Count*2] = SkPackARGB32((a + bias2) >> 16,
(r + bias2) >> 16,
(g + bias2) >> 16,
(b + bias2) >> 16);
cache[kCache32Count*3] = SkPackARGB32((a + bias3) >> 16,
(r + bias3) >> 16,
(g + bias3) >> 16,
(b + bias3) >> 16);
cache += 1;
a += da;
r += dr;
@ -497,18 +524,18 @@ void SkGradientShaderBase::GradientShaderCache::Build32bitCache(
(r + 0 ) >> 16,
(g + 0 ) >> 16,
(b + 0 ) >> 16);
cache[kCache32Count*1] = SkPremultiplyARGBInline((a + 0x8000) >> 16,
(r + 0x8000) >> 16,
(g + 0x8000) >> 16,
(b + 0x8000) >> 16);
cache[kCache32Count*2] = SkPremultiplyARGBInline((a + 0xC000) >> 16,
(r + 0xC000) >> 16,
(g + 0xC000) >> 16,
(b + 0xC000) >> 16);
cache[kCache32Count*3] = SkPremultiplyARGBInline((a + 0x4000) >> 16,
(r + 0x4000) >> 16,
(g + 0x4000) >> 16,
(b + 0x4000) >> 16);
cache[kCache32Count*1] = SkPremultiplyARGBInline((a + bias1) >> 16,
(r + bias1) >> 16,
(g + bias1) >> 16,
(b + bias1) >> 16);
cache[kCache32Count*2] = SkPremultiplyARGBInline((a + bias2) >> 16,
(r + bias2) >> 16,
(g + bias2) >> 16,
(b + bias2) >> 16);
cache[kCache32Count*3] = SkPremultiplyARGBInline((a + bias3) >> 16,
(r + bias3) >> 16,
(g + bias3) >> 16,
(b + bias3) >> 16);
cache += 1;
a += da;
r += dr;
@ -540,7 +567,7 @@ void SkGradientShaderBase::GradientShaderCache::initCache16(GradientShaderCache*
cache->fCache16 = cache->fCache16Storage;
if (cache->fShader.fColorCount == 2) {
Build16bitCache(cache->fCache16, cache->fShader.fOrigColors[0],
cache->fShader.fOrigColors[1], kCache16Count);
cache->fShader.fOrigColors[1], kCache16Count, cache->fCacheDither);
} else {
Rec* rec = cache->fShader.fRecs;
int prevIndex = 0;
@ -550,7 +577,8 @@ void SkGradientShaderBase::GradientShaderCache::initCache16(GradientShaderCache*
if (nextIndex > prevIndex)
Build16bitCache(cache->fCache16 + prevIndex, cache->fShader.fOrigColors[i-1],
cache->fShader.fOrigColors[i], nextIndex - prevIndex + 1);
cache->fShader.fOrigColors[i], nextIndex - prevIndex + 1,
cache->fCacheDither);
prevIndex = nextIndex;
}
}
@ -573,7 +601,7 @@ void SkGradientShaderBase::GradientShaderCache::initCache32(GradientShaderCache*
if (cache->fShader.fColorCount == 2) {
Build32bitCache(cache->fCache32, cache->fShader.fOrigColors[0],
cache->fShader.fOrigColors[1], kCache32Count, cache->fCacheAlpha,
cache->fShader.fGradFlags);
cache->fShader.fGradFlags, cache->fCacheDither);
} else {
Rec* rec = cache->fShader.fRecs;
int prevIndex = 0;
@ -584,7 +612,7 @@ void SkGradientShaderBase::GradientShaderCache::initCache32(GradientShaderCache*
if (nextIndex > prevIndex)
Build32bitCache(cache->fCache32 + prevIndex, cache->fShader.fOrigColors[i-1],
cache->fShader.fOrigColors[i], nextIndex - prevIndex + 1,
cache->fCacheAlpha, cache->fShader.fGradFlags);
cache->fCacheAlpha, cache->fShader.fGradFlags, cache->fCacheDither);
prevIndex = nextIndex;
}
}
@ -594,10 +622,11 @@ void SkGradientShaderBase::GradientShaderCache::initCache32(GradientShaderCache*
* The gradient holds a cache for the most recent value of alpha. Successive
* callers with the same alpha value will share the same cache.
*/
SkGradientShaderBase::GradientShaderCache* SkGradientShaderBase::refCache(U8CPU alpha) const {
SkGradientShaderBase::GradientShaderCache* SkGradientShaderBase::refCache(U8CPU alpha,
bool dither) const {
SkAutoMutexAcquire ama(fCacheMutex);
if (!fCache || fCache->getAlpha() != alpha) {
fCache.reset(new GradientShaderCache(alpha, *this));
if (!fCache || fCache->getAlpha() != alpha || fCache->getDither() != dither) {
fCache.reset(new GradientShaderCache(alpha, dither, *this));
}
// Increment the ref counter inside the mutex to ensure the returned pointer is still valid.
// Otherwise, the pointer may have been overwritten on a different thread before the object's
@ -618,7 +647,7 @@ SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex);
void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap) const {
// our caller assumes no external alpha, so we ensure that our cache is
// built with 0xFF
SkAutoTUnref<GradientShaderCache> cache(this->refCache(0xFF));
SkAutoTUnref<GradientShaderCache> cache(this->refCache(0xFF, true));
// build our key: [numColors + colors[] + {positions[]} + flags ]
int count = 1 + fColorCount + 1;

View File

@ -126,7 +126,7 @@ public:
// The cache is initialized on-demand when getCache16/32 is called.
class GradientShaderCache : public SkRefCnt {
public:
GradientShaderCache(U8CPU alpha, const SkGradientShaderBase& shader);
GradientShaderCache(U8CPU alpha, bool dither, const SkGradientShaderBase& shader);
~GradientShaderCache();
const uint16_t* getCache16();
@ -135,6 +135,7 @@ public:
SkMallocPixelRef* getCache32PixelRef() const { return fCache32PixelRef; }
unsigned getAlpha() const { return fCacheAlpha; }
bool getDither() const { return fCacheDither; }
private:
// Working pointers. If either is nullptr, we need to recompute the corresponding cache values.
@ -146,6 +147,7 @@ public:
const unsigned fCacheAlpha; // The alpha value we used when we computed the cache.
// Larger than 8bits so we can store uninitialized
// value.
const bool fCacheDither; // The dither flag used when we computed the cache.
const SkGradientShaderBase& fShader;
@ -156,9 +158,9 @@ public:
static void initCache16(GradientShaderCache* cache);
static void initCache32(GradientShaderCache* cache);
static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count, bool dither);
static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
U8CPU alpha, uint32_t gradFlags);
U8CPU alpha, uint32_t gradFlags, bool dither);
};
class GradientShaderBaseContext : public SkShader::Context {
@ -172,6 +174,7 @@ public:
SkMatrix::MapXYProc fDstToIndexProc;
uint8_t fDstToIndexClass;
uint8_t fFlags;
bool fDither;
SkAutoTUnref<GradientShaderCache> fCache;
@ -260,7 +263,7 @@ private:
SkScalar* fOrigPos; // original positions
bool fColorsAreOpaque;
GradientShaderCache* refCache(U8CPU alpha) const;
GradientShaderCache* refCache(U8CPU alpha, bool dither) const;
mutable SkMutex fCacheMutex;
mutable SkAutoTUnref<GradientShaderCache> fCache;