improve precision of gradients (disabled for now)
authorreed <reed@google.com>
Thu, 18 Dec 2014 20:43:08 +0000 (12:43 -0800)
committerCommit bot <commit-bot@chromium.org>
Thu, 18 Dec 2014 20:43:08 +0000 (12:43 -0800)
BUG=skia:2898
TBR=caryclark

Review URL: https://codereview.chromium.org/815623004

gyp/skia_for_chromium_defines.gypi
src/effects/gradients/SkClampRange.cpp
src/effects/gradients/SkClampRange.h
src/effects/gradients/SkLinearGradient.cpp

index 6a9adf5a8ed0a212600b3538f131541c9d7d8d70..a21427003419dacd9c223ab176670e1daf1bb237 100644 (file)
@@ -13,6 +13,7 @@
     # If these become 'permanent', they should be moved into skia_common.gypi
     #
     'skia_for_chromium_defines': [
+      'SK_SUPPORT_LEGACY_GRADIENT_PRECISION',
     ],
   },
 }
index 398b02434fd531ac137d69abd8ab18aa84273c0a..cf302cd2a98068cec493c46f052173ad640fd09d 100644 (file)
@@ -13,7 +13,7 @@
  *  returns [0..count] for the number of steps (<= count) for which x0 <= edge
  *  given each step is followed by x0 += dx
  */
