Home | History | Annotate | Download | only in gradients
      1 /*
      2  * Copyright 2016 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  */
      7 
      8 #include "Sk4fLinearGradient.h"
      9 
     10 namespace {
     11 
     12 Sk4f premul_4f(const Sk4f& c) {
     13     const float alpha = c[SkPM4f::A];
     14     // FIXME: portable swizzle?
     15     return c * Sk4f(alpha, alpha, alpha, 1);
     16 }
     17 
     18 template <bool do_premul>
     19 SkPMColor trunc_from_255(const Sk4f& c) {
     20     SkPMColor pmc;
     21     SkNx_cast<uint8_t>(c).store(&pmc);
     22     if (do_premul) {
     23         pmc = SkPreMultiplyARGB(SkGetPackedA32(pmc), SkGetPackedR32(pmc),
     24                                 SkGetPackedG32(pmc), SkGetPackedB32(pmc));
     25     }
     26     return pmc;
     27 }
     28 
     29 template<typename DstType, bool do_premul>
     30 void fill(const Sk4f& c, DstType* dst, int n);
     31 
     32 template<>
     33 void fill<SkPM4f, false>(const Sk4f& c, SkPM4f* dst, int n) {
     34     while (n > 0) {
     35         c.store(dst++);
     36         n--;
     37     }
     38 }
     39 
     40 template<>
     41 void fill<SkPM4f, true>(const Sk4f& c, SkPM4f* dst, int n) {
     42     fill<SkPM4f, false>(premul_4f(c), dst, n);
     43 }
     44 
     45 template<>
     46 void fill<SkPMColor, false>(const Sk4f& c, SkPMColor* dst, int n) {
     47     sk_memset32(dst, trunc_from_255<false>(c), n);
     48 }
     49 
     50 template<>
     51 void fill<SkPMColor, true>(const Sk4f& c, SkPMColor* dst, int n) {
     52     sk_memset32(dst, trunc_from_255<true>(c), n);
     53 }
     54 
     55 template<typename DstType, bool do_premul>
     56 void store(const Sk4f& color, DstType* dst);
     57 
     58 template<>
     59 void store<SkPM4f, false>(const Sk4f& c, SkPM4f* dst) {
     60     c.store(dst);
     61 }
     62 
     63 template<>
     64 void store<SkPM4f, true>(const Sk4f& c, SkPM4f* dst) {
     65     store<SkPM4f, false>(premul_4f(c), dst);
     66 }
     67 
     68 template<>
     69 void store<SkPMColor, false>(const Sk4f& c, SkPMColor* dst) {
     70     *dst = trunc_from_255<false>(c);
     71 }
     72 
     73 template<>
     74 void store<SkPMColor, true>(const Sk4f& c, SkPMColor* dst) {
     75     *dst = trunc_from_255<true>(c);
     76 }
     77 
     78 template<typename DstType, bool do_premul>
     79 void store4x(const Sk4f& c0,
     80              const Sk4f& c1,
     81              const Sk4f& c2,
     82              const Sk4f& c3,
     83              DstType* dst) {
     84     store<DstType, do_premul>(c0, dst++);
     85     store<DstType, do_premul>(c1, dst++);
     86     store<DstType, do_premul>(c2, dst++);
     87     store<DstType, do_premul>(c3, dst++);
     88 }
     89 
     90 template<>
     91 void store4x<SkPMColor, false>(const Sk4f& c0,
     92                                const Sk4f& c1,
     93                                const Sk4f& c2,
     94                                const Sk4f& c3,
     95                                SkPMColor* dst) {
     96     Sk4f_ToBytes((uint8_t*)dst, c0, c1, c2, c3);
     97 }
     98 
     99 template<typename DstType, bool do_premul>
    100 void ramp(const Sk4f& c, const Sk4f& dc, DstType* dst, int n) {
    101     SkASSERT(n > 0);
    102 
    103     const Sk4f dc2 = dc + dc;
    104     const Sk4f dc4 = dc2 + dc2;
    105 
    106     Sk4f c0 = c ;
    107     Sk4f c1 = c + dc;
    108     Sk4f c2 = c0 + dc2;
    109     Sk4f c3 = c1 + dc2;
    110 
    111     while (n >= 4) {
    112         store4x<DstType, do_premul>(c0, c1, c2, c3, dst);
    113         dst += 4;
    114 
    115         c0 = c0 + dc4;
    116         c1 = c1 + dc4;
    117         c2 = c2 + dc4;
    118         c3 = c3 + dc4;
    119         n -= 4;
    120     }
    121     if (n & 2) {
    122         store<DstType, do_premul>(c0, dst++);
    123         store<DstType, do_premul>(c1, dst++);
    124         c0 = c0 + dc2;
    125     }
    126     if (n & 1) {
    127         store<DstType, do_premul>(c0, dst);
    128     }
    129 }
    130 
    131 template<SkShader::TileMode>
    132 SkScalar pinFx(SkScalar);
    133 
    134 template<>
    135 SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) {
    136     return fx;
    137 }
    138 
    139 template<>
    140 SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) {
    141     const SkScalar f = SkScalarFraction(fx);
    142     return f < 0 ? f + 1 : f;
    143 }
    144 
    145 template<>
    146 SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) {
    147     const SkScalar f = SkScalarMod(fx, 2.0f);
    148     return f < 0 ? f + 2 : f;
    149 }
    150 
    151 template<typename DstType>
    152 float dst_component_scale();
    153 
    154 template<>
    155 float dst_component_scale<SkPM4f>() {
    156     return 1;
    157 }
    158 
    159 template<>
    160 float dst_component_scale<SkPMColor>() {
    161     return 255;
    162 }
    163 
    164 } // anonymous namespace
    165 
    166 SkLinearGradient::
    167 LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader,
    168                                                  const ContextRec& rec)
    169     : INHERITED(shader, rec) {}
    170 
    171 void SkLinearGradient::
    172 LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
    173     // TODO: plumb dithering
    174     SkASSERT(count > 0);
    175     if (fColorsArePremul) {
    176         this->shadePremulSpan<SkPMColor, false>(x, y, dst, count);
    177     } else {
    178         this->shadePremulSpan<SkPMColor, true>(x, y, dst, count);
    179     }
    180 }
    181 
    182 void SkLinearGradient::
    183 LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
    184     // TONOTDO: plumb dithering
    185     SkASSERT(count > 0);
    186     if (fColorsArePremul) {
    187         this->shadePremulSpan<SkPM4f, false>(x, y, dst, count);
    188     } else {
    189         this->shadePremulSpan<SkPM4f, true>(x, y, dst, count);
    190     }
    191 }
    192 
    193 template<typename DstType, bool do_premul>
    194 void SkLinearGradient::
    195 LinearGradient4fContext::shadePremulSpan(int x, int y,
    196                                          DstType dst[],
    197                                          int count) const {
    198     const SkLinearGradient& shader =
    199         static_cast<const SkLinearGradient&>(fShader);
    200     switch (shader.fTileMode) {
    201     case kClamp_TileMode:
    202         this->shadeSpanInternal<DstType,
    203                                 do_premul,
    204                                 kClamp_TileMode>(x, y, dst, count);
    205         break;
    206     case kRepeat_TileMode:
    207         this->shadeSpanInternal<DstType,
    208                                 do_premul,
    209                                 kRepeat_TileMode>(x, y, dst, count);
    210         break;
    211     case kMirror_TileMode:
    212         this->shadeSpanInternal<DstType,
    213                                 do_premul,
    214                                 kMirror_TileMode>(x, y, dst, count);
    215         break;
    216     }
    217 }
    218 
    219 template<typename DstType, bool do_premul, SkShader::TileMode tileMode>
    220 void SkLinearGradient::
    221 LinearGradient4fContext::shadeSpanInternal(int x, int y,
    222                                            DstType dst[],
    223                                            int count) const {
    224     SkPoint pt;
    225     fDstToPosProc(fDstToPos,
    226                   x + SK_ScalarHalf,
    227                   y + SK_ScalarHalf,
    228                   &pt);
    229     const SkScalar fx = pinFx<tileMode>(pt.x());
    230     const SkScalar dx = fDstToPos.getScaleX();
    231     LinearIntervalProcessor<DstType, tileMode> proc(fIntervals.begin(),
    232                                                     fIntervals.end() - 1,
    233                                                     this->findInterval(fx),
    234                                                     fx,
    235                                                     dx,
    236                                                     SkScalarNearlyZero(dx * count));
    237     while (count > 0) {
    238         // What we really want here is SkTPin(advance, 1, count)
    239         // but that's a significant perf hit for >> stops; investigate.
    240         const int n = SkScalarTruncToInt(
    241             SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count)));
    242 
    243         // The current interval advance can be +inf (e.g. when reaching
    244         // the clamp mode end intervals) - when that happens, we expect to
    245         //   a) consume all remaining count in one swoop
    246         //   b) return a zero color gradient
    247         SkASSERT(SkScalarIsFinite(proc.currentAdvance())
    248             || (n == count && proc.currentRampIsZero()));
    249 
    250         if (proc.currentRampIsZero()) {
    251             fill<DstType, do_premul>(proc.currentColor(),
    252                                      dst, n);
    253         } else {
    254             ramp<DstType, do_premul>(proc.currentColor(),
    255                                      proc.currentColorGrad(),
    256                                      dst, n);
    257         }
    258 
    259         proc.advance(SkIntToScalar(n));
    260         count -= n;
    261         dst   += n;
    262     }
    263 }
    264 
    265 template<typename DstType, SkShader::TileMode tileMode>
    266 class SkLinearGradient::
    267 LinearGradient4fContext::LinearIntervalProcessor {
    268 public:
    269     LinearIntervalProcessor(const Interval* firstInterval,
    270                             const Interval* lastInterval,
    271                             const Interval* i,
    272                             SkScalar fx,
    273                             SkScalar dx,
    274                             bool is_vertical)
    275         : fDstComponentScale(dst_component_scale<DstType>())
    276         , fAdvX((i->fP1 - fx) / dx)
    277         , fFirstInterval(firstInterval)
    278         , fLastInterval(lastInterval)
    279         , fInterval(i)
    280         , fDx(dx)
    281         , fIsVertical(is_vertical)
    282     {
    283         SkASSERT(firstInterval <= lastInterval);
    284         SkASSERT(i->contains(fx));
    285         this->compute_interval_props(fx - i->fP0);
    286     }
    287 
    288     SkScalar currentAdvance() const {
    289         SkASSERT(fAdvX >= 0);
    290         SkASSERT(fAdvX <= (fInterval->fP1 - fInterval->fP0) / fDx);
    291         return fAdvX;
    292     }
    293 
    294     bool currentRampIsZero() const { return fZeroRamp; }
    295     const Sk4f& currentColor() const { return fCc; }
    296     const Sk4f& currentColorGrad() const { return fDcDx; }
    297 
    298     void advance(SkScalar advX) {
    299         SkASSERT(advX > 0);
    300         SkASSERT(fAdvX >= 0);
    301 
    302         if (advX >= fAdvX) {
    303             advX = this->advance_interval(advX);
    304         }
    305         SkASSERT(advX < fAdvX);
    306 
    307         fCc = fCc + fDcDx * Sk4f(advX);
    308         fAdvX -= advX;
    309     }
    310 
    311 private:
    312     void compute_interval_props(SkScalar t) {
    313         fDc   = Sk4f::Load(fInterval->fDc.fVec);
    314         fCc   = Sk4f::Load(fInterval->fC0.fVec);
    315         fCc   = fCc + fDc * Sk4f(t);
    316         fCc   = fCc * fDstComponentScale;
    317         fDcDx = fDc * fDstComponentScale * Sk4f(fDx);
    318         fZeroRamp = fIsVertical || fInterval->isZeroRamp();
    319     }
    320 
    321     const Interval* next_interval(const Interval* i) const {
    322         SkASSERT(i >= fFirstInterval);
    323         SkASSERT(i <= fLastInterval);
    324         i++;
    325 
    326         if (tileMode == kClamp_TileMode) {
    327             SkASSERT(i <= fLastInterval);
    328             return i;
    329         }
    330 
    331         return (i <= fLastInterval) ? i : fFirstInterval;
    332     }
    333 
    334     SkScalar advance_interval(SkScalar advX) {
    335         SkASSERT(advX >= fAdvX);
    336 
    337         do {
    338             advX -= fAdvX;
    339             fInterval = this->next_interval(fInterval);
    340             fAdvX = (fInterval->fP1 - fInterval->fP0) / fDx;
    341             SkASSERT(fAdvX > 0);
    342         } while (advX >= fAdvX);
    343 
    344         compute_interval_props(0);
    345 
    346         SkASSERT(advX >= 0);
    347         return advX;
    348     }
    349 
    350     const Sk4f      fDstComponentScale; // cached dst scale (PMC: 255, PM4f: 1)
    351 
    352     // Current interval properties.
    353     Sk4f            fDc;        // local color gradient (dc/dt)
    354     Sk4f            fDcDx;      // dst color gradient (dc/dx)
    355     Sk4f            fCc;        // current color, interpolated in dst
    356     SkScalar        fAdvX;      // remaining interval advance in dst
    357     bool            fZeroRamp;  // current interval color grad is 0
    358 
    359     const Interval* fFirstInterval;
    360     const Interval* fLastInterval;
    361     const Interval* fInterval;  // current interval
    362     const SkScalar  fDx;        // 'dx' for consistency with other impls; actually dt/dx
    363     const bool      fIsVertical;
    364 };
    365