| Index: skia/effects/SkGradientShader.cpp
|
| ===================================================================
|
| --- skia/effects/SkGradientShader.cpp (revision 16859)
|
| +++ skia/effects/SkGradientShader.cpp (working copy)
|
| @@ -1,1562 +0,0 @@
|
| -/* libs/graphics/effects/SkGradientShader.cpp
|
| -**
|
| -** Copyright 2006, The Android Open Source Project
|
| -**
|
| -** Licensed under the Apache License, Version 2.0 (the "License");
|
| -** you may not use this file except in compliance with the License.
|
| -** You may obtain a copy of the License at
|
| -**
|
| -** http://www.apache.org/licenses/LICENSE-2.0
|
| -**
|
| -** Unless required by applicable law or agreed to in writing, software
|
| -** distributed under the License is distributed on an "AS IS" BASIS,
|
| -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| -** See the License for the specific language governing permissions and
|
| -** limitations under the License.
|
| -*/
|
| -
|
| -#include "SkGradientShader.h"
|
| -#include "SkColorPriv.h"
|
| -#include "SkUnitMapper.h"
|
| -#include "SkUtils.h"
|
| -
|
| -/*
|
| - ToDo
|
| -
|
| - - not sure we still need the full Rec struct, now that we're using a cache
|
| - - detect const-alpha (but not opaque) in getFlags()
|
| -*/
|
| -
|
| -/* dither seems to look better, but not stuningly yet, and it slows us down a little
|
| - so its not on by default yet.
|
| -*/
|
| -#define TEST_GRADIENT_DITHER
|
| -
|
| -///////////////////////////////////////////////////////////////////////////
|
| -
|
| -typedef SkFixed (*TileProc)(SkFixed);
|
| -
|
| -static SkFixed clamp_tileproc(SkFixed x)
|
| -{
|
| - return SkClampMax(x, 0xFFFF);
|
| -}
|
| -
|
| -static SkFixed repeat_tileproc(SkFixed x)
|
| -{
|
| - return x & 0xFFFF;
|
| -}
|
| -
|
| -static inline SkFixed mirror_tileproc(SkFixed x)
|
| -{
|
| - int s = x << 15 >> 31;
|
| - return (x ^ s) & 0xFFFF;
|
| -}
|
| -
|
| -static const TileProc gTileProcs[] = {
|
| - clamp_tileproc,
|
| - repeat_tileproc,
|
| - mirror_tileproc
|
| -};
|
| -
|
| -//////////////////////////////////////////////////////////////////////////////
|
| -
|
| -static inline int repeat_6bits(int x)
|
| -{
|
| - return x & 63;
|
| -}
|
| -
|
| -static inline int mirror_6bits(int x)
|
| -{
|
| -#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
|
| - if (x & 64)
|
| - x = ~x;
|
| - return x & 63;
|
| -#else
|
| - int s = x << 25 >> 31;
|
| - return (x ^ s) & 63;
|
| -#endif
|
| -}
|
| -
|
| -static inline int repeat_8bits(int x)
|
| -{
|
| - return x & 0xFF;
|
| -}
|
| -
|
| -static inline int mirror_8bits(int x)
|
| -{
|
| -#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
|
| - if (x & 256)
|
| - x = ~x;
|
| - return x & 255;
|
| -#else
|
| - int s = x << 23 >> 31;
|
| - return (x ^ s) & 0xFF;
|
| -#endif
|
| -}
|
| -
|
| -//////////////////////////////////////////////////////////////////////////////
|
| -
|
| -class Gradient_Shader : public SkShader {
|
| -public:
|
| - Gradient_Shader(const SkColor colors[], const SkScalar pos[],
|
| - int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
|
| - virtual ~Gradient_Shader();
|
| -
|
| - // overrides
|
| - virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
|
| - virtual uint32_t getFlags() { return fFlags; }
|
| -
|
| -protected:
|
| - Gradient_Shader(SkFlattenableReadBuffer& );
|
| - SkUnitMapper* fMapper;
|
| - SkMatrix fPtsToUnit; // set by subclass
|
| - SkMatrix fDstToIndex;
|
| - SkMatrix::MapXYProc fDstToIndexProc;
|
| - SkPMColor* fARGB32;
|
| - TileMode fTileMode;
|
| - TileProc fTileProc;
|
| - uint16_t fColorCount;
|
| - uint8_t fDstToIndexClass;
|
| - uint8_t fFlags;
|
| -
|
| - struct Rec {
|
| - SkFixed fPos; // 0...1
|
| - uint32_t fScale; // (1 << 24) / range
|
| - };
|
| - Rec* fRecs;
|
| -
|
| - enum {
|
| - kCache16Bits = 6, // seems like enough for visual accuracy
|
| - kCache16Count = 1 << kCache16Bits,
|
| - kCache32Bits = 8, // pretty much should always be 8
|
| - kCache32Count = 1 << kCache32Bits
|
| - };
|
| - virtual void flatten(SkFlattenableWriteBuffer& );
|
| - const uint16_t* getCache16();
|
| - const SkPMColor* getCache32();
|
| -
|
| -private:
|
| - enum {
|
| - kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
|
| -
|
| - kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec))
|
| - };
|
| - SkColor fStorage[(kStorageSize + 3) >> 2];
|
| - SkColor* fOrigColors;
|
| -
|
| - uint16_t* fCache16; // working ptr. If this is NULL, we need to recompute the cache values
|
| - SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values
|
| -
|
| - uint16_t* fCache16Storage; // storage for fCache16, allocated on demand
|
| - SkPMColor* fCache32Storage; // storage for fCache32, allocated on demand
|
| - unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
|
| -
|
| - typedef SkShader INHERITED;
|
| -};
|
| -
|
| -static inline unsigned scalarToU16(SkScalar x)
|
| -{
|
| - SkASSERT(x >= 0 && x <= SK_Scalar1);
|
| -
|
| -#ifdef SK_SCALAR_IS_FLOAT
|
| - return (unsigned)(x * 0xFFFF);
|
| -#else
|
| - return x - (x >> 16); // probably should be x - (x > 0x7FFF) but that is slower
|
| -#endif
|
| -}
|
| -
|
| -Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[], int colorCount,
|
| - SkShader::TileMode mode, SkUnitMapper* mapper)
|
| -{
|
| - SkASSERT(colorCount > 1);
|
| -
|
| - fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return
|
| -
|
| - fMapper = mapper;
|
| - mapper->safeRef();
|
| -
|
| - fCache16 = fCache16Storage = NULL;
|
| - fCache32 = fCache32Storage = NULL;
|
| -
|
| - fColorCount = SkToU16(colorCount);
|
| - if (colorCount > kColorStorageCount)
|
| - fOrigColors = (SkColor*)sk_malloc_throw((sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec)) * colorCount);
|
| - else
|
| - fOrigColors = fStorage;
|
| - memcpy(fOrigColors, colors, colorCount * sizeof(SkColor));
|
| - // our premul colors point to the 2nd half of the array
|
| - // these are assigned each time in setContext
|
| - fARGB32 = fOrigColors + colorCount;
|
| -
|
| - SkASSERT((unsigned)mode < SkShader::kTileModeCount);
|
| - SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
|
| - fTileMode = mode;
|
| - fTileProc = gTileProcs[mode];
|
| -
|
| - fRecs = (Rec*)(fARGB32 + colorCount);
|
| - if (colorCount > 2)
|
| - {
|
| - Rec* recs = fRecs;
|
| -
|
| - recs[0].fPos = 0;
|
| - // recs[0].fScale = 0; // unused;
|
| - if (pos)
|
| - {
|
| - /* We need to convert the user's array of relative positions into
|
| - fixed-point positions and scale factors. We need these results
|
| - to be strictly monotonic (no two values equal or out of order).
|
| - Hence this complex loop that just jams a zero for the scale
|
| - value if it sees a segment out of order, and it assures that
|
| - we start at 0 and end at 1.0
|
| - */
|
| - SkFixed prev = 0;
|
| - for (int i = 1; i < colorCount; i++)
|
| - {
|
| - // force the last value to be 1.0
|
| - SkFixed curr;
|
| - if (i == colorCount - 1)
|
| - curr = SK_Fixed1;
|
| - else
|
| - {
|
| - curr = SkScalarToFixed(pos[i]);
|
| - // pin curr withing range
|
| - if (curr < 0)
|
| - curr = 0;
|
| - else if (curr > SK_Fixed1)
|
| - curr = SK_Fixed1;
|
| - }
|
| - recs[i].fPos = curr;
|
| - if (curr > prev)
|
| - recs[i].fScale = (1 << 24) / (curr - prev);
|
| - else
|
| - recs[i].fScale = 0; // ignore this segment
|
| - // get ready for the next value
|
| - prev = curr;
|
| - }
|
| - }
|
| - else // assume even distribution
|
| - {
|
| - SkFixed dp = SK_Fixed1 / (colorCount - 1);
|
| - SkFixed p = dp;
|
| - SkFixed scale = (colorCount - 1) << 8; // (1 << 24) / dp
|
| - for (int i = 1; i < colorCount; i++)
|
| - {
|
| - recs[i].fPos = p;
|
| - recs[i].fScale = scale;
|
| - p += dp;
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
|
| - INHERITED(buffer)
|
| -{
|
| - fCacheAlpha = 256;
|
| -
|
| - fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
|
| -
|
| - fCache16 = fCache16Storage = NULL;
|
| - fCache32 = fCache32Storage = NULL;
|
| -
|
| - int colorCount = fColorCount = buffer.readU16();
|
| - if (colorCount > kColorStorageCount)
|
| - fOrigColors = (SkColor*)sk_malloc_throw((sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec)) * colorCount);
|
| - else
|
| - fOrigColors = fStorage;
|
| - buffer.read(fOrigColors, colorCount * sizeof(SkColor));
|
| - fARGB32 = fOrigColors + colorCount;
|
| -
|
| - fTileMode = (TileMode)buffer.readU8();
|
| - fTileProc = gTileProcs[fTileMode];
|
| - fRecs = (Rec*)(fARGB32 + colorCount);
|
| - if (colorCount > 2) {
|
| - Rec* recs = fRecs;
|
| - recs[0].fPos = 0;
|
| - for (int i = 1; i < colorCount; i++) {
|
| - recs[i].fPos = buffer.readS32();
|
| - recs[i].fScale = buffer.readU32();
|
| - }
|
| - }
|
| - buffer.read(&fPtsToUnit, sizeof(SkMatrix));
|
| -}
|
| -
|
| -Gradient_Shader::~Gradient_Shader()
|
| -{
|
| - if (fCache16Storage)
|
| - sk_free(fCache16Storage);
|
| - if (fCache32Storage)
|
| - sk_free(fCache32Storage);
|
| - if (fOrigColors != fStorage)
|
| - sk_free(fOrigColors);
|
| - fMapper->safeUnref();
|
| -}
|
| -
|
| -void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer)
|
| -{
|
| - this->INHERITED::flatten(buffer);
|
| - buffer.writeFlattenable(fMapper);
|
| - buffer.write16(fColorCount);
|
| - buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
|
| - buffer.write8(fTileMode);
|
| - if (fColorCount > 2) {
|
| - Rec* recs = fRecs;
|
| - for (int i = 1; i < fColorCount; i++) {
|
| - buffer.write32(recs[i].fPos);
|
| - buffer.write32(recs[i].fScale);
|
| - }
|
| - }
|
| - buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
|
| -}
|
| -
|
| -bool Gradient_Shader::setContext(const SkBitmap& device,
|
| - const SkPaint& paint,
|
| - const SkMatrix& matrix)
|
| -{
|
| - if (!this->INHERITED::setContext(device, paint, matrix))
|
| - return false;
|
| -
|
| - const SkMatrix& inverse = this->getTotalInverse();
|
| -
|
| - if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
|
| - return false;
|
| - }
|
| -
|
| - fDstToIndexProc = fDstToIndex.getMapXYProc();
|
| - fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
|
| -
|
| - // now convert our colors in to PMColors
|
| - unsigned paintAlpha = this->getPaintAlpha();
|
| - unsigned colorAlpha = 0xFF;
|
| -
|
| - for (unsigned i = 0; i < fColorCount; i++) {
|
| - SkColor src = fOrigColors[i];
|
| - unsigned sa = SkColorGetA(src);
|
| - colorAlpha &= sa;
|
| -
|
| - // now modulate it by the paint for our resulting ARGB32 array
|
| - sa = SkMulDiv255Round(sa, paintAlpha);
|
| - fARGB32[i] = SkPreMultiplyARGB(sa, SkColorGetR(src), SkColorGetG(src),
|
| - SkColorGetB(src));
|
| - }
|
| -
|
| - fFlags = this->INHERITED::getFlags();
|
| - if ((colorAlpha & paintAlpha) == 0xFF) {
|
| - fFlags |= kOpaqueAlpha_Flag;
|
| - }
|
| - // we can do span16 as long as our individual colors are opaque,
|
| - // regardless of the paint's alpha
|
| - if (0xFF == colorAlpha) {
|
| - fFlags |= kHasSpan16_Flag;
|
| - }
|
| -
|
| - // if the new alpha differs from the previous time we were called, inval our cache
|
| - // this will trigger the cache to be rebuilt.
|
| - // we don't care about the first time, since the cache ptrs will already be NULL
|
| - if (fCacheAlpha != paintAlpha) {
|
| - fCache16 = NULL; // inval the cache
|
| - fCache32 = NULL; // inval the cache
|
| - fCacheAlpha = paintAlpha; // record the new alpha
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -static inline int blend8(int a, int b, int scale)
|
| -{
|
| - SkASSERT(a == SkToU8(a));
|
| - SkASSERT(b == SkToU8(b));
|
| - SkASSERT(scale >= 0 && scale <= 256);
|
| -
|
| - return a + ((b - a) * scale >> 8);
|
| -}
|
| -
|
| -static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1, int blend)
|
| -{
|
| -#if 0
|
| - int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
|
| - int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
|
| - int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
|
| - int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
|
| -
|
| - return SkPackARGB32(a, r, g, b);
|
| -#else
|
| - int otherBlend = 256 - blend;
|
| -
|
| -#if 0
|
| - U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
|
| - U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
|
| - SkASSERT((t0 & t1) == 0);
|
| - return t0 | t1;
|
| -#else
|
| - return ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
|
| - ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
|
| -#endif
|
| -
|
| -#endif
|
| -}
|
| -
|
| -#define Fixed_To_Dot8(x) (((x) + 0x80) >> 8)
|
| -
|
| -/** We take the original colors, not our premultiplied PMColors, since we can build a 16bit table
|
| - as long as the original colors are opaque, even if the paint specifies a non-opaque alpha.
|
| -*/
|
| -static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1, int count)
|
| -{
|
| - SkASSERT(count > 1);
|
| - SkASSERT(SkColorGetA(c0) == 0xFF);
|
| - SkASSERT(SkColorGetA(c1) == 0xFF);
|
| -
|
| - SkFixed r = SkColorGetR(c0);
|
| - SkFixed g = SkColorGetG(c0);
|
| - SkFixed b = SkColorGetB(c0);
|
| -
|
| - SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
|
| - SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
|
| - SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
|
| -
|
| - r = SkIntToFixed(r) + 0x8000;
|
| - 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[64] = SkDitherPack888ToRGB16(rr, gg, bb);
|
| - cache += 1;
|
| - r += dr;
|
| - g += dg;
|
| - b += db;
|
| - } while (--count != 0);
|
| -}
|
| -
|
| -static void build_32bit_cache(SkPMColor cache[], SkPMColor c0, SkPMColor c1, int count)
|
| -{
|
| - SkASSERT(count > 1);
|
| -
|
| - SkFixed a = SkGetPackedA32(c0);
|
| - SkFixed r = SkGetPackedR32(c0);
|
| - SkFixed g = SkGetPackedG32(c0);
|
| - SkFixed b = SkGetPackedB32(c0);
|
| -
|
| - SkFixed da = SkIntToFixed(SkGetPackedA32(c1) - a) / (count - 1);
|
| - SkFixed dr = SkIntToFixed(SkGetPackedR32(c1) - r) / (count - 1);
|
| - SkFixed dg = SkIntToFixed(SkGetPackedG32(c1) - g) / (count - 1);
|
| - SkFixed db = SkIntToFixed(SkGetPackedB32(c1) - b) / (count - 1);
|
| -
|
| - a = SkIntToFixed(a) + 0x8000;
|
| - r = SkIntToFixed(r) + 0x8000;
|
| - g = SkIntToFixed(g) + 0x8000;
|
| - b = SkIntToFixed(b) + 0x8000;
|
| -
|
| - do {
|
| - *cache++ = SkPackARGB32(a >> 16, r >> 16, g >> 16, b >> 16);
|
| - a += da;
|
| - r += dr;
|
| - g += dg;
|
| - b += db;
|
| - } while (--count != 0);
|
| -}
|
| -
|
| -static inline int SkFixedToFFFF(SkFixed x)
|
| -{
|
| - SkASSERT((unsigned)x <= SK_Fixed1);
|
| - return x - (x >> 16);
|
| -}
|
| -
|
| -static inline U16CPU dot6to16(unsigned x)
|
| -{
|
| - SkASSERT(x < 64);
|
| - return (x << 10) | (x << 4) | (x >> 2);
|
| -}
|
| -
|
| -const uint16_t* Gradient_Shader::getCache16()
|
| -{
|
| - if (fCache16 == NULL)
|
| - {
|
| - if (fCache16Storage == NULL) // set the storage and our working ptr
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
|
| -#else
|
| - fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
|
| -#endif
|
| - fCache16 = fCache16Storage;
|
| - if (fColorCount == 2)
|
| - build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
|
| - else
|
| - {
|
| - Rec* rec = fRecs;
|
| - int prevIndex = 0;
|
| - for (unsigned i = 1; i < fColorCount; i++)
|
| - {
|
| - int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
|
| - SkASSERT(nextIndex < kCache16Count);
|
| -
|
| - if (nextIndex > prevIndex)
|
| - build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
|
| - prevIndex = nextIndex;
|
| - }
|
| - SkASSERT(prevIndex == kCache16Count - 1);
|
| - }
|
| -
|
| - if (fMapper)
|
| - {
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
|
| -#else
|
| - fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
|
| -#endif
|
| - uint16_t* linear = fCache16; // just computed linear data
|
| - uint16_t* mapped = fCache16Storage; // storage for mapped data
|
| - SkUnitMapper* map = fMapper;
|
| - for (int i = 0; i < 64; i++)
|
| - {
|
| - int index = map->mapUnit16(dot6to16(i)) >> 10;
|
| - mapped[i] = linear[index];
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - mapped[i + 64] = linear[index + 64];
|
| -#endif
|
| - }
|
| - sk_free(fCache16);
|
| - fCache16 = fCache16Storage;
|
| - }
|
| - }
|
| - return fCache16;
|
| -}
|
| -
|
| -const SkPMColor* Gradient_Shader::getCache32()
|
| -{
|
| - if (fCache32 == NULL)
|
| - {
|
| - if (fCache32Storage == NULL) // set the storage and our working ptr
|
| - fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
|
| -
|
| - fCache32 = fCache32Storage;
|
| - if (fColorCount == 2)
|
| - build_32bit_cache(fCache32, fARGB32[0], fARGB32[1], kCache32Count);
|
| - else
|
| - {
|
| - Rec* rec = fRecs;
|
| - int prevIndex = 0;
|
| - for (unsigned i = 1; i < fColorCount; i++)
|
| - {
|
| - int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
|
| - SkASSERT(nextIndex < kCache32Count);
|
| -
|
| - if (nextIndex > prevIndex)
|
| - build_32bit_cache(fCache32 + prevIndex, fARGB32[i-1], fARGB32[i], nextIndex - prevIndex + 1);
|
| - prevIndex = nextIndex;
|
| - }
|
| - SkASSERT(prevIndex == kCache32Count - 1);
|
| - }
|
| -
|
| - if (fMapper)
|
| - {
|
| - fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
|
| - SkPMColor* linear = fCache32; // just computed linear data
|
| - SkPMColor* mapped = fCache32Storage; // storage for mapped data
|
| - SkUnitMapper* map = fMapper;
|
| - for (int i = 0; i < 256; i++)
|
| - mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
|
| - sk_free(fCache32);
|
| - fCache32 = fCache32Storage;
|
| - }
|
| - }
|
| - return fCache32;
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////
|
| -
|
| -static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix)
|
| -{
|
| - SkVector vec = pts[1] - pts[0];
|
| - SkScalar mag = vec.length();
|
| - SkScalar inv = mag ? SkScalarInvert(mag) : 0;
|
| -
|
| - vec.scale(inv);
|
| - matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
|
| - matrix->postTranslate(-pts[0].fX, -pts[0].fY);
|
| - matrix->postScale(inv, inv);
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| -class Linear_Gradient : public Gradient_Shader {
|
| -public:
|
| - Linear_Gradient(const SkPoint pts[2],
|
| - const SkColor colors[], const SkScalar pos[], int colorCount,
|
| - SkShader::TileMode mode, SkUnitMapper* mapper)
|
| - : Gradient_Shader(colors, pos, colorCount, mode, mapper)
|
| - {
|
| - pts_to_unit_matrix(pts, &fPtsToUnit);
|
| - }
|
| - virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
|
| - virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
|
| - virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
|
| -
|
| - static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
|
| - return SkNEW_ARGS(Linear_Gradient, (buffer));
|
| - }
|
| -
|
| -protected:
|
| - Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
|
| - virtual Factory getFactory() { return CreateProc; }
|
| -
|
| -private:
|
| - typedef Gradient_Shader INHERITED;
|
| -};
|
| -
|
| -// Return true if fx, fx+dx, fx+2*dx, ... is always in range
|
| -static bool no_need_for_clamp(int fx, int dx, int count)
|
| -{
|
| - SkASSERT(count > 0);
|
| - return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
|
| -}
|
| -
|
| -void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
|
| -{
|
| - SkASSERT(count > 0);
|
| -
|
| - SkPoint srcPt;
|
| - SkMatrix::MapXYProc dstProc = fDstToIndexProc;
|
| - TileProc proc = fTileProc;
|
| - const SkPMColor* cache = this->getCache32();
|
| -
|
| - if (fDstToIndexClass != kPerspective_MatrixClass)
|
| - {
|
| - dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
|
| - SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
|
| -
|
| - if (fDstToIndexClass == kFixedStepInX_MatrixClass)
|
| - {
|
| - SkFixed dxStorage[1];
|
| - (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
|
| - dx = dxStorage[0];
|
| - }
|
| - else
|
| - {
|
| - SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
|
| - dx = SkScalarToFixed(fDstToIndex.getScaleX());
|
| - }
|
| -
|
| - if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
|
| - {
|
| - unsigned fi = proc(fx);
|
| - SkASSERT(fi <= 0xFFFF);
|
| - sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
|
| - }
|
| - else if (proc == clamp_tileproc)
|
| - {
|
| -#if 0
|
| - if (no_need_for_clamp(fx, dx, count))
|
| - {
|
| - unsigned fi;
|
| - while ((count -= 4) >= 0)
|
| - {
|
| - fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
|
| - fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
|
| - fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
|
| - fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
|
| - }
|
| - SkASSERT(count <= -1 && count >= -4);
|
| - count += 4;
|
| - while (--count >= 0)
|
| - {
|
| - fi = fx >> 8;
|
| - SkASSERT(fi <= 0xFF);
|
| - fx += dx;
|
| - *dstC++ = cache[fi];
|
| - }
|
| - }
|
| - else
|
| -#endif
|
| - do {
|
| - unsigned fi = SkClampMax(fx >> 8, 0xFF);
|
| - SkASSERT(fi <= 0xFF);
|
| - fx += dx;
|
| - *dstC++ = cache[fi];
|
| - } while (--count != 0);
|
| - }
|
| - else if (proc == mirror_tileproc)
|
| - {
|
| - do {
|
| - unsigned fi = mirror_8bits(fx >> 8);
|
| - SkASSERT(fi <= 0xFF);
|
| - fx += dx;
|
| - *dstC++ = cache[fi];
|
| - } while (--count != 0);
|
| - }
|
| - else
|
| - {
|
| - SkASSERT(proc == repeat_tileproc);
|
| - do {
|
| - unsigned fi = repeat_8bits(fx >> 8);
|
| - SkASSERT(fi <= 0xFF);
|
| - fx += dx;
|
| - *dstC++ = cache[fi];
|
| - } while (--count != 0);
|
| - }
|
| - }
|
| - else
|
| - {
|
| - SkScalar dstX = SkIntToScalar(x);
|
| - SkScalar dstY = SkIntToScalar(y);
|
| - do {
|
| - dstProc(fDstToIndex, dstX, dstY, &srcPt);
|
| - unsigned fi = proc(SkScalarToFixed(srcPt.fX));
|
| - SkASSERT(fi <= 0xFFFF);
|
| - *dstC++ = cache[fi >> (16 - kCache32Bits)];
|
| - dstX += SK_Scalar1;
|
| - } while (--count != 0);
|
| - }
|
| -}
|
| -
|
| -bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
|
| - TileMode xy[]) {
|
| - if (bitmap) {
|
| - bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
|
| - bitmap->allocPixels(); // share with shader???
|
| - memcpy(bitmap->getPixels(), this->getCache32(), kCache32Count * 4);
|
| - }
|
| - if (matrix) {
|
| - matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
|
| - matrix->preConcat(fPtsToUnit);
|
| - }
|
| - if (xy) {
|
| - xy[0] = fTileMode;
|
| - xy[1] = kClamp_TileMode;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -#ifdef TEST_GRADIENT_DITHER
|
| -static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count)
|
| -{
|
| - if ((unsigned)dst & 2)
|
| - {
|
| - *dst++ = value;
|
| - count -= 1;
|
| - SkTSwap(value, other);
|
| - }
|
| -
|
| - sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
|
| -
|
| - if (count & 1)
|
| - dst[count - 1] = value;
|
| -}
|
| -#endif
|
| -
|
| -void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
|
| -{
|
| - SkASSERT(count > 0);
|
| -
|
| - SkPoint srcPt;
|
| - SkMatrix::MapXYProc dstProc = fDstToIndexProc;
|
| - TileProc proc = fTileProc;
|
| - const uint16_t* cache = this->getCache16();
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - int toggle = ((x ^ y) & 1) << kCache16Bits;
|
| -#endif
|
| -
|
| - if (fDstToIndexClass != kPerspective_MatrixClass)
|
| - {
|
| - dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
|
| - SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
|
| -
|
| - if (fDstToIndexClass == kFixedStepInX_MatrixClass)
|
| - {
|
| - SkFixed dxStorage[1];
|
| - (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
|
| - dx = dxStorage[0];
|
| - }
|
| - else
|
| - {
|
| - SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
|
| - dx = SkScalarToFixed(fDstToIndex.getScaleX());
|
| - }
|
| -
|
| - if (SkFixedNearlyZero(dx)) // we're a vertical gradient, so no change in a span
|
| - {
|
| - unsigned fi = proc(fx) >> 10;
|
| - SkASSERT(fi <= 63);
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
|
| -#else
|
| - sk_memset16(dstC, cache[fi], count);
|
| -#endif
|
| - }
|
| - else if (proc == clamp_tileproc)
|
| - {
|
| - do {
|
| - unsigned fi = SkClampMax(fx >> 10, 63);
|
| - SkASSERT(fi <= 63);
|
| - fx += dx;
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - *dstC++ = cache[toggle + fi];
|
| - toggle ^= (1 << kCache16Bits);
|
| -#else
|
| - *dstC++ = cache[fi];
|
| -#endif
|
| - } while (--count != 0);
|
| - }
|
| - else if (proc == mirror_tileproc)
|
| - {
|
| - do {
|
| - unsigned fi = mirror_6bits(fx >> 10);
|
| - SkASSERT(fi <= 0x3F);
|
| - fx += dx;
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - *dstC++ = cache[toggle + fi];
|
| - toggle ^= (1 << kCache16Bits);
|
| -#else
|
| - *dstC++ = cache[fi];
|
| -#endif
|
| - } while (--count != 0);
|
| - }
|
| - else
|
| - {
|
| - SkASSERT(proc == repeat_tileproc);
|
| - do {
|
| - unsigned fi = repeat_6bits(fx >> 10);
|
| - SkASSERT(fi <= 0x3F);
|
| - fx += dx;
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - *dstC++ = cache[toggle + fi];
|
| - toggle ^= (1 << kCache16Bits);
|
| -#else
|
| - *dstC++ = cache[fi];
|
| -#endif
|
| - } while (--count != 0);
|
| - }
|
| - }
|
| - else
|
| - {
|
| - SkScalar dstX = SkIntToScalar(x);
|
| - SkScalar dstY = SkIntToScalar(y);
|
| - do {
|
| - dstProc(fDstToIndex, dstX, dstY, &srcPt);
|
| - unsigned fi = proc(SkScalarToFixed(srcPt.fX));
|
| - SkASSERT(fi <= 0xFFFF);
|
| -
|
| - int index = fi >> (16 - kCache16Bits);
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - *dstC++ = cache[toggle + index];
|
| - toggle ^= (1 << kCache16Bits);
|
| -#else
|
| - *dstC++ = cache[index];
|
| -#endif
|
| -
|
| - dstX += SK_Scalar1;
|
| - } while (--count != 0);
|
| - }
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| -#define kSQRT_TABLE_BITS 11
|
| -#define kSQRT_TABLE_SIZE (1 << kSQRT_TABLE_BITS)
|
| -
|
| -#include "SkRadialGradient_Table.h"
|
| -
|
| -#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
|
| -
|
| -#include <stdio.h>
|
| -
|
| -void SkRadialGradient_BuildTable()
|
| -{
|
| - // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
|
| -
|
| - FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
|
| - SkASSERT(file);
|
| - ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
|
| -
|
| - for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
|
| - {
|
| - if ((i & 15) == 0)
|
| - ::fprintf(file, "\t");
|
| -
|
| - uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
|
| -
|
| - ::fprintf(file, "0x%02X", value);
|
| - if (i < kSQRT_TABLE_SIZE-1)
|
| - ::fprintf(file, ", ");
|
| - if ((i & 15) == 15)
|
| - ::fprintf(file, "\n");
|
| - }
|
| - ::fprintf(file, "};\n");
|
| - ::fclose(file);
|
| -}
|
| -
|
| -#endif
|
| -
|
| -
|
| -static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
|
| -{
|
| - SkScalar inv = SkScalarInvert(radius);
|
| -
|
| - matrix->setTranslate(-center.fX, -center.fY);
|
| - matrix->postScale(inv, inv);
|
| -}
|
| -
|
| -class Radial_Gradient : public Gradient_Shader {
|
| -public:
|
| - Radial_Gradient(const SkPoint& center, SkScalar radius,
|
| - const SkColor colors[], const SkScalar pos[], int colorCount,
|
| - SkShader::TileMode mode, SkUnitMapper* mapper)
|
| - : Gradient_Shader(colors, pos, colorCount, mode, mapper)
|
| - {
|
| - // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
|
| - SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
|
| -
|
| - rad_to_unit_matrix(center, radius, &fPtsToUnit);
|
| - }
|
| - virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
|
| - {
|
| - SkASSERT(count > 0);
|
| -
|
| - SkPoint srcPt;
|
| - SkMatrix::MapXYProc dstProc = fDstToIndexProc;
|
| - TileProc proc = fTileProc;
|
| - const SkPMColor* cache = this->getCache32();
|
| -
|
| - if (fDstToIndexClass != kPerspective_MatrixClass)
|
| - {
|
| - dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
|
| - SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
|
| - SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
|
| -
|
| - if (fDstToIndexClass == kFixedStepInX_MatrixClass)
|
| - {
|
| - SkFixed storage[2];
|
| - (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
|
| - dx = storage[0];
|
| - dy = storage[1];
|
| - }
|
| - else
|
| - {
|
| - SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
|
| - dx = SkScalarToFixed(fDstToIndex.getScaleX());
|
| - dy = SkScalarToFixed(fDstToIndex.getSkewY());
|
| - }
|
| -
|
| - if (proc == clamp_tileproc)
|
| - {
|
| - const uint8_t* sqrt_table = gSqrt8Table;
|
| - fx >>= 1;
|
| - dx >>= 1;
|
| - fy >>= 1;
|
| - dy >>= 1;
|
| - do {
|
| - unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
|
| - unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
|
| - fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
|
| - fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
|
| - *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
|
| - fx += dx;
|
| - fy += dy;
|
| - } while (--count != 0);
|
| - }
|
| - else if (proc == mirror_tileproc)
|
| - {
|
| - do {
|
| - SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
|
| - unsigned fi = mirror_tileproc(dist);
|
| - SkASSERT(fi <= 0xFFFF);
|
| - *dstC++ = cache[fi >> (16 - kCache32Bits)];
|
| - fx += dx;
|
| - fy += dy;
|
| - } while (--count != 0);
|
| - }
|
| - else
|
| - {
|
| - SkASSERT(proc == repeat_tileproc);
|
| - do {
|
| - SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
|
| - unsigned fi = repeat_tileproc(dist);
|
| - SkASSERT(fi <= 0xFFFF);
|
| - *dstC++ = cache[fi >> (16 - kCache32Bits)];
|
| - fx += dx;
|
| - fy += dy;
|
| - } while (--count != 0);
|
| - }
|
| - }
|
| - else // perspective case
|
| - {
|
| - SkScalar dstX = SkIntToScalar(x);
|
| - SkScalar dstY = SkIntToScalar(y);
|
| - do {
|
| - dstProc(fDstToIndex, dstX, dstY, &srcPt);
|
| - unsigned fi = proc(SkScalarToFixed(srcPt.length()));
|
| - SkASSERT(fi <= 0xFFFF);
|
| - *dstC++ = cache[fi >> (16 - kCache32Bits)];
|
| - dstX += SK_Scalar1;
|
| - } while (--count != 0);
|
| - }
|
| - }
|
| - virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
|
| - {
|
| - SkASSERT(count > 0);
|
| -
|
| - SkPoint srcPt;
|
| - SkMatrix::MapXYProc dstProc = fDstToIndexProc;
|
| - TileProc proc = fTileProc;
|
| - const uint16_t* cache = this->getCache16();
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - int toggle = ((x ^ y) & 1) << kCache16Bits;
|
| -#endif
|
| -
|
| - if (fDstToIndexClass != kPerspective_MatrixClass)
|
| - {
|
| - dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
|
| - SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
|
| - SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
|
| -
|
| - if (fDstToIndexClass == kFixedStepInX_MatrixClass)
|
| - {
|
| - SkFixed storage[2];
|
| - (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
|
| - dx = storage[0];
|
| - dy = storage[1];
|
| - }
|
| - else
|
| - {
|
| - SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
|
| - dx = SkScalarToFixed(fDstToIndex.getScaleX());
|
| - dy = SkScalarToFixed(fDstToIndex.getSkewY());
|
| - }
|
| -
|
| - if (proc == clamp_tileproc)
|
| - {
|
| - const uint8_t* sqrt_table = gSqrt8Table;
|
| -
|
| - /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
|
| - rather than 0xFFFF which is slower. This is a compromise, since it reduces our
|
| - precision, but that appears to be visually OK. If we decide this is OK for
|
| - all of our cases, we could (it seems) put this scale-down into fDstToIndex,
|
| - to avoid having to do these extra shifts each time.
|
| - */
|
| - fx >>= 1;
|
| - dx >>= 1;
|
| - fy >>= 1;
|
| - dy >>= 1;
|
| - if (dy == 0) // might perform this check for the other modes, but the win will be a smaller % of the total
|
| - {
|
| - fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
|
| - fy *= fy;
|
| - do {
|
| - unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
|
| - unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
|
| - fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
|
| - fx += dx;
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
|
| - toggle ^= (1 << kCache16Bits);
|
| -#else
|
| - *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
|
| -#endif
|
| - } while (--count != 0);
|
| - }
|
| - else
|
| - {
|
| - do {
|
| - unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
|
| - unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
|
| - fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
|
| - fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
|
| - fx += dx;
|
| - fy += dy;
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
|
| - toggle ^= (1 << kCache16Bits);
|
| -#else
|
| - *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
|
| -#endif
|
| - } while (--count != 0);
|
| - }
|
| - }
|
| - else if (proc == mirror_tileproc)
|
| - {
|
| - do {
|
| - SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
|
| - unsigned fi = mirror_tileproc(dist);
|
| - SkASSERT(fi <= 0xFFFF);
|
| - fx += dx;
|
| - fy += dy;
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
|
| - toggle ^= (1 << kCache16Bits);
|
| -#else
|
| - *dstC++ = cache[fi >> (16 - kCache16Bits)];
|
| -#endif
|
| - } while (--count != 0);
|
| - }
|
| - else
|
| - {
|
| - SkASSERT(proc == repeat_tileproc);
|
| - do {
|
| - SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
|
| - unsigned fi = repeat_tileproc(dist);
|
| - SkASSERT(fi <= 0xFFFF);
|
| - fx += dx;
|
| - fy += dy;
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
|
| - toggle ^= (1 << kCache16Bits);
|
| -#else
|
| - *dstC++ = cache[fi >> (16 - kCache16Bits)];
|
| -#endif
|
| - } while (--count != 0);
|
| - }
|
| - }
|
| - else // perspective case
|
| - {
|
| - SkScalar dstX = SkIntToScalar(x);
|
| - SkScalar dstY = SkIntToScalar(y);
|
| - do {
|
| - dstProc(fDstToIndex, dstX, dstY, &srcPt);
|
| - unsigned fi = proc(SkScalarToFixed(srcPt.length()));
|
| - SkASSERT(fi <= 0xFFFF);
|
| -
|
| - int index = fi >> (16 - kCache16Bits);
|
| -#ifdef TEST_GRADIENT_DITHER
|
| - *dstC++ = cache[toggle + index];
|
| - toggle ^= (1 << kCache16Bits);
|
| -#else
|
| - *dstC++ = cache[index];
|
| -#endif
|
| -
|
| - dstX += SK_Scalar1;
|
| - } while (--count != 0);
|
| - }
|
| - }
|
| -
|
| - static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
|
| - return SkNEW_ARGS(Radial_Gradient, (buffer));
|
| - }
|
| -
|
| -protected:
|
| - Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
|
| - virtual Factory getFactory() { return CreateProc; }
|
| -
|
| -private:
|
| - typedef Gradient_Shader INHERITED;
|
| -};
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| -class Sweep_Gradient : public Gradient_Shader {
|
| -public:
|
| - Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
|
| - const SkScalar pos[], int count, SkUnitMapper* mapper)
|
| - : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
|
| - {
|
| - fPtsToUnit.setTranslate(-cx, -cy);
|
| - }
|
| - virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
|
| - virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
|
| -
|
| - static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
|
| - return SkNEW_ARGS(Sweep_Gradient, (buffer));
|
| - }
|
| -
|
| -protected:
|
| - Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
|
| -
|
| - virtual Factory getFactory() { return CreateProc; }
|
| -
|
| -private:
|
| - typedef Gradient_Shader INHERITED;
|
| -};
|
| -
|
| -#ifdef COMPUTE_SWEEP_TABLE
|
| -#define PI 3.14159265
|
| -static bool gSweepTableReady;
|
| -static uint8_t gSweepTable[65];
|
| -
|
| -/* Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
|
| - We scale the results to [0..32]
|
| -*/
|
| -static const uint8_t* build_sweep_table()
|
| -{
|
| - if (!gSweepTableReady)
|
| - {
|
| - const int N = 65;
|
| - const double DENOM = N - 1;
|
| -
|
| - for (int i = 0; i < N; i++)
|
| - {
|
| - double arg = i / DENOM;
|
| - double v = atan(arg);
|
| - int iv = (int)round(v * DENOM * 2 / PI);
|
| -// printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
|
| - printf("%d, ", iv);
|
| - gSweepTable[i] = iv;
|
| - }
|
| - gSweepTableReady = true;
|
| - }
|
| - return gSweepTable;
|
| -}
|
| -#else
|
| -static const uint8_t gSweepTable[] = {
|
| - 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
|
| - 10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
|
| - 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
|
| - 26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
|
| - 32
|
| -};
|
| -static const uint8_t* build_sweep_table() { return gSweepTable; }
|
| -#endif
|
| -
|
| -// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
|
| -// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
|
| -// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
|
| -
|
| -//unsigned div_64(int numer, int denom);
|
| -static unsigned div_64(int numer, int denom)
|
| -{
|
| - SkASSERT(numer <= denom);
|
| - SkASSERT(numer > 0);
|
| - SkASSERT(denom > 0);
|
| -
|
| - int nbits = SkCLZ(numer);
|
| - int dbits = SkCLZ(denom);
|
| - int bits = 6 - nbits + dbits;
|
| - SkASSERT(bits <= 6);
|
| -
|
| - if (bits < 0) // detect underflow
|
| - return 0;
|
| -
|
| - denom <<= dbits - 1;
|
| - numer <<= nbits - 1;
|
| -
|
| - unsigned result = 0;
|
| -
|
| - // do the first one
|
| - if ((numer -= denom) >= 0)
|
| - result = 1;
|
| - else
|
| - numer += denom;
|
| -
|
| - // Now fall into our switch statement if there are more bits to compute
|
| - if (bits > 0)
|
| - {
|
| - // make room for the rest of the answer bits
|
| - result <<= bits;
|
| - switch (bits) {
|
| - case 6:
|
| - if ((numer = (numer << 1) - denom) >= 0)
|
| - result |= 32;
|
| - else
|
| - numer += denom;
|
| - case 5:
|
| - if ((numer = (numer << 1) - denom) >= 0)
|
| - result |= 16;
|
| - else
|
| - numer += denom;
|
| - case 4:
|
| - if ((numer = (numer << 1) - denom) >= 0)
|
| - result |= 8;
|
| - else
|
| - numer += denom;
|
| - case 3:
|
| - if ((numer = (numer << 1) - denom) >= 0)
|
| - result |= 4;
|
| - else
|
| - numer += denom;
|
| - case 2:
|
| - if ((numer = (numer << 1) - denom) >= 0)
|
| - result |= 2;
|
| - else
|
| - numer += denom;
|
| - case 1:
|
| - default: // not strictly need, but makes GCC make better ARM code
|
| - if ((numer = (numer << 1) - denom) >= 0)
|
| - result |= 1;
|
| - else
|
| - numer += denom;
|
| - }
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
|
| -static unsigned atan_0_90(SkFixed y, SkFixed x)
|
| -{
|
| -#ifdef SK_DEBUG
|
| - {
|
| - static bool gOnce;
|
| - if (!gOnce)
|
| - {
|
| - gOnce = true;
|
| - SkASSERT(div_64(55, 55) == 64);
|
| - SkASSERT(div_64(128, 256) == 32);
|
| - SkASSERT(div_64(2326528, 4685824) == 31);
|
| - SkASSERT(div_64(753664, 5210112) == 9);
|
| - SkASSERT(div_64(229376, 4882432) == 3);
|
| - SkASSERT(div_64(2, 64) == 2);
|
| - SkASSERT(div_64(1, 64) == 1);
|
| - // test that we handle underflow correctly
|
| - SkASSERT(div_64(12345, 0x54321234) == 0);
|
| - }
|
| - }
|
| -#endif
|
| -
|
| - SkASSERT(y > 0 && x > 0);
|
| - const uint8_t* table = build_sweep_table();
|
| -
|
| - unsigned result;
|
| - bool swap = (x < y);
|
| - if (swap)
|
| - {
|
| - // first part of the atan(v) = PI/2 - atan(1/v) identity
|
| - // since our div_64 and table want v <= 1, where v = y/x
|
| - SkTSwap<SkFixed>(x, y);
|
| - }
|
| -
|
| - result = div_64(y, x);
|
| -
|
| -#ifdef SK_DEBUG
|
| - {
|
| - unsigned result2 = SkDivBits(y, x, 6);
|
| - SkASSERT(result2 == result ||
|
| - (result == 1 && result2 == 0));
|
| - }
|
| -#endif
|
| -
|
| - SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
|
| - result = table[result];
|
| -
|
| - if (swap)
|
| - {
|
| - // complete the atan(v) = PI/2 - atan(1/v) identity
|
| - result = 64 - result;
|
| - // pin to 63
|
| - result -= result >> 6;
|
| - }
|
| -
|
| - SkASSERT(result <= 63);
|
| - return result;
|
| -}
|
| -
|
| -// returns angle in a circle [0..2PI) -> [0..255]
|
| -static unsigned SkATan2_255(SkFixed y, SkFixed x)
|
| -{
|
| - if (x == 0)
|
| - {
|
| - if (y == 0)
|
| - return 0;
|
| - return y < 0 ? 192 : 64;
|
| - }
|
| - if (y == 0)
|
| - return x < 0 ? 128 : 0;
|
| -
|
| - /* Find the right quadrant for x,y
|
| - Since atan_0_90 only handles the first quadrant, we rotate x,y
|
| - appropriately before calling it, and then add the right amount
|
| - to account for the real quadrant.
|
| - quadrant 0 : add 0 | x > 0 && y > 0
|
| - quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0
|
| - quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0
|
| - quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0
|
| -
|
| - map x<0 to (1 << 6)
|
| - map y<0 to (3 << 6)
|
| - add = map_x ^ map_y
|
| - */
|
| - int xsign = x >> 31;
|
| - int ysign = y >> 31;
|
| - int add = ((-xsign) ^ (ysign & 3)) << 6;
|
| -
|
| -#ifdef SK_DEBUG
|
| - if (0 == add)
|
| - SkASSERT(x > 0 && y > 0);
|
| - else if (64 == add)
|
| - SkASSERT(x < 0 && y > 0);
|
| - else if (128 == add)
|
| - SkASSERT(x < 0 && y < 0);
|
| - else if (192 == add)
|
| - SkASSERT(x > 0 && y < 0);
|
| - else
|
| - SkASSERT(!"bad value for add");
|
| -#endif
|
| -
|
| - /* This ^ trick makes x, y positive, and the swap<> handles quadrants
|
| - where we need to rotate x,y by 90 or -90
|
| - */
|
| - x = (x ^ xsign) - xsign;
|
| - y = (y ^ ysign) - ysign;
|
| - if (add & 64) // quads 1 or 3 need to swap x,y
|
| - SkTSwap<SkFixed>(x, y);
|
| -
|
| - unsigned result = add + atan_0_90(y, x);
|
| - SkASSERT(result < 256);
|
| - return result;
|
| -}
|
| -
|
| -void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
|
| -{
|
| - SkMatrix::MapXYProc proc = fDstToIndexProc;
|
| - const SkMatrix& matrix = fDstToIndex;
|
| - const SkPMColor* cache = this->getCache32();
|
| - SkPoint srcPt;
|
| -
|
| - if (fDstToIndexClass != kPerspective_MatrixClass)
|
| - {
|
| - proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
|
| - SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
|
| - SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
|
| - SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
|
| -
|
| - if (fDstToIndexClass == kFixedStepInX_MatrixClass)
|
| - {
|
| - SkFixed storage[2];
|
| - (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
|
| - &storage[0], &storage[1]);
|
| - dx = storage[0];
|
| - dy = storage[1];
|
| - }
|
| - else
|
| - {
|
| - SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
|
| - dx = SkScalarToFixed(matrix.getScaleX());
|
| - dy = SkScalarToFixed(matrix.getSkewY());
|
| - }
|
| -
|
| - for (; count > 0; --count)
|
| - {
|
| - *dstC++ = cache[SkATan2_255(fy, fx)];
|
| - fx += dx;
|
| - fy += dy;
|
| - }
|
| - }
|
| - else // perspective case
|
| - {
|
| - for (int stop = x + count; x < stop; x++)
|
| - {
|
| - proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
|
| - SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
|
| -
|
| - int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
|
| - SkScalarToFixed(srcPt.fX));
|
| - *dstC++ = cache[index];
|
| - }
|
| - }
|
| -}
|
| -
|
| -void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
|
| -{
|
| - SkMatrix::MapXYProc proc = fDstToIndexProc;
|
| - const SkMatrix& matrix = fDstToIndex;
|
| - const uint16_t* cache = this->getCache16();
|
| - int toggle = ((x ^ y) & 1) << kCache16Bits;
|
| - SkPoint srcPt;
|
| -
|
| - if (fDstToIndexClass != kPerspective_MatrixClass)
|
| - {
|
| - proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
|
| - SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
|
| - SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
|
| - SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
|
| -
|
| - if (fDstToIndexClass == kFixedStepInX_MatrixClass)
|
| - {
|
| - SkFixed storage[2];
|
| - (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
|
| - &storage[0], &storage[1]);
|
| - dx = storage[0];
|
| - dy = storage[1];
|
| - }
|
| - else
|
| - {
|
| - SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
|
| - dx = SkScalarToFixed(matrix.getScaleX());
|
| - dy = SkScalarToFixed(matrix.getSkewY());
|
| - }
|
| -
|
| - for (; count > 0; --count)
|
| - {
|
| - int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
|
| - *dstC++ = cache[toggle + index];
|
| - toggle ^= (1 << kCache16Bits);
|
| - fx += dx;
|
| - fy += dy;
|
| - }
|
| - }
|
| - else // perspective case
|
| - {
|
| - for (int stop = x + count; x < stop; x++)
|
| - {
|
| - proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
|
| - SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
|
| -
|
| - int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
|
| - SkScalarToFixed(srcPt.fX));
|
| - index >>= (8 - kCache16Bits);
|
| - *dstC++ = cache[toggle + index];
|
| - toggle ^= (1 << kCache16Bits);
|
| - }
|
| - }
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////
|
| -///////////////////////////////////////////////////////////////////////////
|
| -
|
| -// assumes colors is SkColor* and pos is SkScalar*
|
| -#define EXPAND_1_COLOR(count) \
|
| - SkColor tmp[2]; \
|
| - do { \
|
| - if (1 == count) { \
|
| - tmp[0] = tmp[1] = colors[0]; \
|
| - colors = tmp; \
|
| - pos = NULL; \
|
| - count = 2; \
|
| - } \
|
| - } while (0)
|
| -
|
| -SkShader* SkGradientShader::CreateLinear( const SkPoint pts[2],
|
| - const SkColor colors[], const SkScalar pos[], int colorCount,
|
| - SkShader::TileMode mode, SkUnitMapper* mapper)
|
| -{
|
| - if (NULL == pts || NULL == colors || colorCount < 1) {
|
| - return NULL;
|
| - }
|
| - EXPAND_1_COLOR(colorCount);
|
| -
|
| - return SkNEW_ARGS(Linear_Gradient, (pts, colors, pos, colorCount, mode, mapper));
|
| -}
|
| -
|
| -SkShader* SkGradientShader::CreateRadial( const SkPoint& center, SkScalar radius,
|
| - const SkColor colors[], const SkScalar pos[], int colorCount,
|
| - SkShader::TileMode mode, SkUnitMapper* mapper)
|
| -{
|
| - if (radius <= 0 || NULL == colors || colorCount < 1) {
|
| - return NULL;
|
| - }
|
| - EXPAND_1_COLOR(colorCount);
|
| -
|
| - return SkNEW_ARGS(Radial_Gradient, (center, radius, colors, pos, colorCount, mode, mapper));
|
| -}
|
| -
|
| -SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
|
| - const SkColor colors[],
|
| - const SkScalar pos[],
|
| - int count, SkUnitMapper* mapper)
|
| -{
|
| - if (NULL == colors || count < 1) {
|
| - return NULL;
|
| - }
|
| - EXPAND_1_COLOR(count);
|
| -
|
| - return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
|
| -}
|
| -
|
| -static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
|
| - Linear_Gradient::CreateProc);
|
| -
|
| -static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
|
| - Radial_Gradient::CreateProc);
|
| -
|
| -static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
|
| - Sweep_Gradient::CreateProc);
|
| -
|
|
|