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));
 }