gfx/skia/trunk/src/effects/gradients/SkLinearGradient.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /*
     2  * Copyright 2012 Google Inc.
     3  *
     4  * Use of this source code is governed by a BSD-style license that can be
     5  * found in the LICENSE file.
     6  */
     8 #include "SkLinearGradient.h"
    10 static inline int repeat_bits(int x, const int bits) {
    11     return x & ((1 << bits) - 1);
    12 }
    14 static inline int repeat_8bits(int x) {
    15     return x & 0xFF;
    16 }
    18 // Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
    19 // See http://code.google.com/p/skia/issues/detail?id=472
    20 #if defined(_MSC_VER) && (_MSC_VER >= 1600)
    21 #pragma optimize("", off)
    22 #endif
    24 static inline int mirror_bits(int x, const int bits) {
    25     if (x & (1 << bits)) {
    26         x = ~x;
    27     }
    28     return x & ((1 << bits) - 1);
    29 }
    31 static inline int mirror_8bits(int x) {
    32     if (x & 256) {
    33         x = ~x;
    34     }
    35     return x & 255;
    36 }
    38 #if defined(_MSC_VER) && (_MSC_VER >= 1600)
    39 #pragma optimize("", on)
    40 #endif
    42 static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
    43     SkVector    vec = pts[1] - pts[0];
    44     SkScalar    mag = vec.length();
    45     SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
    47     vec.scale(inv);
    48     matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
    49     matrix->postTranslate(-pts[0].fX, -pts[0].fY);
    50     matrix->postScale(inv, inv);
    51 }
    53 ///////////////////////////////////////////////////////////////////////////////
    55 SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
    56     : SkGradientShaderBase(desc)
    57     , fStart(pts[0])
    58     , fEnd(pts[1]) {
    59     pts_to_unit_matrix(pts, &fPtsToUnit);
    60 }
    62 SkLinearGradient::SkLinearGradient(SkReadBuffer& buffer)
    63     : INHERITED(buffer)
    64     , fStart(buffer.readPoint())
    65     , fEnd(buffer.readPoint()) {
    66 }
    68 void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
    69     this->INHERITED::flatten(buffer);
    70     buffer.writePoint(fStart);
    71     buffer.writePoint(fEnd);
    72 }
    74 bool SkLinearGradient::setContext(const SkBitmap& device, const SkPaint& paint,
    75                                  const SkMatrix& matrix) {
    76     if (!this->INHERITED::setContext(device, paint, matrix)) {
    77         return false;
    78     }
    80     unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
    81     if ((fDstToIndex.getType() & ~mask) == 0) {
    82         // when we dither, we are (usually) not const-in-Y
    83         if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
    84             // only claim this if we do have a 16bit mode (i.e. none of our
    85             // colors have alpha), and if we are not dithering (which obviously
    86             // is not const in Y).
    87             fFlags |= SkShader::kConstInY16_Flag;
    88         }
    89     }
    90     return true;
    91 }
    93 #define NO_CHECK_ITER               \
    94     do {                            \
    95     unsigned fi = fx >> SkGradientShaderBase::kCache32Shift; \
    96     SkASSERT(fi <= 0xFF);           \
    97     fx += dx;                       \
    98     *dstC++ = cache[toggle + fi];   \
    99     toggle = next_dither_toggle(toggle); \
   100     } while (0)
   102 namespace {
   104 typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
   105                                 SkPMColor* dstC, const SkPMColor* cache,
   106                                 int toggle, int count);
   108 // Linear interpolation (lerp) is unnecessary if there are no sharp
   109 // discontinuities in the gradient - which must be true if there are
   110 // only 2 colors - but it's cheap.
   111 void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
   112                                     SkPMColor* SK_RESTRICT dstC,
   113                                     const SkPMColor* SK_RESTRICT cache,
   114                                     int toggle, int count) {
   115     // We're a vertical gradient, so no change in a span.
   116     // If colors change sharply across the gradient, dithering is
   117     // insufficient (it subsamples the color space) and we need to lerp.
   118     unsigned fullIndex = proc(fx);
   119     unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
   120     unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
   122     int index0 = fi + toggle;
   123     int index1 = index0;
   124     if (fi < SkGradientShaderBase::kCache32Count - 1) {
   125         index1 += 1;
   126     }
   127     SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
   128     index0 ^= SkGradientShaderBase::kDitherStride32;
   129     index1 ^= SkGradientShaderBase::kDitherStride32;
   130     SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
   131     sk_memset32_dither(dstC, lerp, dlerp, count);
   132 }
   134 void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
   135                             SkPMColor* SK_RESTRICT dstC,
   136                             const SkPMColor* SK_RESTRICT cache,
   137                             int toggle, int count) {
   138     SkClampRange range;
   139     range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
   141     if ((count = range.fCount0) > 0) {
   142         sk_memset32_dither(dstC,
   143             cache[toggle + range.fV0],
   144             cache[next_dither_toggle(toggle) + range.fV0],
   145             count);
   146         dstC += count;
   147     }
   148     if ((count = range.fCount1) > 0) {
   149         int unroll = count >> 3;
   150         fx = range.fFx1;
   151         for (int i = 0; i < unroll; i++) {
   152             NO_CHECK_ITER;  NO_CHECK_ITER;
   153             NO_CHECK_ITER;  NO_CHECK_ITER;
   154             NO_CHECK_ITER;  NO_CHECK_ITER;
   155             NO_CHECK_ITER;  NO_CHECK_ITER;
   156         }
   157         if ((count &= 7) > 0) {
   158             do {
   159                 NO_CHECK_ITER;
   160             } while (--count != 0);
   161         }
   162     }
   163     if ((count = range.fCount2) > 0) {
   164         sk_memset32_dither(dstC,
   165             cache[toggle + range.fV1],
   166             cache[next_dither_toggle(toggle) + range.fV1],
   167             count);
   168     }
   169 }
   171 void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
   172                              SkPMColor* SK_RESTRICT dstC,
   173                              const SkPMColor* SK_RESTRICT cache,
   174                              int toggle, int count) {
   175     do {
   176         unsigned fi = mirror_8bits(fx >> 8);
   177         SkASSERT(fi <= 0xFF);
   178         fx += dx;
   179         *dstC++ = cache[toggle + fi];
   180         toggle = next_dither_toggle(toggle);
   181     } while (--count != 0);
   182 }
   184 void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
   185         SkPMColor* SK_RESTRICT dstC,
   186         const SkPMColor* SK_RESTRICT cache,
   187         int toggle, int count) {
   188     do {
   189         unsigned fi = repeat_8bits(fx >> 8);
   190         SkASSERT(fi <= 0xFF);
   191         fx += dx;
   192         *dstC++ = cache[toggle + fi];
   193         toggle = next_dither_toggle(toggle);
   194     } while (--count != 0);
   195 }
   197 }
   199 void SkLinearGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
   200                                 int count) {
   201     SkASSERT(count > 0);
   203     SkPoint             srcPt;
   204     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
   205     TileProc            proc = fTileProc;
   206     const SkPMColor* SK_RESTRICT cache = this->getCache32();
   207     int                 toggle = init_dither_toggle(x, y);
   209     if (fDstToIndexClass != kPerspective_MatrixClass) {
   210         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
   211                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
   212         SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
   214         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
   215             SkFixed dxStorage[1];
   216             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
   217             dx = dxStorage[0];
   218         } else {
   219             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
   220             dx = SkScalarToFixed(fDstToIndex.getScaleX());
   221         }
   223         LinearShadeProc shadeProc = shadeSpan_linear_repeat;
   224         if (0 == dx) {
   225             shadeProc = shadeSpan_linear_vertical_lerp;
   226         } else if (SkShader::kClamp_TileMode == fTileMode) {
   227             shadeProc = shadeSpan_linear_clamp;
   228         } else if (SkShader::kMirror_TileMode == fTileMode) {
   229             shadeProc = shadeSpan_linear_mirror;
   230         } else {
   231             SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
   232         }
   233         (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
   234     } else {
   235         SkScalar    dstX = SkIntToScalar(x);
   236         SkScalar    dstY = SkIntToScalar(y);
   237         do {
   238             dstProc(fDstToIndex, dstX, dstY, &srcPt);
   239             unsigned fi = proc(SkScalarToFixed(srcPt.fX));
   240             SkASSERT(fi <= 0xFFFF);
   241             *dstC++ = cache[toggle + (fi >> kCache32Shift)];
   242             toggle = next_dither_toggle(toggle);
   243             dstX += SK_Scalar1;
   244         } while (--count != 0);
   245     }
   246 }
   248 SkShader::BitmapType SkLinearGradient::asABitmap(SkBitmap* bitmap,
   249                                                 SkMatrix* matrix,
   250                                                 TileMode xy[]) const {
   251     if (bitmap) {
   252         this->getGradientTableBitmap(bitmap);
   253     }
   254     if (matrix) {
   255         matrix->preConcat(fPtsToUnit);
   256     }
   257     if (xy) {
   258         xy[0] = fTileMode;
   259         xy[1] = kClamp_TileMode;
   260     }
   261     return kLinear_BitmapType;
   262 }
   264 SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
   265     if (info) {
   266         commonAsAGradient(info);
   267         info->fPoint[0] = fStart;
   268         info->fPoint[1] = fEnd;
   269     }
   270     return kLinear_GradientType;
   271 }
   273 static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
   274                             int count) {
   275     if (reinterpret_cast<uintptr_t>(dst) & 2) {
   276         *dst++ = value;
   277         count -= 1;
   278         SkTSwap(value, other);
   279     }
   281     sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
   283     if (count & 1) {
   284         dst[count - 1] = value;
   285     }
   286 }
   288 #define NO_CHECK_ITER_16                \
   289     do {                                \
   290     unsigned fi = fx >> SkGradientShaderBase::kCache16Shift;  \
   291     SkASSERT(fi < SkGradientShaderBase::kCache16Count);       \
   292     fx += dx;                           \
   293     *dstC++ = cache[toggle + fi];       \
   294     toggle = next_dither_toggle16(toggle);            \
   295     } while (0)
   297 namespace {
   299 typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
   300                                   uint16_t* dstC, const uint16_t* cache,
   301                                   int toggle, int count);
   303 void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
   304                                  uint16_t* SK_RESTRICT dstC,
   305                                  const uint16_t* SK_RESTRICT cache,
   306                                  int toggle, int count) {
   307     // we're a vertical gradient, so no change in a span
   308     unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift;
   309     SkASSERT(fi < SkGradientShaderBase::kCache16Count);
   310     dither_memset16(dstC, cache[toggle + fi],
   311         cache[next_dither_toggle16(toggle) + fi], count);
   312 }
   314 void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
   315                               uint16_t* SK_RESTRICT dstC,
   316                               const uint16_t* SK_RESTRICT cache,
   317                               int toggle, int count) {
   318     SkClampRange range;
   319     range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
   321     if ((count = range.fCount0) > 0) {
   322         dither_memset16(dstC,
   323             cache[toggle + range.fV0],
   324             cache[next_dither_toggle16(toggle) + range.fV0],
   325             count);
   326         dstC += count;
   327     }
   328     if ((count = range.fCount1) > 0) {
   329         int unroll = count >> 3;
   330         fx = range.fFx1;
   331         for (int i = 0; i < unroll; i++) {
   332             NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
   333             NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
   334             NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
   335             NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
   336         }
   337         if ((count &= 7) > 0) {
   338             do {
   339                 NO_CHECK_ITER_16;
   340             } while (--count != 0);
   341         }
   342     }
   343     if ((count = range.fCount2) > 0) {
   344         dither_memset16(dstC,
   345             cache[toggle + range.fV1],
   346             cache[next_dither_toggle16(toggle) + range.fV1],
   347             count);
   348     }
   349 }
   351 void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
   352                                uint16_t* SK_RESTRICT dstC,
   353                                const uint16_t* SK_RESTRICT cache,
   354                                int toggle, int count) {
   355     do {
   356         unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift,
   357                                         SkGradientShaderBase::kCache16Bits);
   358         SkASSERT(fi < SkGradientShaderBase::kCache16Count);
   359         fx += dx;
   360         *dstC++ = cache[toggle + fi];
   361         toggle = next_dither_toggle16(toggle);
   362     } while (--count != 0);
   363 }
   365 void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
   366                                uint16_t* SK_RESTRICT dstC,
   367                                const uint16_t* SK_RESTRICT cache,
   368                                int toggle, int count) {
   369     do {
   370         unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift,
   371                                   SkGradientShaderBase::kCache16Bits);
   372         SkASSERT(fi < SkGradientShaderBase::kCache16Count);
   373         fx += dx;
   374         *dstC++ = cache[toggle + fi];
   375         toggle = next_dither_toggle16(toggle);
   376     } while (--count != 0);
   377 }
   378 }
   380 static bool fixed_nearly_zero(SkFixed x) {
   381     return SkAbs32(x) < (SK_Fixed1 >> 12);
   382 }
   384 void SkLinearGradient::shadeSpan16(int x, int y,
   385                                   uint16_t* SK_RESTRICT dstC, int count) {
   386     SkASSERT(count > 0);
   388     SkPoint             srcPt;
   389     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
   390     TileProc            proc = fTileProc;
   391     const uint16_t* SK_RESTRICT cache = this->getCache16();
   392     int                 toggle = init_dither_toggle16(x, y);
   394     if (fDstToIndexClass != kPerspective_MatrixClass) {
   395         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
   396                              SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
   397         SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
   399         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
   400             SkFixed dxStorage[1];
   401             (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
   402             dx = dxStorage[0];
   403         } else {
   404             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
   405             dx = SkScalarToFixed(fDstToIndex.getScaleX());
   406         }
   408         LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
   409         if (fixed_nearly_zero(dx)) {
   410             shadeProc = shadeSpan16_linear_vertical;
   411         } else if (SkShader::kClamp_TileMode == fTileMode) {
   412             shadeProc = shadeSpan16_linear_clamp;
   413         } else if (SkShader::kMirror_TileMode == fTileMode) {
   414             shadeProc = shadeSpan16_linear_mirror;
   415         } else {
   416             SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
   417         }
   418         (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
   419     } else {
   420         SkScalar    dstX = SkIntToScalar(x);
   421         SkScalar    dstY = SkIntToScalar(y);
   422         do {
   423             dstProc(fDstToIndex, dstX, dstY, &srcPt);
   424             unsigned fi = proc(SkScalarToFixed(srcPt.fX));
   425             SkASSERT(fi <= 0xFFFF);
   427             int index = fi >> kCache16Shift;
   428             *dstC++ = cache[toggle + index];
   429             toggle = next_dither_toggle16(toggle);
   431             dstX += SK_Scalar1;
   432         } while (--count != 0);
   433     }
   434 }
   436 #if SK_SUPPORT_GPU
   438 #include "GrTBackendEffectFactory.h"
   440 /////////////////////////////////////////////////////////////////////
   442 class GrGLLinearGradient : public GrGLGradientEffect {
   443 public:
   445     GrGLLinearGradient(const GrBackendEffectFactory& factory, const GrDrawEffect&)
   446                        : INHERITED (factory) { }
   448     virtual ~GrGLLinearGradient() { }
   450     virtual void emitCode(GrGLShaderBuilder*,
   451                           const GrDrawEffect&,
   452                           EffectKey,
   453                           const char* outputColor,
   454                           const char* inputColor,
   455                           const TransformedCoordsArray&,
   456                           const TextureSamplerArray&) SK_OVERRIDE;
   458     static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
   459         return GenBaseGradientKey(drawEffect);
   460     }
   462 private:
   464     typedef GrGLGradientEffect INHERITED;
   465 };
   467 /////////////////////////////////////////////////////////////////////
   469 class GrLinearGradient : public GrGradientEffect {
   470 public:
   472     static GrEffectRef* Create(GrContext* ctx,
   473                                const SkLinearGradient& shader,
   474                                const SkMatrix& matrix,
   475                                SkShader::TileMode tm) {
   476         AutoEffectUnref effect(SkNEW_ARGS(GrLinearGradient, (ctx, shader, matrix, tm)));
   477         return CreateEffectRef(effect);
   478     }
   480     virtual ~GrLinearGradient() { }
   482     static const char* Name() { return "Linear Gradient"; }
   483     const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
   484         return GrTBackendEffectFactory<GrLinearGradient>::getInstance();
   485     }
   487     typedef GrGLLinearGradient GLEffect;
   489 private:
   490     GrLinearGradient(GrContext* ctx,
   491                      const SkLinearGradient& shader,
   492                      const SkMatrix& matrix,
   493                      SkShader::TileMode tm)
   494         : INHERITED(ctx, shader, matrix, tm) { }
   495     GR_DECLARE_EFFECT_TEST;
   497     typedef GrGradientEffect INHERITED;
   498 };
   500 /////////////////////////////////////////////////////////////////////
   502 GR_DEFINE_EFFECT_TEST(GrLinearGradient);
   504 GrEffectRef* GrLinearGradient::TestCreate(SkRandom* random,
   505                                           GrContext* context,
   506                                           const GrDrawTargetCaps&,
   507                                           GrTexture**) {
   508     SkPoint points[] = {{random->nextUScalar1(), random->nextUScalar1()},
   509                         {random->nextUScalar1(), random->nextUScalar1()}};
   511     SkColor colors[kMaxRandomGradientColors];
   512     SkScalar stopsArray[kMaxRandomGradientColors];
   513     SkScalar* stops = stopsArray;
   514     SkShader::TileMode tm;
   515     int colorCount = RandomGradientParams(random, colors, &stops, &tm);
   516     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points,
   517                                                                  colors, stops, colorCount,
   518                                                                  tm));
   519     SkPaint paint;
   520     return shader->asNewEffect(context, paint);
   521 }
   523 /////////////////////////////////////////////////////////////////////
   525 void GrGLLinearGradient::emitCode(GrGLShaderBuilder* builder,
   526                                   const GrDrawEffect&,
   527                                   EffectKey key,
   528                                   const char* outputColor,
   529                                   const char* inputColor,
   530                                   const TransformedCoordsArray& coords,
   531                                   const TextureSamplerArray& samplers) {
   532     this->emitUniforms(builder, key);
   533     SkString t = builder->ensureFSCoords2D(coords, 0);
   534     t.append(".x");
   535     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
   536 }
   538 /////////////////////////////////////////////////////////////////////
   540 GrEffectRef* SkLinearGradient::asNewEffect(GrContext* context, const SkPaint&) const {
   541     SkASSERT(NULL != context);
   542     SkMatrix matrix;
   543     if (!this->getLocalMatrix().invert(&matrix)) {
   544         return NULL;
   545     }
   546     matrix.postConcat(fPtsToUnit);
   547     return GrLinearGradient::Create(context, *this, matrix, fTileMode);
   548 }
   550 #else
   552 GrEffectRef* SkLinearGradient::asNewEffect(GrContext*, const SkPaint&) const {
   553     SkDEBUGFAIL("Should not call in GPU-less build");
   554     return NULL;
   555 }
   557 #endif
   559 #ifndef SK_IGNORE_TO_STRING
   560 void SkLinearGradient::toString(SkString* str) const {
   561     str->append("SkLinearGradient (");
   563     str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
   564     str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
   566     this->INHERITED::toString(str);
   568     str->append(")");
   569 }
   570 #endif

mercurial