Bug 755869 - [11] Re-apply bug 687188 - Skia radial gradients should use the 0/1 color stop values for clamping. r=mattwoodrow
authorGeorge Wright <gwright@mozilla.com>
Wed, 23 May 2012 11:40:25 -0400
changeset 99206 544c28fd16293341cc7187b90956f906d2d2f3dd
parent 99205 858fb563db5805444f5c7486353ee59dcf8fc644
child 99207 4b93ab161451d6530c52328dc6ace52609df201a
push idunknown
push userunknown
push dateunknown
reviewersmattwoodrow
bugs755869, 687188
milestone15.0a1
Bug 755869 - [11] Re-apply bug 687188 - Skia radial gradients should use the 0/1 color stop values for clamping. r=mattwoodrow
gfx/skia/src/effects/SkGradientShader.cpp
--- a/gfx/skia/src/effects/SkGradientShader.cpp
+++ b/gfx/skia/src/effects/SkGradientShader.cpp
@@ -199,16 +199,17 @@ private:
 
     mutable uint16_t*   fCache16;   // working ptr. If this is NULL, we need to recompute the cache values
     mutable SkPMColor*  fCache32;   // working ptr. If this is NULL, we need to recompute the cache values
 
     mutable uint16_t*   fCache16Storage;    // storage for fCache16, allocated on demand
     mutable SkMallocPixelRef* fCache32PixelRef;
     mutable unsigned    fCacheAlpha;        // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
 
+    static SkPMColor PremultiplyColor(SkColor c0, U8CPU alpha);
     static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
     static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
                                 U8CPU alpha);
     void setCacheAlpha(U8CPU alpha) const;
     void initCommon();
 
     typedef SkShader INHERITED;
 };
@@ -502,16 +503,31 @@ static inline U8CPU dither_fixed_to_8(Sk
  *  For dithering with premultiply, we want to ceiling the alpha component,
  *  to ensure that it is always >= any color component.
  */
 static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
     n >>= 8;
     return ((n << 1) - (n | (n >> 8))) >> 8;
 }
 
+SkPMColor Gradient_Shader::PremultiplyColor(SkColor c0, U8CPU paintAlpha)
+{
+    SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
+    SkFixed r = SkColorGetR(c0);
+    SkFixed g = SkColorGetG(c0);
+    SkFixed b = SkColorGetB(c0);
+
+    a = SkIntToFixed(a) + 0x8000;
+    r = SkIntToFixed(r) + 0x8000;
+    g = SkIntToFixed(g) + 0x8000;
+    b = SkIntToFixed(b) + 0x8000;
+
+    return SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
+}
+
 void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
                                       int count, U8CPU paintAlpha) {
     SkASSERT(count > 1);
 
     // need to apply paintAlpha to our two endpoints
     SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
     SkFixed da;
     {
@@ -623,24 +639,24 @@ const uint16_t* Gradient_Shader::getCach
 static void complete_32bit_cache(SkPMColor* cache, int stride) {
     cache[stride - 1] = cache[stride - 2];
     cache[2 * stride - 1] = cache[2 * stride - 2];
 }
 
 const SkPMColor* Gradient_Shader::getCache32() const {
     if (fCache32 == NULL) {
         // double the count for dither entries
-        const int entryCount = kCache32Count * 2;
+        const int entryCount = kCache32Count * 2 + 2;
         const size_t allocSize = sizeof(SkPMColor) * entryCount;
 
         if (NULL == fCache32PixelRef) {
             fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
                                           (NULL, allocSize, NULL));
         }
-        fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
+        fCache32 = (SkPMColor*)fCache32PixelRef->getAddr() + 1;
         if (fColorCount == 2) {
             Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
                             kGradient32Length, fCacheAlpha);
         } else {
             Rec* rec = fRecs;
             int prevIndex = 0;
             for (int i = 1; i < fColorCount; i++) {
                 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
@@ -654,29 +670,32 @@ const SkPMColor* Gradient_Shader::getCac
             }
             SkASSERT(prevIndex == kGradient32Length - 1);
         }
 
         if (fMapper) {
             SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
                                                  (NULL, allocSize, NULL));
             SkPMColor* linear = fCache32;           // just computed linear data
-            SkPMColor* mapped = (SkPMColor*)newPR->getAddr();    // storage for mapped data
+            SkPMColor* mapped = (SkPMColor*)newPR->getAddr() + 1;    // storage for mapped data
             SkUnitMapper* map = fMapper;
             for (int i = 0; i < kGradient32Length; i++) {
                 int index = map->mapUnit16((i << 8) | i) >> 8;
                 mapped[i] = linear[index];
                 mapped[i + kCache32Count] = linear[index + kCache32Count];
             }
             fCache32PixelRef->unref();
             fCache32PixelRef = newPR;
-            fCache32 = (SkPMColor*)newPR->getAddr();
+            fCache32 = (SkPMColor*)newPR->getAddr() + 1;
         }
         complete_32bit_cache(fCache32, kCache32Count);
     }
