extend gradients to handle (by clamping) the first and last color position if
it is not at 0 or 1.
git-svn-id: http://skia.googlecode.com/svn/trunk@146 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp
index 9b5d922..df46a8f 100644
--- a/src/effects/SkGradientShader.cpp
+++ b/src/effects/SkGradientShader.cpp
@@ -36,18 +36,15 @@
typedef SkFixed (*TileProc)(SkFixed);
-static SkFixed clamp_tileproc(SkFixed x)
-{
+static SkFixed clamp_tileproc(SkFixed x) {
return SkClampMax(x, 0xFFFF);
}
-static SkFixed repeat_tileproc(SkFixed x)
-{
+static SkFixed repeat_tileproc(SkFixed x) {
return x & 0xFFFF;
}
-static inline SkFixed mirror_tileproc(SkFixed x)
-{
+static inline SkFixed mirror_tileproc(SkFixed x) {
int s = x << 15 >> 31;
return (x ^ s) & 0xFFFF;
}
@@ -60,13 +57,11 @@
//////////////////////////////////////////////////////////////////////////////
-static inline int repeat_6bits(int x)
-{
+static inline int repeat_6bits(int x) {
return x & 63;
}
-static inline int mirror_6bits(int x)
-{
+static inline int mirror_6bits(int x) {
#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
if (x & 64)
x = ~x;
@@ -77,16 +72,15 @@
#endif
}
-static inline int repeat_8bits(int x)
-{
+static inline int repeat_8bits(int x) {
return x & 0xFF;
}
-static inline int mirror_8bits(int x)
-{
+static inline int mirror_8bits(int x) {
#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
- if (x & 256)
+ if (x & 256) {
x = ~x;
+ }
return x & 255;
#else
int s = x << 23 >> 31;
@@ -99,7 +93,7 @@
class Gradient_Shader : public SkShader {
public:
Gradient_Shader(const SkColor colors[], const SkScalar pos[],
- int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
+ int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
virtual ~Gradient_Shader();
// overrides
@@ -115,7 +109,7 @@
SkPMColor* fARGB32;
TileMode fTileMode;
TileProc fTileProc;
- uint16_t fColorCount;
+ int fColorCount;
uint8_t fDstToIndexClass;
uint8_t fFlags;
@@ -154,8 +148,7 @@
typedef SkShader INHERITED;
};
-static inline unsigned scalarToU16(SkScalar x)
-{
+static inline unsigned scalarToU16(SkScalar x) {
SkASSERT(x >= 0 && x <= SK_Scalar1);
#ifdef SK_SCALAR_IS_FLOAT
@@ -165,9 +158,8 @@
#endif
}
-Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[], int colorCount,
- SkShader::TileMode mode, SkUnitMapper* mapper)
-{
+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
@@ -175,33 +167,67 @@
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];
+
+ fCache16 = fCache16Storage = NULL;
+ fCache32 = fCache32Storage = NULL;
- fRecs = (Rec*)(fARGB32 + colorCount);
- if (colorCount > 2)
+ /* Note: we let the caller skip the first and/or last position.
+ i.e. pos[0] = 0.3, pos[1] = 0.7
+ In these cases, we insert dummy entries to ensure that the final data
+ will be bracketed by [0, 1].
+ i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
+
+ Thus colorCount (the caller's value, and fColorCount (our value) may
+ differ by up to 2. In the above example:
+ colorCount = 2
+ fColorCount = 4
+ */
+ fColorCount = colorCount;
+ // check if we need to add in dummy start and/or end position/colors
+ bool dummyFirst = false;
+ bool dummyLast = false;
+ if (pos) {
+ dummyFirst = pos[0] != 0;
+ dummyLast = pos[colorCount - 1] != SK_Scalar1;
+ fColorCount += dummyFirst + dummyLast;
+ }
+
+ if (fColorCount > kColorStorageCount) {
+ size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
+ fOrigColors = reinterpret_cast<SkColor*>(
+ sk_malloc_throw(size * fColorCount));
+ }
+ else {
+ fOrigColors = fStorage;
+ }
+
+ // Now copy over the colors, adding the dummies as needed
{
- Rec* recs = fRecs;
+ SkColor* origColors = fOrigColors;
+ if (dummyFirst) {
+ *origColors++ = colors[0];
+ }
+ memcpy(origColors, colors, colorCount * sizeof(SkColor));
+ if (dummyLast) {
+ origColors += colorCount;
+ *origColors = colors[colorCount - 1];
+ }
+ }
- recs[0].fPos = 0;
- // recs[0].fScale = 0; // unused;
- if (pos)
- {
+ // our premul colors point to the 2nd half of the array
+ // these are assigned each time in setContext
+ fARGB32 = fOrigColors + fColorCount;
+ fRecs = (Rec*)(fARGB32 + fColorCount);
+ if (fColorCount > 2) {
+ Rec* recs = fRecs;
+ recs->fPos = 0;
+ // recs->fScale = 0; // unused;
+ recs += 1;
+ 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).
@@ -210,39 +236,40 @@
we start at 0 and end at 1.0
*/
SkFixed prev = 0;
- for (int i = 1; i < colorCount; i++)
- {
+ int startIndex = dummyFirst ? 0 : 1;
+ int count = colorCount + dummyLast;
+ for (int i = startIndex; i < count; i++) {
// force the last value to be 1.0
SkFixed curr;
- if (i == colorCount - 1)
+ if (i == colorCount) { // we're really at the dummyLast
curr = SK_Fixed1;
- else
- {
+ } 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
+ // pin curr withing range
+ if (curr < 0) {
+ curr = 0;
+ } else if (curr > SK_Fixed1) {
+ curr = SK_Fixed1;
+ }
+ recs->fPos = curr;
+ if (curr > prev) {
+ recs->fScale = (1 << 24) / (curr - prev);
+ } else {
+ recs->fScale = 0; // ignore this segment
+ }
// get ready for the next value
prev = curr;
+ recs += 1;
}
- }
- else // assume even distribution
- {
+ } 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;
+ for (int i = 1; i < colorCount; i++) {
+ recs->fPos = p;
+ recs->fScale = scale;
+ recs += 1;
p += dp;
}
}
@@ -250,8 +277,7 @@
}
Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
- INHERITED(buffer)
-{
+ INHERITED(buffer) {
fCacheAlpha = 256;
fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
@@ -259,11 +285,13 @@
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
+ int colorCount = fColorCount = buffer.readU32();
+ if (colorCount > kColorStorageCount) {
+ size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
+ fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
+ } else {
fOrigColors = fStorage;
+ }
buffer.read(fOrigColors, colorCount * sizeof(SkColor));
fARGB32 = fOrigColors + colorCount;
@@ -281,22 +309,23 @@
buffer.read(&fPtsToUnit, sizeof(SkMatrix));
}
-Gradient_Shader::~Gradient_Shader()
-{
- if (fCache16Storage)
+Gradient_Shader::~Gradient_Shader() {
+ if (fCache16Storage) {
sk_free(fCache16Storage);
- if (fCache32Storage)
+ }
+ if (fCache32Storage) {
sk_free(fCache32Storage);
- if (fOrigColors != fStorage)
+ }
+ if (fOrigColors != fStorage) {
sk_free(fOrigColors);
+ }
fMapper->safeUnref();
}
-void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer)
-{
+void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
this->INHERITED::flatten(buffer);
buffer.writeFlattenable(fMapper);
- buffer.write16(fColorCount);
+ buffer.write32(fColorCount);
buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
buffer.write8(fTileMode);
if (fColorCount > 2) {
@@ -311,10 +340,10 @@
bool Gradient_Shader::setContext(const SkBitmap& device,
const SkPaint& paint,
- const SkMatrix& matrix)
-{
- if (!this->INHERITED::setContext(device, paint, matrix))
+ const SkMatrix& matrix) {
+ if (!this->INHERITED::setContext(device, paint, matrix)) {
return false;
+ }
const SkMatrix& inverse = this->getTotalInverse();
@@ -329,7 +358,7 @@
unsigned paintAlpha = this->getPaintAlpha();
unsigned colorAlpha = 0xFF;
- for (unsigned i = 0; i < fColorCount; i++) {
+ for (int i = 0; i < fColorCount; i++) {
SkColor src = fOrigColors[i];
unsigned sa = SkColorGetA(src);
colorAlpha &= sa;
@@ -361,17 +390,15 @@
return true;
}
-static inline int blend8(int a, int b, int scale)
-{
+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)
-{
+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);
@@ -397,11 +424,12 @@
#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.
+/** 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)
-{
+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);
@@ -431,8 +459,8 @@
} while (--count != 0);
}
-static void build_32bit_cache(SkPMColor cache[], SkPMColor c0, SkPMColor c1, int count)
-{
+static void build_32bit_cache(SkPMColor cache[], SkPMColor c0, SkPMColor c1,
+ int count) {
SkASSERT(count > 1);
SkFixed a = SkGetPackedA32(c0);
@@ -459,22 +487,18 @@
} while (--count != 0);
}
-static inline int SkFixedToFFFF(SkFixed x)
-{
+static inline int SkFixedToFFFF(SkFixed x) {
SkASSERT((unsigned)x <= SK_Fixed1);
return x - (x >> 16);
}
-static inline U16CPU dot6to16(unsigned x)
-{
+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)
- {
+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);
@@ -482,14 +506,12 @@
fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
#endif
fCache16 = fCache16Storage;
- if (fColorCount == 2)
+ if (fColorCount == 2) {
build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
- else
- {
+ } else {
Rec* rec = fRecs;
int prevIndex = 0;
- for (unsigned i = 1; i < fColorCount; i++)
- {
+ for (int i = 1; i < fColorCount; i++) {
int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
SkASSERT(nextIndex < kCache16Count);
@@ -500,8 +522,7 @@
SkASSERT(prevIndex == kCache16Count - 1);
}
- if (fMapper)
- {
+ if (fMapper) {
#ifdef TEST_GRADIENT_DITHER
fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
#else
@@ -510,8 +531,7 @@
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++)
- {
+ for (int i = 0; i < 64; i++) {
int index = map->mapUnit16(dot6to16(i)) >> 10;
mapped[i] = linear[index];
#ifdef TEST_GRADIENT_DITHER
@@ -525,22 +545,18 @@
return fCache16;
}
-const SkPMColor* Gradient_Shader::getCache32()
-{
- if (fCache32 == NULL)
- {
+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)
+ if (fColorCount == 2) {
build_32bit_cache(fCache32, fARGB32[0], fARGB32[1], kCache32Count);
- else
- {
+ } else {
Rec* rec = fRecs;
int prevIndex = 0;
- for (unsigned i = 1; i < fColorCount; i++)
- {
+ for (int i = 1; i < fColorCount; i++) {
int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
SkASSERT(nextIndex < kCache32Count);
@@ -551,14 +567,14 @@
SkASSERT(prevIndex == kCache32Count - 1);
}
- if (fMapper)
- {
+ 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++)
+ for (int i = 0; i < 256; i++) {
mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
+ }
sk_free(fCache32);
fCache32 = fCache32Storage;
}
@@ -568,8 +584,7 @@
///////////////////////////////////////////////////////////////////////////
-static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix)
-{
+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;
@@ -1523,6 +1538,13 @@
}
EXPAND_1_COLOR(colorCount);
+ SkScalar posStorage[2];
+ if (colorCount == 2 && pos == NULL) {
+ posStorage[0] = SK_Scalar1/4;
+ posStorage[1] = 3*SK_Scalar1/4;
+ pos = posStorage;
+ }
+
return SkNEW_ARGS(Linear_Gradient, (pts, colors, pos, colorCount, mode, mapper));
}