-static int chop(int64_t x0, SkFixed edge, int64_t x1, int64_t dx, int count) {
+static int chop(int64_t x0, SkGradFixed edge, int64_t x1, int64_t dx, int count) {
     SkASSERT(dx > 0);
     SkASSERT(count >= 0);
 
@@ -29,15 +29,17 @@ static int chop(int64_t x0, SkFixed edge, int64_t x1, int64_t dx, int count) {
     return (int)n;
 }
 
-static bool overflows_fixed(int64_t x) {
+#ifdef SK_SUPPORT_LEGACY_GRADIENT_PRECISION
+static bool overflows_gradfixed(int64_t x) {
     return x < -SK_FixedMax || x > SK_FixedMax;
 }
+#endif
 
-void SkClampRange::initFor1(SkFixed fx) {
+void SkClampRange::initFor1(SkGradFixed fx) {
     fCount0 = fCount1 = fCount2 = 0;
     if (fx <= 0) {
         fCount0 = 1;
-    } else if (fx < 0xFFFF) {
+    } else if (fx < kFracMax_SkGradFixed) {
         fCount1 = 1;
         fFx1 = fx;
     } else {
@@ -45,7 +47,7 @@ void SkClampRange::initFor1(SkFixed fx) {
     }
 }
 
-void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) {
+void SkClampRange::init(SkGradFixed fx0, SkGradFixed dx0, int count, int v0, int v1) {
     SkASSERT(count > 0);
 
     fV0 = v0;
@@ -60,10 +62,11 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) {
 
     int64_t fx = fx0;
     int64_t dx = dx0;
+
     // start with ex equal to the last computed value
     int64_t ex = fx + (count - 1) * dx;
 
-    if ((uint64_t)(fx | ex) <= 0xFFFF) {
+    if ((uint64_t)(fx | ex) <= kFracMax_SkGradFixed) {
         fCount0 = fCount2 = 0;
         fCount1 = count;
         fFx1 = fx0;
@@ -74,7 +77,7 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) {
         fCount0 = count;
         return;
     }
-    if (fx >= 0xFFFF && ex >= 0xFFFF) {
+    if (fx >= kFracMax_SkGradFixed && ex >= kFracMax_SkGradFixed) {
         fCount0 = fCount1 = 0;
         fCount2 = count;
         return;
@@ -84,8 +87,10 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) {
 
     // now make ex be 1 past the last computed value
     ex += dx;
+
+#ifdef SK_SUPPORT_LEGACY_GRADIENT_PRECISION
     // now check for over/under flow
-    if (overflows_fixed(ex)) {
+    if (overflows_gradfixed(ex)) {
         int originalCount = count;
         int64_t ccount;
         bool swap = dx < 0;
@@ -93,7 +98,13 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) {
             dx = -dx;
             fx = -fx;
         }
-        ccount = (SK_FixedMax - fx + dx - 1) / dx;
+
+        int shift = 0;
+        if (sizeof(SkGradFixed) == 8) {
+            shift = 16;
+        }
+
+        ccount = ((SK_FixedMax << shift) - fx + dx - 1) / dx;
         if (swap) {
             dx = -dx;
             fx = -fx;
@@ -113,6 +124,7 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) {
         extraCount = originalCount - count;
         ex = fx + dx * count;
     }
+#endif
 
     bool doSwap = dx < 0;
 
@@ -129,7 +141,7 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) {
     fx += fCount0 * dx;
     SkASSERT(fx >= 0);
     SkASSERT(fCount0 == 0 || (fx - dx) < 0);
-    fCount1 = chop(fx, 0xFFFF, ex, dx, count);
+    fCount1 = chop(fx, kFracMax_SkGradFixed, ex, dx, count);
     count -= fCount1;
     fCount2 = count;
 
@@ -137,9 +149,9 @@ void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) {
     fx += fCount1 * dx;
     SkASSERT(fx <= ex);
     if (fCount2 > 0) {
-        SkASSERT(fx >= 0xFFFF);
+        SkASSERT(fx >= kFracMax_SkGradFixed);
         if (fCount1 > 0) {
-            SkASSERT(fx - dx < 0xFFFF);
+            SkASSERT(fx - dx < kFracMax_SkGradFixed);
         }
     }
 #endif
index 09386d7e71bb7e9bf37c2eaede1f83885e35463f..a71009d65436aa72d82a3c2f3a88ea5faf29da00 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,14 +5,30 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkClampRange_DEFINED
 #define SkClampRange_DEFINED
 
 #include "SkFixed.h"
+#include "SkScalar.h"
+
+#define SK_SUPPORT_LEGACY_GRADIENT_PRECISION
+
+#ifdef SK_SUPPORT_LEGACY_GRADIENT_PRECISION
+    #define SkGradFixed             SkFixed
+    #define SkScalarToGradFixed     SkScalarToFixed
+    #define SkFixedToGradFixed(x)   (x)
+    #define SkGradFixedToFixed(x)   (x)
+    #define kFracMax_SkGradFixed    0xFFFF
+#else
+    #define SkGradFixed             SkFixed3232
+    #define SkScalarToGradFixed     SkScalarToFixed3232
+    #define SkFixedToGradFixed      SkFixedToFixed3232
+    #define SkGradFixedToFixed(x)   (SkFixed)((x) >> 16)
+    #define kFracMax_SkGradFixed    0xFFFFFFFFLL
+#endif
 
 /**
- *  Iteration fixed fx by dx, clamping as you go to [0..0xFFFF], this class
+ *  Iteration fixed fx by dx, clamping as you go to [0..kFracMax_SkGradFixed], this class
  *  computes the (up to) 3 spans there are:
  *
  *  range0: use constant value V0
@@ -24,14 +39,14 @@ struct SkClampRange {
     int fCount0;    // count for fV0
     int fCount1;    // count for interpolating (fV0...fV1)
     int fCount2;    // count for fV1
-    SkFixed fFx1;   // initial fx value for the fCount1 range.
+    SkGradFixed fFx1;   // initial fx value for the fCount1 range.
                     // only valid if fCount1 > 0
     int fV0, fV1;
 
-    void init(SkFixed fx, SkFixed dx, int count, int v0, int v1);
+    void init(SkGradFixed fx, SkGradFixed dx, int count, int v0, int v1);
 
 private:
-    void initFor1(SkFixed fx);
+    void initFor1(SkGradFixed fx);
 };
 
 #endif
index 4e7a6c729d5c014b89b319a45624f1040266c041..885a1b56c8e28d1b034cc726c465f80ffae73afa 100644 (file)
@@ -104,7 +104,7 @@ SkLinearGradient::LinearGradientContext::LinearGradientContext(
 
 #define NO_CHECK_ITER               \
     do {                            \
-    unsigned fi = fx >> SkGradientShaderBase::kCache32Shift; \
+    unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \
     SkASSERT(fi <= 0xFF);           \
     fx += dx;                       \
     *dstC++ = cache[toggle + fi];   \
@@ -113,21 +113,21 @@ SkLinearGradient::LinearGradientContext::LinearGradientContext(
 
 namespace {
 
-typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
+typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
                                 SkPMColor* dstC, const SkPMColor* cache,
                                 int toggle, int count);
 
 // 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,
+void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
                                     SkPMColor* SK_RESTRICT dstC,
                                     const SkPMColor* SK_RESTRICT cache,
                                     int toggle, int count) {
     // 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 fullIndex = proc(SkGradFixedToFixed(fx));
     unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
     unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
 
@@ -143,7 +143,7 @@ void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
     sk_memset32_dither(dstC, lerp, dlerp, count);
 }
 
-void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
+void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
                             SkPMColor* SK_RESTRICT dstC,
                             const SkPMColor* SK_RESTRICT cache,
                             int toggle, int count) {
@@ -180,12 +180,12 @@ void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
     }
 }
 
-void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
+void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
                              SkPMColor* SK_RESTRICT dstC,
                              const SkPMColor* SK_RESTRICT cache,
                              int toggle, int count) {
     do {
-        unsigned fi = mirror_8bits(fx >> 8);
+        unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8);
         SkASSERT(fi <= 0xFF);
         fx += dx;
         *dstC++ = cache[toggle + fi];
@@ -193,12 +193,12 @@ void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
     } while (--count != 0);
 }
 
-void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
+void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
         SkPMColor* SK_RESTRICT dstC,
         const SkPMColor* SK_RESTRICT cache,
         int toggle, int count) {
     do {
-        unsigned fi = repeat_8bits(fx >> 8);
+        unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8);
         SkASSERT(fi <= 0xFF);
         fx += dx;
         *dstC++ = cache[toggle + fi];
@@ -223,15 +223,16 @@ void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor*
     if (fDstToIndexClass != kPerspective_MatrixClass) {
         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+        SkGradFixed dx, fx = SkScalarToGradFixed(srcPt.fX);
 
         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
             SkFixed dxStorage[1];
             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
-            dx = dxStorage[0];
+            // todo: do we need a real/high-precision value for dx here?
+            dx = SkFixedToGradFixed(dxStorage[0]);
         } else {
             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            dx = SkScalarToFixed(fDstToIndex.getScaleX());
+            dx = SkScalarToGradFixed(fDstToIndex.getScaleX());
         }
 
         LinearShadeProc shadeProc = shadeSpan_linear_repeat;
@@ -301,7 +302,7 @@ static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
 
 #define NO_CHECK_ITER_16                \
     do {                                \
-    unsigned fi = fx >> SkGradientShaderBase::kCache16Shift;  \
+    unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift;  \
     SkASSERT(fi < SkGradientShaderBase::kCache16Count);       \
     fx += dx;                           \
     *dstC++ = cache[toggle + fi];       \
@@ -310,22 +311,22 @@ static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
 
 namespace {
 
-typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
+typedef void (*LinearShade16Proc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
                                   uint16_t* dstC, const uint16_t* cache,
                                   int toggle, int count);
 
-void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
+void shadeSpan16_linear_vertical(TileProc proc, SkGradFixed dx, SkGradFixed fx,
                                  uint16_t* SK_RESTRICT dstC,
                                  const uint16_t* SK_RESTRICT cache,
                                  int toggle, int count) {
     // we're a vertical gradient, so no change in a span
-    unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift;
+    unsigned fi = proc(SkGradFixedToFixed(fx)) >> SkGradientShaderBase::kCache16Shift;
     SkASSERT(fi < SkGradientShaderBase::kCache16Count);
     dither_memset16(dstC, cache[toggle + fi],
         cache[next_dither_toggle16(toggle) + fi], count);
 }
 
-void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
+void shadeSpan16_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
                               uint16_t* SK_RESTRICT dstC,
                               const uint16_t* SK_RESTRICT cache,
                               int toggle, int count) {
@@ -362,12 +363,12 @@ void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
     }
 }
 
-void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
+void shadeSpan16_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
                                uint16_t* SK_RESTRICT dstC,
                                const uint16_t* SK_RESTRICT cache,
                                int toggle, int count) {
     do {
-        unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift,
+        unsigned fi = mirror_bits(SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift,
                                         SkGradientShaderBase::kCache16Bits);
         SkASSERT(fi < SkGradientShaderBase::kCache16Count);
         fx += dx;
@@ -376,12 +377,12 @@ void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
     } while (--count != 0);
 }
 
-void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
+void shadeSpan16_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
                                uint16_t* SK_RESTRICT dstC,
                                const uint16_t* SK_RESTRICT cache,
                                int toggle, int count) {
     do {
-        unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift,
+        unsigned fi = repeat_bits(SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache16Shift,
                                   SkGradientShaderBase::kCache16Bits);
         SkASSERT(fi < SkGradientShaderBase::kCache16Count);
         fx += dx;
@@ -410,19 +411,20 @@ void SkLinearGradient::LinearGradientContext::shadeSpan16(int x, int y,
     if (fDstToIndexClass != kPerspective_MatrixClass) {
         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+        SkGradFixed dx, fx = SkScalarToGradFixed(srcPt.fX);
 
         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
             SkFixed dxStorage[1];
             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
-            dx = dxStorage[0];
+            // todo: do we need a real/high-precision value for dx here?
+            dx = SkFixedToGradFixed(dxStorage[0]);
         } else {
             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            dx = SkScalarToFixed(fDstToIndex.getScaleX());
+            dx = SkScalarToGradFixed(fDstToIndex.getScaleX());
         }
 
         LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
-        if (fixed_nearly_zero(dx)) {
+        if (fixed_nearly_zero(SkGradFixedToFixed(dx))) {
             shadeProc = shadeSpan16_linear_vertical;
         } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
             shadeProc = shadeSpan16_linear_clamp;