1bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita/*
2bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita * Copyright 2016 Google Inc.
3bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita *
4bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita * Use of this source code is governed by a BSD-style license that can be
5bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita * found in the LICENSE file.
6bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita */
7bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita
8bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita#include "Sk4fGradientBase.h"
97e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
107e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita#include <functional>
11bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita
12bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalitanamespace {
13bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita
14b931336f3b7913e64602562d7d6d9187086bbd5afmalitaSk4f pack_color(SkColor c, bool premul, const Sk4f& component_scale) {
15b931336f3b7913e64602562d7d6d9187086bbd5afmalita    const SkColor4f c4f = SkColor4f::FromColor(c);
16b931336f3b7913e64602562d7d6d9187086bbd5afmalita    const Sk4f pm4f = premul
17b931336f3b7913e64602562d7d6d9187086bbd5afmalita        ? c4f.premul().to4f()
18b931336f3b7913e64602562d7d6d9187086bbd5afmalita        : Sk4f{c4f.fR, c4f.fG, c4f.fB, c4f.fA};
19b931336f3b7913e64602562d7d6d9187086bbd5afmalita
20b931336f3b7913e64602562d7d6d9187086bbd5afmalita    return pm4f * component_scale;
217e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita}
227e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
237e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitaclass IntervalIterator {
247e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitapublic:
257e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    IntervalIterator(const SkColor* colors, const SkScalar* pos, int count, bool reverse)
267e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        : fColors(colors)
277e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        , fPos(pos)
287e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        , fCount(count)
297e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        , fFirstPos(reverse ? SK_Scalar1 : 0)
307e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        , fBegin(reverse ? count - 1 : 0)
317e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        , fAdvance(reverse ? -1 : 1) {
327e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        SkASSERT(colors);
337e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        SkASSERT(count > 0);
347e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
357e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
367e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    void iterate(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const {
377e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        if (!fPos) {
387e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            this->iterateImplicitPos(func);
397e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            return;
407e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        }
417e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
427e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        const int end = fBegin + fAdvance * (fCount - 1);
437e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        const SkScalar lastPos = 1 - fFirstPos;
447e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        int prev = fBegin;
457e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        SkScalar prevPos = fFirstPos;
467e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
477e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        do {
487e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            const int curr = prev + fAdvance;
497e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            SkASSERT(curr >= 0 && curr < fCount);
507e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
517e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            // TODO: this sanitization should be done in SkGradientShaderBase
527e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            const SkScalar currPos = (fAdvance > 0)
537e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita                ? SkTPin(fPos[curr], prevPos, lastPos)
547e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita                : SkTPin(fPos[curr], lastPos, prevPos);
557e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
567e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            if (currPos != prevPos) {
577e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita                SkASSERT((currPos - prevPos > 0) == (fAdvance > 0));
587e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita                func(fColors[prev], fColors[curr], prevPos, currPos);
597e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            }
607e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
617e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            prev = curr;
627e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            prevPos = currPos;
637e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        } while (prev != end);
647e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
657e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
667e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitaprivate:
677e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    void iterateImplicitPos(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const {
687e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        // When clients don't provide explicit color stop positions (fPos == nullptr),
697e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        // the color stops are distributed evenly across the unit interval
707e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        // (implicit positioning).
717e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        const SkScalar dt = fAdvance * SK_Scalar1 / (fCount - 1);
727e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        const int end = fBegin + fAdvance * (fCount - 2);
737e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        int prev = fBegin;
747e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        SkScalar prevPos = fFirstPos;
757e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
767e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        while (prev != end) {
777e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            const int curr = prev + fAdvance;
787e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            SkASSERT(curr >= 0 && curr < fCount);
797e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
807e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            const SkScalar currPos = prevPos + dt;
817e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            func(fColors[prev], fColors[curr], prevPos, currPos);
827e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            prev = curr;
837e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            prevPos = currPos;
847e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        }
857e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
867e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        // emit the last interval with a pinned end position, to avoid precision issues
877e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        func(fColors[prev], fColors[prev + fAdvance], prevPos, 1 - fFirstPos);
887e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
897e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
907e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    const SkColor*  fColors;
917e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    const SkScalar* fPos;
927e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    const int       fCount;
937e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    const SkScalar  fFirstPos;
947e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    const int       fBegin;
957e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    const int       fAdvance;
967e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita};
977e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
98da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malitavoid addMirrorIntervals(const SkColor colors[],
99da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                        const SkScalar pos[], int count,
100da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                        const Sk4f& componentScale,
101da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                        bool premulColors, bool reverse,
102da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                        Sk4fGradientIntervalBuffer::BufferType* buffer) {
103da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const IntervalIterator iter(colors, pos, count, reverse);
104da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    iter.iterate([&] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) {
105da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        SkASSERT(buffer->empty() || buffer->back().fP1 == 2 - p0);
106da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
107da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        const auto mirror_p0 = 2 - p0;
108da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        const auto mirror_p1 = 2 - p1;
109da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid
110da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        // triggering Interval asserts.
111da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        if (mirror_p0 != mirror_p1) {
112da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            buffer->emplace_back(pack_color(c0, premulColors, componentScale), mirror_p0,
113da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                                 pack_color(c1, premulColors, componentScale), mirror_p1);
114da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        }
115da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    });
116da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita}
117da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
118bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita} // anonymous namespace
119bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita
120da4545bfc58f8ec19df79f6ae75b7231477973d8Florin MalitaSk4fGradientInterval::Sk4fGradientInterval(const Sk4f& c0, SkScalar p0,
121da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                                           const Sk4f& c1, SkScalar p1)
122bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita    : fP0(p0)
123bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita    , fP1(p1)
124b931336f3b7913e64602562d7d6d9187086bbd5afmalita    , fZeroRamp((c0 == c1).allTrue()) {
125b931336f3b7913e64602562d7d6d9187086bbd5afmalita    SkASSERT(p0 != p1);
1268f457591e6a21c240fd2fca56a97281b556ce796fmalita    // Either p0 or p1 can be (-)inf for synthetic clamp edge intervals.
1278f457591e6a21c240fd2fca56a97281b556ce796fmalita    SkASSERT(SkScalarIsFinite(p0) || SkScalarIsFinite(p1));
1288f457591e6a21c240fd2fca56a97281b556ce796fmalita
1298f457591e6a21c240fd2fca56a97281b556ce796fmalita    const auto dp = p1 - p0;
1308f457591e6a21c240fd2fca56a97281b556ce796fmalita
1318f457591e6a21c240fd2fca56a97281b556ce796fmalita    // Clamp edge intervals are always zero-ramp.
1328f457591e6a21c240fd2fca56a97281b556ce796fmalita    SkASSERT(SkScalarIsFinite(dp) || fZeroRamp);
1338f457591e6a21c240fd2fca56a97281b556ce796fmalita    const Sk4f dc = SkScalarIsFinite(dp) ? (c1 - c0) / dp : 0;
134bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita
135b931336f3b7913e64602562d7d6d9187086bbd5afmalita    c0.store(&fC0.fVec);
136b931336f3b7913e64602562d7d6d9187086bbd5afmalita    dc.store(&fDc.fVec);
137bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita}
138bc590c01b00ef79e1e1f30058e7a70a29419f2a9fmalita
139da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malitavoid Sk4fGradientIntervalBuffer::init(const SkColor colors[], const SkScalar pos[], int count,
140da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                                      SkShader::TileMode tileMode, bool premulColors,
141da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                                      SkScalar alpha, bool reverse) {
1427e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    // The main job here is to build a specialized interval list: a different
1437e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    // representation of the color stops data, optimized for efficient scan line
1447e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    // access during shading.
1457e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1467e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //   [{P0,C0} , {P1,C1}) [{P1,C2} , {P2,c3}) ... [{Pn,C2n} , {Pn+1,C2n+1})
1477e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1487e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    // The list may be inverted when requested (such that e.g. points are sorted
1497e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    // in increasing x order when dx < 0).
1507e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1517e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    // Note: the current representation duplicates pos data; we could refactor to
1527e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //       avoid this if interval storage size becomes a concern.
1537e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1547e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    // Aside from reordering, we also perform two more pre-processing steps at
1557e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    // this stage:
1567e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1577e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //   1) scale the color components depending on paint alpha and the requested
1587e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //      interpolation space (note: the interval color storage is SkPM4f, but
1597e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //      that doesn't necessarily mean the colors are premultiplied; that
1607e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //      property is tracked in fColorsArePremul)
1617e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1627e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //   2) inject synthetic intervals to support tiling.
1637e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1647e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //      * for kRepeat, no extra intervals are needed - the iterator just
1657e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //        wraps around at the end:
1667e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1677e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //          ->[P0,P1)->..[Pn-1,Pn)->
1687e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1697e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //      * for kClamp, we add two "infinite" intervals before/after:
1707e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1717e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //          [-/+inf , P0)->[P0 , P1)->..[Pn-1 , Pn)->[Pn , +/-inf)
1727e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1737e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //        (the iterator should never run off the end in this mode)
1747e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1757e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //      * for kMirror, we extend the range to [0..2] and add a flipped
1767e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //        interval series - then the iterator operates just as in the
1777e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //        kRepeat case:
1787e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1797e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //          ->[P0,P1)->..[Pn-1,Pn)->[2 - Pn,2 - Pn-1)->..[2 - P1,2 - P0)->
1807e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    //
1817e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    // TODO: investigate collapsing intervals << 1px.
1827e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
183da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    SkASSERT(count > 0);
184da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    SkASSERT(colors);
1857e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
186da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    fIntervals.reset();
187da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
188da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const Sk4f componentScale = premulColors
189da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        ? Sk4f(alpha)
190da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        : Sk4f(1.0f, 1.0f, 1.0f, alpha);
191da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const int first_index = reverse ? count - 1 : 0;
192da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const int last_index = count - 1 - first_index;
1937e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    const SkScalar first_pos = reverse ? SK_Scalar1 : 0;
1947e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    const SkScalar last_pos = SK_Scalar1 - first_pos;
1957e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
196da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    if (tileMode == SkShader::kClamp_TileMode) {
1977e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        // synthetic edge interval: -/+inf .. P0
198da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        const Sk4f clamp_color = pack_color(colors[first_index],
199da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                                            premulColors, componentScale);
2008f457591e6a21c240fd2fca56a97281b556ce796fmalita        const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity;
2017e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        fIntervals.emplace_back(clamp_color, clamp_pos,
202b931336f3b7913e64602562d7d6d9187086bbd5afmalita                                clamp_color, first_pos);
203da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    } else if (tileMode == SkShader::kMirror_TileMode && reverse) {
2047e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        // synthetic mirror intervals injected before main intervals: (2 .. 1]
205da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        addMirrorIntervals(colors, pos, count, componentScale, premulColors, false, &fIntervals);
2067e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
2077e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
208da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const IntervalIterator iter(colors, pos, count, reverse);
209da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    iter.iterate([&] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) {
2107e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        SkASSERT(fIntervals.empty() || fIntervals.back().fP1 == p0);
2117e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
212da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), p0,
213da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                                pack_color(c1, premulColors, componentScale), p1);
2147e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    });
2157e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
216da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    if (tileMode == SkShader::kClamp_TileMode) {
2177e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        // synthetic edge interval: Pn .. +/-inf
218da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        const Sk4f clamp_color = pack_color(colors[last_index], premulColors, componentScale);
2198f457591e6a21c240fd2fca56a97281b556ce796fmalita        const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity;
2207e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        fIntervals.emplace_back(clamp_color, last_pos,
221b931336f3b7913e64602562d7d6d9187086bbd5afmalita                                clamp_color, clamp_pos);
222da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    } else if (tileMode == SkShader::kMirror_TileMode && !reverse) {
2237e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        // synthetic mirror intervals injected after main intervals: [1 .. 2)
224da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        addMirrorIntervals(colors, pos, count, componentScale, premulColors, true, &fIntervals);
2257e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
2267e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita}
2277e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
228da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malitaconst Sk4fGradientInterval* Sk4fGradientIntervalBuffer::find(SkScalar t) const {
229da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    // Binary search.
230da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const auto* i0 = fIntervals.begin();
231da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const auto* i1 = fIntervals.end() - 1;
2327e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
233da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    while (i0 != i1) {
234da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        SkASSERT(i0 < i1);
235da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        SkASSERT(t >= i0->fP0 && t <= i1->fP1);
236da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
237da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        const auto* i = i0 + ((i1 - i0) >> 1);
238da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
239da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        if (t > i->fP1) {
240da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            i0 = i + 1;
241da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        } else {
242da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            i1 = i;
243cc34176c040c9703e351af7268dfc3a8b3cbf71dfmalita        }
244da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    }
245da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
246da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    SkASSERT(i0->contains(t));
247da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    return i0;
248da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita}
249da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
250da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malitaconst Sk4fGradientInterval* Sk4fGradientIntervalBuffer::findNext(
251da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    SkScalar t, const Sk4fGradientInterval* prev, bool increasing) const {
252da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
253da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    SkASSERT(!prev->contains(t));
254da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    SkASSERT(prev >= fIntervals.begin() && prev < fIntervals.end());
255da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    SkASSERT(t >= fIntervals.front().fP0 && t <= fIntervals.back().fP1);
256da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
257da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const auto* i = prev;
258da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
259da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    // Use the |increasing| signal to figure which direction we should search for
260da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    // the next interval, then perform a linear search.
261da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    if (increasing) {
262da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        do {
263da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            i += 1;
264da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            if (i >= fIntervals.end()) {
265da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                i = fIntervals.begin();
266da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            }
267da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        } while (!i->contains(t));
268da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    } else {
269da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        do {
270da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            i -= 1;
271da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            if (i < fIntervals.begin()) {
272da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                i = fIntervals.end() - 1;
273da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            }
274da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        } while (!i->contains(t));
275da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    }
276da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
277da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    return i;
278da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita}
279da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
280da4545bfc58f8ec19df79f6ae75b7231477973d8Florin MalitaSkGradientShaderBase::
281da4545bfc58f8ec19df79f6ae75b7231477973d8Florin MalitaGradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader,
282da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita                                                         const ContextRec& rec)
283da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    : INHERITED(shader, rec)
284da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    , fFlags(this->INHERITED::getFlags())
285da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
286da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    , fDither(true)
287da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita#else
288da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    , fDither(rec.fPaint->isDither())
289da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita#endif
290da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita{
291da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const SkMatrix& inverse = this->getTotalInverse();
292da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    fDstToPos.setConcat(shader.fPtsToUnit, inverse);
293da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    fDstToPosProc = fDstToPos.getMapXYProc();
294da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos));
295da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
296da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) {
297da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        fFlags |= kOpaqueAlpha_Flag;
298da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    }
299da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
300da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    fColorsArePremul =
301da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
302da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        || shader.fColorsAreOpaque;
303da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita}
304da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita
305da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malitabool SkGradientShaderBase::
306da4545bfc58f8ec19df79f6ae75b7231477973d8Florin MalitaGradientShaderBase4fContext::isValid() const {
307da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    return fDstToPos.isFinite();
3087e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita}
3097e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
3107e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitavoid SkGradientShaderBase::
3117e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitaGradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
3127e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    if (fColorsArePremul) {
313dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalita        this->shadePremulSpan<DstType::L32, ApplyPremul::False>(x, y, dst, count);
3147e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    } else {
315dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalita        this->shadePremulSpan<DstType::L32, ApplyPremul::True>(x, y, dst, count);
3167e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
3177e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita}
3187e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
3197e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitavoid SkGradientShaderBase::
3207e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitaGradientShaderBase4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
3217e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    if (fColorsArePremul) {
322dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalita        this->shadePremulSpan<DstType::F32, ApplyPremul::False>(x, y, dst, count);
3237e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    } else {
324dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalita        this->shadePremulSpan<DstType::F32, ApplyPremul::True>(x, y, dst, count);
3257e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
3267e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita}
3277e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
328dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalitatemplate<DstType dstType, ApplyPremul premul>
3297e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitavoid SkGradientShaderBase::
3307e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitaGradientShaderBase4fContext::shadePremulSpan(int x, int y,
331dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalita                                             typename DstTraits<dstType, premul>::Type dst[],
3327e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita                                             int count) const {
3337e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    const SkGradientShaderBase& shader =
3347e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        static_cast<const SkGradientShaderBase&>(fShader);
3357e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
3367e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    switch (shader.fTileMode) {
3377e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    case kClamp_TileMode:
338dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalita        this->shadeSpanInternal<dstType,
339a928b288b3001eb34cc3c9caedbaac9a403b05edfmalita                                premul,
3407e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita                                kClamp_TileMode>(x, y, dst, count);
3417e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        break;
3427e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    case kRepeat_TileMode:
343dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalita        this->shadeSpanInternal<dstType,
344a928b288b3001eb34cc3c9caedbaac9a403b05edfmalita                                premul,
3457e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita                                kRepeat_TileMode>(x, y, dst, count);
3467e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        break;
3477e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    case kMirror_TileMode:
348dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalita        this->shadeSpanInternal<dstType,
349a928b288b3001eb34cc3c9caedbaac9a403b05edfmalita                                premul,
3507e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita                                kMirror_TileMode>(x, y, dst, count);
3517e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        break;
3527e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
3537e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita}
3547e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
355dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalitatemplate<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
3567e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitavoid SkGradientShaderBase::
3577e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitaGradientShaderBase4fContext::shadeSpanInternal(int x, int y,
358dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalita                                               typename DstTraits<dstType, premul>::Type dst[],
3597e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita                                               int count) const {
3607e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    static const int kBufSize = 128;
3617e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    SkScalar ts[kBufSize];
3623a2e45a6ed50c07cb1a710d3f7b74be796e61251fmalita    TSampler<dstType, premul, tileMode> sampler(*this);
3637e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
3647e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    SkASSERT(count > 0);
3657e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    do {
3667e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        const int n = SkTMin(kBufSize, count);
3677e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        this->mapTs(x, y, ts, n);
3687e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        for (int i = 0; i < n; ++i) {
3697e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            const Sk4f c = sampler.sample(ts[i]);
370dc6c9bf91c158e89cd9d5ef19dfbf1da98c598a6fmalita            DstTraits<dstType, premul>::store(c, dst++);
3717e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        }
3727e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        x += n;
3737e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        count -= n;
3747e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    } while (count > 0);
3757e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita}
3767e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
3773a2e45a6ed50c07cb1a710d3f7b74be796e61251fmalitatemplate<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
3787e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitaclass SkGradientShaderBase::GradientShaderBase4fContext::TSampler {
3797e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitapublic:
3807e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    TSampler(const GradientShaderBase4fContext& ctx)
381da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita        : fCtx(ctx)
3825b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita        , fInterval(nullptr) {
3835b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita        switch (tileMode) {
3845b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita        case kClamp_TileMode:
3855b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita            fLargestIntervalValue = SK_ScalarInfinity;
3865b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita            break;
3875b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita        case kRepeat_TileMode:
3885b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita            fLargestIntervalValue = nextafterf(1, 0);
3895b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita            break;
3905b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita        case kMirror_TileMode:
3915b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita            fLargestIntervalValue = nextafterf(2.0f, 0);
3925b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita            break;
3935b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita        }
3947e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
3957e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
3967e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    Sk4f sample(SkScalar t) {
3978ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita        const auto tiled_t = tileProc(t);
3987e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
3997e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        if (!fInterval) {
4007e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            // Very first sample => locate the initial interval.
4017e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            // TODO: maybe do this in ctor to remove a branch?
402da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            fInterval = fCtx.fIntervals.find(tiled_t);
4037e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            this->loadIntervalData(fInterval);
4043d1a6bc5f3124dd5cd237ccc39ead26fe4b3355fFlorin Malita        } else if (!fInterval->contains(tiled_t)) {
405da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita            fInterval = fCtx.fIntervals.findNext(tiled_t, fInterval, t >= fPrevT);
4067e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita            this->loadIntervalData(fInterval);
4077e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        }
4087e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
4097e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        fPrevT = t;
4107e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        return lerp(tiled_t);
4117e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
4127e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
4137e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalitaprivate:
4148ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita    SkScalar tileProc(SkScalar t) const {
4158ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita        switch (tileMode) {
4168ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita        case kClamp_TileMode:
4178ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita            // synthetic clamp-mode edge intervals allow for a free-floating t:
4188ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita            //   [-inf..0)[0..1)[1..+inf)
4198ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita            return t;
4208ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita        case kRepeat_TileMode:
4218ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita            // t % 1  (intervals range: [0..1))
4225b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita            // Due to the extra arithmetic, we must clamp to ensure the value remains less than 1.
4235b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita            return SkTMin(t - SkScalarFloorToScalar(t), fLargestIntervalValue);
4248ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita        case kMirror_TileMode:
4258ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita            // t % 2  (synthetic mirror intervals expand the range to [0..2)
4268ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita            // Due to the extra arithmetic, we must clamp to ensure the value remains less than 2.
4275b1a7c21006175d313aad09ef40f9453a21480e2Florin Malita            return SkTMin(t - SkScalarFloorToScalar(t / 2) * 2, fLargestIntervalValue);
4288ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita        }
4298ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita
4308ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita        SK_ABORT("Unhandled tile mode.");
4318ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita        return 0;
4328ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita    }
4338ffb3e5d4c3731d02b3502a89c7a7eb18daf398cfmalita
4347e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    Sk4f lerp(SkScalar t) {
4353d1a6bc5f3124dd5cd237ccc39ead26fe4b3355fFlorin Malita        SkASSERT(fInterval->contains(t));
4367e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita        return fCc + fDc * (t - fInterval->fP0);
4377e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
4387e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
439da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    void loadIntervalData(const Sk4fGradientInterval* i) {
4403a2e45a6ed50c07cb1a710d3f7b74be796e61251fmalita        fCc = DstTraits<dstType, premul>::load(i->fC0);
4413a2e45a6ed50c07cb1a710d3f7b74be796e61251fmalita        fDc = DstTraits<dstType, premul>::load(i->fDc);
4427e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita    }
4437e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita
444da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const GradientShaderBase4fContext& fCtx;
445da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    const Sk4fGradientInterval*        fInterval;
446da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    SkScalar                           fPrevT;
447da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    SkScalar                           fLargestIntervalValue;
448da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    Sk4f                               fCc;
449da4545bfc58f8ec19df79f6ae75b7231477973d8Florin Malita    Sk4f                               fDc;
4507e6fcf890a1a63249136b8e6d9f4d5a606ef7508fmalita};
451