+    //Write the clamp colours into the first and last entries of fCache32
+    fCache32[-1] = PremultiplyColor(fOrigColors[0], fCacheAlpha);
+    fCache32[kCache32Count * 2] = PremultiplyColor(fOrigColors[fColorCount - 1], fCacheAlpha);
     return fCache32;
 }
 
 /*
  *  Because our caller might rebuild the same (logically the same) gradient
  *  over and over, we'd like to return exactly the same "bitmap" if possible,
  *  allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
  *  To do that, we maintain a private cache of built-bitmaps, based on our
@@ -852,16 +871,28 @@ typedef void (*LinearShadeProc)(TileProc
                                 int toggle, int count);
 
 // This function is deprecated, and will be replaced by 
 // shadeSpan_linear_vertical_lerp() once Chrome has been weaned off of it.
 void shadeSpan_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
                                SkPMColor* SK_RESTRICT dstC,
                                const SkPMColor* SK_RESTRICT cache,
                                int toggle, int count) {
+    if (proc == clamp_tileproc) {
+        // Read out clamp values from beginning/end of the cache. No need to lerp
+        // or dither
+        if (fx < 0) {
+            sk_memset32(dstC, cache[-1], count);
+            return;
+        } else if (fx > 0xFFFF) {
+            sk_memset32(dstC, cache[Gradient_Shader::kCache32Count * 2], count);
+            return;
+        }
+    }
+
     // 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 fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
     sk_memset32_dither(dstC,
             cache[toggle + fi],
             cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi],
@@ -870,16 +901,28 @@ void shadeSpan_linear_vertical(TileProc 
 
 // 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,
                                     SkPMColor* SK_RESTRICT dstC,
                                     const SkPMColor* SK_RESTRICT cache,
                                     int toggle, int count) {
+    if (proc == clamp_tileproc) {
+        // Read out clamp values from beginning/end of the cache. No need to lerp
+        // or dither
+        if (fx < 0) {
+            sk_memset32(dstC, cache[-1], count);
+            return;
+        } else if (fx > 0xFFFF) {
+            sk_memset32(dstC, cache[Gradient_Shader::kCache32Count * 2], count);
+            return;
+        }
+    }
+
     // 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 fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
     unsigned remainder = fullIndex & Gradient_Shader::kLerpRemainderMask32;
     SkPMColor lerp =
         SkFastFourByteInterp(
@@ -895,20 +938,18 @@ void shadeSpan_linear_vertical_lerp(Tile
 void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
                             SkPMColor* SK_RESTRICT dstC,
                             const SkPMColor* SK_RESTRICT cache,
                             int toggle, int count) {
     SkClampRange range;
     range.init(fx, dx, count, 0, Gradient_Shader::kGradient32Length);
 
     if ((count = range.fCount0) > 0) {
-        sk_memset32_dither(dstC,
-            cache[toggle + range.fV0],
-            cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV0],
-            count);
+        // Shouldn't be any need to dither for clamping?
+        sk_memset32(dstC, cache[-1], count);
         dstC += count;
     }
     if ((count = range.fCount1) > 0) {
         int unroll = count >> 3;
         fx = range.fFx1;
         for (int i = 0; i < unroll; i++) {
             NO_CHECK_ITER;  NO_CHECK_ITER;
             NO_CHECK_ITER;  NO_CHECK_ITER;
@@ -917,20 +958,18 @@ void shadeSpan_linear_clamp(TileProc pro
         }
         if ((count &= 7) > 0) {
             do {
                 NO_CHECK_ITER;
             } while (--count != 0);
         }
     }
     if ((count = range.fCount2) > 0) {
-        sk_memset32_dither(dstC,
-            cache[toggle + range.fV1],
-            cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV1],
-            count);
+        // Shouldn't be any need to dither for clamping?
+        sk_memset32(dstC, cache[Gradient_Shader::kCache32Count * 2], count);
     }
 }
 
 void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
                              SkPMColor* SK_RESTRICT dstC,
                              const SkPMColor* SK_RESTRICT cache,
                              int toggle, int count) {
     do {
@@ -1791,19 +1830,26 @@ void shadeSpan_twopoint_clamp(SkScalar f
         SkScalar fy, SkScalar dy,
         SkScalar b, SkScalar db,
         SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
         SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
         int count) {
     for (; count > 0; --count) {
         SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
                                      fOneOverTwoA, posRoot);
-        SkFixed index = SkClampMax(t, 0xFFFF);
-        SkASSERT(index <= 0xFFFF);
-        *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
+
+        if (t < 0) {
+            *dstC++ = cache[-1];
+        } else if (t > 0xFFFF) {
+            *dstC++ = cache[Gradient_Shader::kCache32Count * 2];
+        } else {
+            SkASSERT(t <= 0xFFFF);
+            *dstC++ = cache[t >> Gradient_Shader::kCache32Shift];
+        }
+
         fx += dx;
         fy += dy;
         b += db;
     }
 }
 void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
         SkScalar fy, SkScalar dy,
         SkScalar b, SkScalar db,