LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/effects/gradients - Sk4fGradientBase.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 208 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 91 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "Sk4fGradientBase.h"
       9             : 
      10             : #include <functional>
      11             : 
      12             : namespace {
      13             : 
      14           0 : Sk4f pack_color(SkColor c, bool premul, const Sk4f& component_scale) {
      15           0 :     const SkColor4f c4f = SkColor4f::FromColor(c);
      16             :     const Sk4f pm4f = premul
      17           0 :         ? c4f.premul().to4f()
      18           0 :         : Sk4f{c4f.fR, c4f.fG, c4f.fB, c4f.fA};
      19             : 
      20           0 :     return pm4f * component_scale;
      21             : }
      22             : 
      23             : class IntervalIterator {
      24             : public:
      25           0 :     IntervalIterator(const SkColor* colors, const SkScalar* pos, int count, bool reverse)
      26           0 :         : fColors(colors)
      27             :         , fPos(pos)
      28             :         , fCount(count)
      29           0 :         , fFirstPos(reverse ? SK_Scalar1 : 0)
      30           0 :         , fBegin(reverse ? count - 1 : 0)
      31           0 :         , fAdvance(reverse ? -1 : 1) {
      32           0 :         SkASSERT(colors);
      33           0 :         SkASSERT(count > 0);
      34           0 :     }
      35             : 
      36           0 :     void iterate(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const {
      37           0 :         if (!fPos) {
      38           0 :             this->iterateImplicitPos(func);
      39           0 :             return;
      40             :         }
      41             : 
      42           0 :         const int end = fBegin + fAdvance * (fCount - 1);
      43           0 :         const SkScalar lastPos = 1 - fFirstPos;
      44           0 :         int prev = fBegin;
      45           0 :         SkScalar prevPos = fFirstPos;
      46             : 
      47           0 :         do {
      48           0 :             const int curr = prev + fAdvance;
      49           0 :             SkASSERT(curr >= 0 && curr < fCount);
      50             : 
      51             :             // TODO: this sanitization should be done in SkGradientShaderBase
      52           0 :             const SkScalar currPos = (fAdvance > 0)
      53           0 :                 ? SkTPin(fPos[curr], prevPos, lastPos)
      54           0 :                 : SkTPin(fPos[curr], lastPos, prevPos);
      55             : 
      56           0 :             if (currPos != prevPos) {
      57           0 :                 SkASSERT((currPos - prevPos > 0) == (fAdvance > 0));
      58           0 :                 func(fColors[prev], fColors[curr], prevPos, currPos);
      59             :             }
      60             : 
      61           0 :             prev = curr;
      62           0 :             prevPos = currPos;
      63           0 :         } while (prev != end);
      64             :     }
      65             : 
      66             : private:
      67           0 :     void iterateImplicitPos(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const {
      68             :         // When clients don't provide explicit color stop positions (fPos == nullptr),
      69             :         // the color stops are distributed evenly across the unit interval
      70             :         // (implicit positioning).
      71           0 :         const SkScalar dt = fAdvance * SK_Scalar1 / (fCount - 1);
      72           0 :         const int end = fBegin + fAdvance * (fCount - 2);
      73           0 :         int prev = fBegin;
      74           0 :         SkScalar prevPos = fFirstPos;
      75             : 
      76           0 :         while (prev != end) {
      77           0 :             const int curr = prev + fAdvance;
      78           0 :             SkASSERT(curr >= 0 && curr < fCount);
      79             : 
      80           0 :             const SkScalar currPos = prevPos + dt;
      81           0 :             func(fColors[prev], fColors[curr], prevPos, currPos);
      82           0 :             prev = curr;
      83           0 :             prevPos = currPos;
      84             :         }
      85             : 
      86             :         // emit the last interval with a pinned end position, to avoid precision issues
      87           0 :         func(fColors[prev], fColors[prev + fAdvance], prevPos, 1 - fFirstPos);
      88           0 :     }
      89             : 
      90             :     const SkColor*  fColors;
      91             :     const SkScalar* fPos;
      92             :     const int       fCount;
      93             :     const SkScalar  fFirstPos;
      94             :     const int       fBegin;
      95             :     const int       fAdvance;
      96             : };
      97             : 
      98           0 : void addMirrorIntervals(const SkColor colors[],
      99             :                         const SkScalar pos[], int count,
     100             :                         const Sk4f& componentScale,
     101             :                         bool premulColors, bool reverse,
     102             :                         Sk4fGradientIntervalBuffer::BufferType* buffer) {
     103           0 :     const IntervalIterator iter(colors, pos, count, reverse);
     104           0 :     iter.iterate([&] (SkColor c0, SkColor c1, SkScalar t0, SkScalar t1) {
     105           0 :         SkASSERT(buffer->empty() || buffer->back().fT1 == 2 - t0);
     106             : 
     107           0 :         const auto mirror_t0 = 2 - t0;
     108           0 :         const auto mirror_t1 = 2 - t1;
     109             :         // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid
     110             :         // triggering Interval asserts.
     111           0 :         if (mirror_t0 != mirror_t1) {
     112           0 :             buffer->emplace_back(pack_color(c0, premulColors, componentScale), mirror_t0,
     113           0 :                                  pack_color(c1, premulColors, componentScale), mirror_t1);
     114             :         }
     115           0 :     });
     116           0 : }
     117             : 
     118             : } // anonymous namespace
     119             : 
     120           0 : Sk4fGradientInterval::Sk4fGradientInterval(const Sk4f& c0, SkScalar t0,
     121           0 :                                            const Sk4f& c1, SkScalar t1)
     122             :     : fT0(t0)
     123             :     , fT1(t1)
     124           0 :     , fZeroRamp((c0 == c1).allTrue()) {
     125           0 :     SkASSERT(t0 != t1);
     126             :     // Either p0 or p1 can be (-)inf for synthetic clamp edge intervals.
     127           0 :     SkASSERT(SkScalarIsFinite(t0) || SkScalarIsFinite(t1));
     128             : 
     129           0 :     const auto dt = t1 - t0;
     130             : 
     131             :     // Clamp edge intervals are always zero-ramp.
     132           0 :     SkASSERT(SkScalarIsFinite(dt) || fZeroRamp);
     133           0 :     SkASSERT(SkScalarIsFinite(t0) || fZeroRamp);
     134           0 :     const Sk4f   dc = SkScalarIsFinite(dt) ? (c1 - c0) / dt : 0;
     135           0 :     const Sk4f bias = c0 - (SkScalarIsFinite(t0) ? t0 * dc : 0);
     136             : 
     137           0 :     bias.store(&fCb.fVec);
     138           0 :     dc.store(&fCg.fVec);
     139           0 : }
     140             : 
     141           0 : void Sk4fGradientIntervalBuffer::init(const SkColor colors[], const SkScalar pos[], int count,
     142             :                                       SkShader::TileMode tileMode, bool premulColors,
     143             :                                       SkScalar alpha, bool reverse) {
     144             :     // The main job here is to build a specialized interval list: a different
     145             :     // representation of the color stops data, optimized for efficient scan line
     146             :     // access during shading.
     147             :     //
     148             :     //   [{P0,C0} , {P1,C1}) [{P1,C2} , {P2,c3}) ... [{Pn,C2n} , {Pn+1,C2n+1})
     149             :     //
     150             :     // The list may be inverted when requested (such that e.g. points are sorted
     151             :     // in increasing x order when dx < 0).
     152             :     //
     153             :     // Note: the current representation duplicates pos data; we could refactor to
     154             :     //       avoid this if interval storage size becomes a concern.
     155             :     //
     156             :     // Aside from reordering, we also perform two more pre-processing steps at
     157             :     // this stage:
     158             :     //
     159             :     //   1) scale the color components depending on paint alpha and the requested
     160             :     //      interpolation space (note: the interval color storage is SkPM4f, but
     161             :     //      that doesn't necessarily mean the colors are premultiplied; that
     162             :     //      property is tracked in fColorsArePremul)
     163             :     //
     164             :     //   2) inject synthetic intervals to support tiling.
     165             :     //
     166             :     //      * for kRepeat, no extra intervals are needed - the iterator just
     167             :     //        wraps around at the end:
     168             :     //
     169             :     //          ->[P0,P1)->..[Pn-1,Pn)->
     170             :     //
     171             :     //      * for kClamp, we add two "infinite" intervals before/after:
     172             :     //
     173             :     //          [-/+inf , P0)->[P0 , P1)->..[Pn-1 , Pn)->[Pn , +/-inf)
     174             :     //
     175             :     //        (the iterator should never run off the end in this mode)
     176             :     //
     177             :     //      * for kMirror, we extend the range to [0..2] and add a flipped
     178             :     //        interval series - then the iterator operates just as in the
     179             :     //        kRepeat case:
     180             :     //
     181             :     //          ->[P0,P1)->..[Pn-1,Pn)->[2 - Pn,2 - Pn-1)->..[2 - P1,2 - P0)->
     182             :     //
     183             :     // TODO: investigate collapsing intervals << 1px.
     184             : 
     185           0 :     SkASSERT(count > 0);
     186           0 :     SkASSERT(colors);
     187             : 
     188           0 :     fIntervals.reset();
     189             : 
     190             :     const Sk4f componentScale = premulColors
     191             :         ? Sk4f(alpha)
     192           0 :         : Sk4f(1.0f, 1.0f, 1.0f, alpha);
     193           0 :     const int first_index = reverse ? count - 1 : 0;
     194           0 :     const int last_index = count - 1 - first_index;
     195           0 :     const SkScalar first_pos = reverse ? SK_Scalar1 : 0;
     196           0 :     const SkScalar last_pos = SK_Scalar1 - first_pos;
     197             : 
     198           0 :     if (tileMode == SkShader::kClamp_TileMode) {
     199             :         // synthetic edge interval: -/+inf .. P0
     200           0 :         const Sk4f clamp_color = pack_color(colors[first_index],
     201           0 :                                             premulColors, componentScale);
     202           0 :         const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity;
     203             :         fIntervals.emplace_back(clamp_color, clamp_pos,
     204           0 :                                 clamp_color, first_pos);
     205           0 :     } else if (tileMode == SkShader::kMirror_TileMode && reverse) {
     206             :         // synthetic mirror intervals injected before main intervals: (2 .. 1]
     207           0 :         addMirrorIntervals(colors, pos, count, componentScale, premulColors, false, &fIntervals);
     208             :     }
     209             : 
     210           0 :     const IntervalIterator iter(colors, pos, count, reverse);
     211           0 :     iter.iterate([&] (SkColor c0, SkColor c1, SkScalar t0, SkScalar t1) {
     212           0 :         SkASSERT(fIntervals.empty() || fIntervals.back().fT1 == t0);
     213             : 
     214           0 :         fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), t0,
     215           0 :                                 pack_color(c1, premulColors, componentScale), t1);
     216           0 :     });
     217             : 
     218           0 :     if (tileMode == SkShader::kClamp_TileMode) {
     219             :         // synthetic edge interval: Pn .. +/-inf
     220           0 :         const Sk4f clamp_color = pack_color(colors[last_index], premulColors, componentScale);
     221           0 :         const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity;
     222             :         fIntervals.emplace_back(clamp_color, last_pos,
     223           0 :                                 clamp_color, clamp_pos);
     224           0 :     } else if (tileMode == SkShader::kMirror_TileMode && !reverse) {
     225             :         // synthetic mirror intervals injected after main intervals: [1 .. 2)
     226           0 :         addMirrorIntervals(colors, pos, count, componentScale, premulColors, true, &fIntervals);
     227             :     }
     228           0 : }
     229             : 
     230           0 : const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::find(SkScalar t) const {
     231             :     // Binary search.
     232           0 :     const auto* i0 = fIntervals.begin();
     233           0 :     const auto* i1 = fIntervals.end() - 1;
     234             : 
     235           0 :     while (i0 != i1) {
     236           0 :         SkASSERT(i0 < i1);
     237           0 :         SkASSERT(t >= i0->fT0 && t <= i1->fT1);
     238             : 
     239           0 :         const auto* i = i0 + ((i1 - i0) >> 1);
     240             : 
     241           0 :         if (t > i->fT1) {
     242           0 :             i0 = i + 1;
     243             :         } else {
     244           0 :             i1 = i;
     245             :         }
     246             :     }
     247             : 
     248           0 :     SkASSERT(i0->contains(t));
     249           0 :     return i0;
     250             : }
     251             : 
     252           0 : const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::findNext(
     253             :     SkScalar t, const Sk4fGradientInterval* prev, bool increasing) const {
     254             : 
     255           0 :     SkASSERT(!prev->contains(t));
     256           0 :     SkASSERT(prev >= fIntervals.begin() && prev < fIntervals.end());
     257           0 :     SkASSERT(t >= fIntervals.front().fT0 && t <= fIntervals.back().fT1);
     258             : 
     259           0 :     const auto* i = prev;
     260             : 
     261             :     // Use the |increasing| signal to figure which direction we should search for
     262             :     // the next interval, then perform a linear search.
     263           0 :     if (increasing) {
     264           0 :         do {
     265           0 :             i += 1;
     266           0 :             if (i >= fIntervals.end()) {
     267           0 :                 i = fIntervals.begin();
     268             :             }
     269           0 :         } while (!i->contains(t));
     270             :     } else {
     271           0 :         do {
     272           0 :             i -= 1;
     273           0 :             if (i < fIntervals.begin()) {
     274           0 :                 i = fIntervals.end() - 1;
     275             :             }
     276           0 :         } while (!i->contains(t));
     277             :     }
     278             : 
     279           0 :     return i;
     280             : }
     281             : 
     282           0 : SkGradientShaderBase::
     283             : GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader,
     284           0 :                                                          const ContextRec& rec)
     285             :     : INHERITED(shader, rec)
     286           0 :     , fFlags(this->INHERITED::getFlags())
     287             : #ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
     288             :     , fDither(true)
     289             : #else
     290           0 :     , fDither(rec.fPaint->isDither())
     291             : #endif
     292             : {
     293           0 :     const SkMatrix& inverse = this->getTotalInverse();
     294           0 :     fDstToPos.setConcat(shader.fPtsToUnit, inverse);
     295           0 :     fDstToPosProc = fDstToPos.getMapXYProc();
     296           0 :     fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos));
     297             : 
     298           0 :     if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) {
     299           0 :         fFlags |= kOpaqueAlpha_Flag;
     300             :     }
     301             : 
     302           0 :     fColorsArePremul =
     303           0 :         (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
     304           0 :         || shader.fColorsAreOpaque;
     305           0 : }
     306             : 
     307           0 : bool SkGradientShaderBase::
     308             : GradientShaderBase4fContext::isValid() const {
     309           0 :     return fDstToPos.isFinite();
     310             : }
     311             : 
     312           0 : void SkGradientShaderBase::
     313             : GradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
     314           0 :     if (fColorsArePremul) {
     315           0 :         this->shadePremulSpan<DstType::L32, ApplyPremul::False>(x, y, dst, count);
     316             :     } else {
     317           0 :         this->shadePremulSpan<DstType::L32, ApplyPremul::True>(x, y, dst, count);
     318             :     }
     319           0 : }
     320             : 
     321           0 : void SkGradientShaderBase::
     322             : GradientShaderBase4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
     323           0 :     if (fColorsArePremul) {
     324           0 :         this->shadePremulSpan<DstType::F32, ApplyPremul::False>(x, y, dst, count);
     325             :     } else {
     326           0 :         this->shadePremulSpan<DstType::F32, ApplyPremul::True>(x, y, dst, count);
     327             :     }
     328           0 : }
     329             : 
     330             : template<DstType dstType, ApplyPremul premul>
     331           0 : void SkGradientShaderBase::
     332             : GradientShaderBase4fContext::shadePremulSpan(int x, int y,
     333             :                                              typename DstTraits<dstType, premul>::Type dst[],
     334             :                                              int count) const {
     335             :     const SkGradientShaderBase& shader =
     336           0 :         static_cast<const SkGradientShaderBase&>(fShader);
     337             : 
     338           0 :     switch (shader.fTileMode) {
     339             :     case kClamp_TileMode:
     340           0 :         this->shadeSpanInternal<dstType,
     341             :                                 premul,
     342             :                                 kClamp_TileMode>(x, y, dst, count);
     343           0 :         break;
     344             :     case kRepeat_TileMode:
     345           0 :         this->shadeSpanInternal<dstType,
     346             :                                 premul,
     347             :                                 kRepeat_TileMode>(x, y, dst, count);
     348           0 :         break;
     349             :     case kMirror_TileMode:
     350           0 :         this->shadeSpanInternal<dstType,
     351             :                                 premul,
     352             :                                 kMirror_TileMode>(x, y, dst, count);
     353           0 :         break;
     354             :     }
     355           0 : }
     356             : 
     357             : template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
     358           0 : void SkGradientShaderBase::
     359             : GradientShaderBase4fContext::shadeSpanInternal(int x, int y,
     360             :                                                typename DstTraits<dstType, premul>::Type dst[],
     361             :                                                int count) const {
     362             :     static const int kBufSize = 128;
     363             :     SkScalar ts[kBufSize];
     364           0 :     TSampler<dstType, premul, tileMode> sampler(*this);
     365             : 
     366           0 :     SkASSERT(count > 0);
     367           0 :     do {
     368           0 :         const int n = SkTMin(kBufSize, count);
     369           0 :         this->mapTs(x, y, ts, n);
     370           0 :         for (int i = 0; i < n; ++i) {
     371           0 :             const Sk4f c = sampler.sample(ts[i]);
     372           0 :             DstTraits<dstType, premul>::store(c, dst++);
     373             :         }
     374           0 :         x += n;
     375           0 :         count -= n;
     376           0 :     } while (count > 0);
     377           0 : }
     378             : 
     379             : template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
     380             : class SkGradientShaderBase::GradientShaderBase4fContext::TSampler {
     381             : public:
     382           0 :     TSampler(const GradientShaderBase4fContext& ctx)
     383             :         : fCtx(ctx)
     384           0 :         , fInterval(nullptr) {
     385             :         switch (tileMode) {
     386             :         case kClamp_TileMode:
     387           0 :             fLargestIntervalValue = SK_ScalarInfinity;
     388           0 :             break;
     389             :         case kRepeat_TileMode:
     390           0 :             fLargestIntervalValue = nextafterf(1, 0);
     391           0 :             break;
     392             :         case kMirror_TileMode:
     393           0 :             fLargestIntervalValue = nextafterf(2.0f, 0);
     394           0 :             break;
     395             :         }
     396           0 :     }
     397             : 
     398           0 :     Sk4f sample(SkScalar t) {
     399           0 :         const auto tiled_t = tileProc(t);
     400             : 
     401           0 :         if (!fInterval) {
     402             :             // Very first sample => locate the initial interval.
     403             :             // TODO: maybe do this in ctor to remove a branch?
     404           0 :             fInterval = fCtx.fIntervals.find(tiled_t);
     405           0 :             this->loadIntervalData(fInterval);
     406           0 :         } else if (!fInterval->contains(tiled_t)) {
     407           0 :             fInterval = fCtx.fIntervals.findNext(tiled_t, fInterval, t >= fPrevT);
     408           0 :             this->loadIntervalData(fInterval);
     409             :         }
     410             : 
     411           0 :         fPrevT = t;
     412           0 :         return lerp(tiled_t);
     413             :     }
     414             : 
     415             : private:
     416           0 :     SkScalar tileProc(SkScalar t) const {
     417             :         switch (tileMode) {
     418             :         case kClamp_TileMode:
     419             :             // synthetic clamp-mode edge intervals allow for a free-floating t:
     420             :             //   [-inf..0)[0..1)[1..+inf)
     421           0 :             return t;
     422             :         case kRepeat_TileMode:
     423             :             // t % 1  (intervals range: [0..1))
     424             :             // Due to the extra arithmetic, we must clamp to ensure the value remains less than 1.
     425           0 :             return SkTMin(t - SkScalarFloorToScalar(t), fLargestIntervalValue);
     426             :         case kMirror_TileMode:
     427             :             // t % 2  (synthetic mirror intervals expand the range to [0..2)
     428             :             // Due to the extra arithmetic, we must clamp to ensure the value remains less than 2.
     429           0 :             return SkTMin(t - SkScalarFloorToScalar(t / 2) * 2, fLargestIntervalValue);
     430             :         }
     431             : 
     432             :         SK_ABORT("Unhandled tile mode.");
     433             :         return 0;
     434             :     }
     435             : 
     436           0 :     Sk4f lerp(SkScalar t) {
     437           0 :         SkASSERT(fInterval->contains(t));
     438           0 :         return fCb + fCg * t;
     439             :     }
     440             : 
     441           0 :     void loadIntervalData(const Sk4fGradientInterval* i) {
     442           0 :         fCb = DstTraits<dstType, premul>::load(i->fCb);
     443           0 :         fCg = DstTraits<dstType, premul>::load(i->fCg);
     444           0 :     }
     445             : 
     446             :     const GradientShaderBase4fContext& fCtx;
     447             :     const Sk4fGradientInterval*        fInterval;
     448             :     SkScalar                           fPrevT;
     449             :     SkScalar                           fLargestIntervalValue;
     450             :     Sk4f                               fCb;
     451             :     Sk4f                               fCg;
     452             : };

Generated by: LCOV version 1.13