1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2006 The Android Open Source Project
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include <algorithm>
9cb93a386Sopenharmony_ci#include "include/core/SkMallocPixelRef.h"
10cb93a386Sopenharmony_ci#include "include/private/SkFloatBits.h"
11cb93a386Sopenharmony_ci#include "include/private/SkHalf.h"
12cb93a386Sopenharmony_ci#include "include/private/SkTPin.h"
13cb93a386Sopenharmony_ci#include "include/private/SkVx.h"
14cb93a386Sopenharmony_ci#include "src/core/SkColorSpacePriv.h"
15cb93a386Sopenharmony_ci#include "src/core/SkConvertPixels.h"
16cb93a386Sopenharmony_ci#include "src/core/SkMatrixProvider.h"
17cb93a386Sopenharmony_ci#include "src/core/SkReadBuffer.h"
18cb93a386Sopenharmony_ci#include "src/core/SkVM.h"
19cb93a386Sopenharmony_ci#include "src/core/SkWriteBuffer.h"
20cb93a386Sopenharmony_ci#include "src/shaders/gradients/Sk4fLinearGradient.h"
21cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkGradientShaderPriv.h"
22cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkLinearGradient.h"
23cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkRadialGradient.h"
24cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkSweepGradient.h"
25cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkTwoPointConicalGradient.h"
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_cienum GradientSerializationFlags {
28cb93a386Sopenharmony_ci    // Bits 29:31 used for various boolean flags
29cb93a386Sopenharmony_ci    kHasPosition_GSF    = 0x80000000,
30cb93a386Sopenharmony_ci    kHasLocalMatrix_GSF = 0x40000000,
31cb93a386Sopenharmony_ci    kHasColorSpace_GSF  = 0x20000000,
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ci    // Bits 12:28 unused
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_ci    // Bits 8:11 for fTileMode
36cb93a386Sopenharmony_ci    kTileModeShift_GSF  = 8,
37cb93a386Sopenharmony_ci    kTileModeMask_GSF   = 0xF,
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_ci    // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80)
40cb93a386Sopenharmony_ci    kGradFlagsShift_GSF = 0,
41cb93a386Sopenharmony_ci    kGradFlagsMask_GSF  = 0xFF,
42cb93a386Sopenharmony_ci};
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_civoid SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
45cb93a386Sopenharmony_ci    uint32_t flags = 0;
46cb93a386Sopenharmony_ci    if (fPos) {
47cb93a386Sopenharmony_ci        flags |= kHasPosition_GSF;
48cb93a386Sopenharmony_ci    }
49cb93a386Sopenharmony_ci    if (fLocalMatrix) {
50cb93a386Sopenharmony_ci        flags |= kHasLocalMatrix_GSF;
51cb93a386Sopenharmony_ci    }
52cb93a386Sopenharmony_ci    sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
53cb93a386Sopenharmony_ci    if (colorSpaceData) {
54cb93a386Sopenharmony_ci        flags |= kHasColorSpace_GSF;
55cb93a386Sopenharmony_ci    }
56cb93a386Sopenharmony_ci    SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
57cb93a386Sopenharmony_ci    flags |= ((unsigned)fTileMode << kTileModeShift_GSF);
58cb93a386Sopenharmony_ci    SkASSERT(fGradFlags <= kGradFlagsMask_GSF);
59cb93a386Sopenharmony_ci    flags |= (fGradFlags << kGradFlagsShift_GSF);
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci    buffer.writeUInt(flags);
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_ci    buffer.writeColor4fArray(fColors, fCount);
64cb93a386Sopenharmony_ci    if (colorSpaceData) {
65cb93a386Sopenharmony_ci        buffer.writeDataAsByteArray(colorSpaceData.get());
66cb93a386Sopenharmony_ci    }
67cb93a386Sopenharmony_ci    if (fPos) {
68cb93a386Sopenharmony_ci        buffer.writeScalarArray(fPos, fCount);
69cb93a386Sopenharmony_ci    }
70cb93a386Sopenharmony_ci    if (fLocalMatrix) {
71cb93a386Sopenharmony_ci        buffer.writeMatrix(*fLocalMatrix);
72cb93a386Sopenharmony_ci    }
73cb93a386Sopenharmony_ci}
74cb93a386Sopenharmony_ci
75cb93a386Sopenharmony_citemplate <int N, typename T, bool MEM_MOVE>
76cb93a386Sopenharmony_cistatic bool validate_array(SkReadBuffer& buffer, size_t count, SkSTArray<N, T, MEM_MOVE>* array) {
77cb93a386Sopenharmony_ci    if (!buffer.validateCanReadN<T>(count)) {
78cb93a386Sopenharmony_ci        return false;
79cb93a386Sopenharmony_ci    }
80cb93a386Sopenharmony_ci
81cb93a386Sopenharmony_ci    array->resize_back(count);
82cb93a386Sopenharmony_ci    return true;
83cb93a386Sopenharmony_ci}
84cb93a386Sopenharmony_ci
85cb93a386Sopenharmony_cibool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
86cb93a386Sopenharmony_ci    // New gradient format. Includes floating point color, color space, densely packed flags
87cb93a386Sopenharmony_ci    uint32_t flags = buffer.readUInt();
88cb93a386Sopenharmony_ci
89cb93a386Sopenharmony_ci    fTileMode = (SkTileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
90cb93a386Sopenharmony_ci    fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF;
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ci    fCount = buffer.getArrayCount();
93cb93a386Sopenharmony_ci
94cb93a386Sopenharmony_ci    if (!(validate_array(buffer, fCount, &fColorStorage) &&
95cb93a386Sopenharmony_ci          buffer.readColor4fArray(fColorStorage.begin(), fCount))) {
96cb93a386Sopenharmony_ci        return false;
97cb93a386Sopenharmony_ci    }
98cb93a386Sopenharmony_ci    fColors = fColorStorage.begin();
99cb93a386Sopenharmony_ci
100cb93a386Sopenharmony_ci    if (SkToBool(flags & kHasColorSpace_GSF)) {
101cb93a386Sopenharmony_ci        sk_sp<SkData> data = buffer.readByteArrayAsData();
102cb93a386Sopenharmony_ci        fColorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr;
103cb93a386Sopenharmony_ci    } else {
104cb93a386Sopenharmony_ci        fColorSpace = nullptr;
105cb93a386Sopenharmony_ci    }
106cb93a386Sopenharmony_ci    if (SkToBool(flags & kHasPosition_GSF)) {
107cb93a386Sopenharmony_ci        if (!(validate_array(buffer, fCount, &fPosStorage) &&
108cb93a386Sopenharmony_ci              buffer.readScalarArray(fPosStorage.begin(), fCount))) {
109cb93a386Sopenharmony_ci            return false;
110cb93a386Sopenharmony_ci        }
111cb93a386Sopenharmony_ci        fPos = fPosStorage.begin();
112cb93a386Sopenharmony_ci    } else {
113cb93a386Sopenharmony_ci        fPos = nullptr;
114cb93a386Sopenharmony_ci    }
115cb93a386Sopenharmony_ci    if (SkToBool(flags & kHasLocalMatrix_GSF)) {
116cb93a386Sopenharmony_ci        fLocalMatrix = &fLocalMatrixStorage;
117cb93a386Sopenharmony_ci        buffer.readMatrix(&fLocalMatrixStorage);
118cb93a386Sopenharmony_ci    } else {
119cb93a386Sopenharmony_ci        fLocalMatrix = nullptr;
120cb93a386Sopenharmony_ci    }
121cb93a386Sopenharmony_ci    return buffer.isValid();
122cb93a386Sopenharmony_ci}
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////////////////////
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ciSkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
127cb93a386Sopenharmony_ci    : INHERITED(desc.fLocalMatrix)
128cb93a386Sopenharmony_ci    , fPtsToUnit(ptsToUnit)
129cb93a386Sopenharmony_ci    , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB())
130cb93a386Sopenharmony_ci    , fColorsAreOpaque(true)
131cb93a386Sopenharmony_ci{
132cb93a386Sopenharmony_ci    fPtsToUnit.getType();  // Precache so reads are threadsafe.
133cb93a386Sopenharmony_ci    SkASSERT(desc.fCount > 1);
134cb93a386Sopenharmony_ci
135cb93a386Sopenharmony_ci    fGradFlags = static_cast<uint8_t>(desc.fGradFlags);
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci    SkASSERT((unsigned)desc.fTileMode < kSkTileModeCount);
138cb93a386Sopenharmony_ci    fTileMode = desc.fTileMode;
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_ci    /*  Note: we let the caller skip the first and/or last position.
141cb93a386Sopenharmony_ci        i.e. pos[0] = 0.3, pos[1] = 0.7
142cb93a386Sopenharmony_ci        In these cases, we insert entries to ensure that the final data
143cb93a386Sopenharmony_ci        will be bracketed by [0, 1].
144cb93a386Sopenharmony_ci        i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
145cb93a386Sopenharmony_ci
146cb93a386Sopenharmony_ci        Thus colorCount (the caller's value, and fColorCount (our value) may
147cb93a386Sopenharmony_ci        differ by up to 2. In the above example:
148cb93a386Sopenharmony_ci            colorCount = 2
149cb93a386Sopenharmony_ci            fColorCount = 4
150cb93a386Sopenharmony_ci     */
151cb93a386Sopenharmony_ci    fColorCount = desc.fCount;
152cb93a386Sopenharmony_ci    // check if we need to add in start and/or end position/colors
153cb93a386Sopenharmony_ci    bool needsFirst = false;
154cb93a386Sopenharmony_ci    bool needsLast = false;
155cb93a386Sopenharmony_ci    if (desc.fPos) {
156cb93a386Sopenharmony_ci        needsFirst = desc.fPos[0] != 0;
157cb93a386Sopenharmony_ci        needsLast = desc.fPos[desc.fCount - 1] != SK_Scalar1;
158cb93a386Sopenharmony_ci        fColorCount += needsFirst + needsLast;
159cb93a386Sopenharmony_ci    }
160cb93a386Sopenharmony_ci
161cb93a386Sopenharmony_ci    size_t storageSize = fColorCount * (sizeof(SkColor4f) + (desc.fPos ? sizeof(SkScalar) : 0));
162cb93a386Sopenharmony_ci    fOrigColors4f      = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
163cb93a386Sopenharmony_ci    fOrigPos           = desc.fPos ? reinterpret_cast<SkScalar*>(fOrigColors4f + fColorCount)
164cb93a386Sopenharmony_ci                                   : nullptr;
165cb93a386Sopenharmony_ci
166cb93a386Sopenharmony_ci    // Now copy over the colors, adding the dummies as needed
167cb93a386Sopenharmony_ci    SkColor4f* origColors = fOrigColors4f;
168cb93a386Sopenharmony_ci    if (needsFirst) {
169cb93a386Sopenharmony_ci        *origColors++ = desc.fColors[0];
170cb93a386Sopenharmony_ci    }
171cb93a386Sopenharmony_ci    for (int i = 0; i < desc.fCount; ++i) {
172cb93a386Sopenharmony_ci        origColors[i] = desc.fColors[i];
173cb93a386Sopenharmony_ci        fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1);
174cb93a386Sopenharmony_ci    }
175cb93a386Sopenharmony_ci    if (needsLast) {
176cb93a386Sopenharmony_ci        origColors += desc.fCount;
177cb93a386Sopenharmony_ci        *origColors = desc.fColors[desc.fCount - 1];
178cb93a386Sopenharmony_ci    }
179cb93a386Sopenharmony_ci
180cb93a386Sopenharmony_ci    if (desc.fPos) {
181cb93a386Sopenharmony_ci        SkScalar prev = 0;
182cb93a386Sopenharmony_ci        SkScalar* origPosPtr = fOrigPos;
183cb93a386Sopenharmony_ci        *origPosPtr++ = prev; // force the first pos to 0
184cb93a386Sopenharmony_ci
185cb93a386Sopenharmony_ci        int startIndex = needsFirst ? 0 : 1;
186cb93a386Sopenharmony_ci        int count = desc.fCount + needsLast;
187cb93a386Sopenharmony_ci
188cb93a386Sopenharmony_ci        bool uniformStops = true;
189cb93a386Sopenharmony_ci        const SkScalar uniformStep = desc.fPos[startIndex] - prev;
190cb93a386Sopenharmony_ci        for (int i = startIndex; i < count; i++) {
191cb93a386Sopenharmony_ci            // Pin the last value to 1.0, and make sure pos is monotonic.
192cb93a386Sopenharmony_ci            auto curr = (i == desc.fCount) ? 1 : SkTPin(desc.fPos[i], prev, 1.0f);
193cb93a386Sopenharmony_ci            uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev);
194cb93a386Sopenharmony_ci
195cb93a386Sopenharmony_ci            *origPosPtr++ = prev = curr;
196cb93a386Sopenharmony_ci        }
197cb93a386Sopenharmony_ci
198cb93a386Sopenharmony_ci        // If the stops are uniform, treat them as implicit.
199cb93a386Sopenharmony_ci        if (uniformStops) {
200cb93a386Sopenharmony_ci            fOrigPos = nullptr;
201cb93a386Sopenharmony_ci        }
202cb93a386Sopenharmony_ci    }
203cb93a386Sopenharmony_ci}
204cb93a386Sopenharmony_ci
205cb93a386Sopenharmony_ciSkGradientShaderBase::~SkGradientShaderBase() {}
206cb93a386Sopenharmony_ci
207cb93a386Sopenharmony_civoid SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
208cb93a386Sopenharmony_ci    Descriptor desc;
209cb93a386Sopenharmony_ci    desc.fColors = fOrigColors4f;
210cb93a386Sopenharmony_ci    desc.fColorSpace = fColorSpace;
211cb93a386Sopenharmony_ci    desc.fPos = fOrigPos;
212cb93a386Sopenharmony_ci    desc.fCount = fColorCount;
213cb93a386Sopenharmony_ci    desc.fTileMode = fTileMode;
214cb93a386Sopenharmony_ci    desc.fGradFlags = fGradFlags;
215cb93a386Sopenharmony_ci
216cb93a386Sopenharmony_ci    const SkMatrix& m = this->getLocalMatrix();
217cb93a386Sopenharmony_ci    desc.fLocalMatrix = m.isIdentity() ? nullptr : &m;
218cb93a386Sopenharmony_ci    desc.flatten(buffer);
219cb93a386Sopenharmony_ci}
220cb93a386Sopenharmony_ci
221cb93a386Sopenharmony_cistatic void add_stop_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f Fs, SkPMColor4f Bs) {
222cb93a386Sopenharmony_ci    (ctx->fs[0])[stop] = Fs.fR;
223cb93a386Sopenharmony_ci    (ctx->fs[1])[stop] = Fs.fG;
224cb93a386Sopenharmony_ci    (ctx->fs[2])[stop] = Fs.fB;
225cb93a386Sopenharmony_ci    (ctx->fs[3])[stop] = Fs.fA;
226cb93a386Sopenharmony_ci
227cb93a386Sopenharmony_ci    (ctx->bs[0])[stop] = Bs.fR;
228cb93a386Sopenharmony_ci    (ctx->bs[1])[stop] = Bs.fG;
229cb93a386Sopenharmony_ci    (ctx->bs[2])[stop] = Bs.fB;
230cb93a386Sopenharmony_ci    (ctx->bs[3])[stop] = Bs.fA;
231cb93a386Sopenharmony_ci}
232cb93a386Sopenharmony_ci
233cb93a386Sopenharmony_cistatic void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f color) {
234cb93a386Sopenharmony_ci    add_stop_color(ctx, stop, { 0, 0, 0, 0 }, color);
235cb93a386Sopenharmony_ci}
236cb93a386Sopenharmony_ci
237cb93a386Sopenharmony_ci// Calculate a factor F and a bias B so that color = F*t + B when t is in range of
238cb93a386Sopenharmony_ci// the stop. Assume that the distance between stops is 1/gapCount.
239cb93a386Sopenharmony_cistatic void init_stop_evenly(
240cb93a386Sopenharmony_ci    SkRasterPipeline_GradientCtx* ctx, float gapCount, size_t stop, SkPMColor4f c_l, SkPMColor4f c_r) {
241cb93a386Sopenharmony_ci    // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
242cb93a386Sopenharmony_ci    SkPMColor4f Fs = {
243cb93a386Sopenharmony_ci        (c_r.fR - c_l.fR) * gapCount,
244cb93a386Sopenharmony_ci        (c_r.fG - c_l.fG) * gapCount,
245cb93a386Sopenharmony_ci        (c_r.fB - c_l.fB) * gapCount,
246cb93a386Sopenharmony_ci        (c_r.fA - c_l.fA) * gapCount,
247cb93a386Sopenharmony_ci    };
248cb93a386Sopenharmony_ci    SkPMColor4f Bs = {
249cb93a386Sopenharmony_ci        c_l.fR - Fs.fR*(stop/gapCount),
250cb93a386Sopenharmony_ci        c_l.fG - Fs.fG*(stop/gapCount),
251cb93a386Sopenharmony_ci        c_l.fB - Fs.fB*(stop/gapCount),
252cb93a386Sopenharmony_ci        c_l.fA - Fs.fA*(stop/gapCount),
253cb93a386Sopenharmony_ci    };
254cb93a386Sopenharmony_ci    add_stop_color(ctx, stop, Fs, Bs);
255cb93a386Sopenharmony_ci}
256cb93a386Sopenharmony_ci
257cb93a386Sopenharmony_ci// For each stop we calculate a bias B and a scale factor F, such that
258cb93a386Sopenharmony_ci// for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
259cb93a386Sopenharmony_cistatic void init_stop_pos(
260cb93a386Sopenharmony_ci    SkRasterPipeline_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPMColor4f c_l, SkPMColor4f c_r) {
261cb93a386Sopenharmony_ci    // See note about Clankium's old compiler in init_stop_evenly().
262cb93a386Sopenharmony_ci    SkPMColor4f Fs = {
263cb93a386Sopenharmony_ci        (c_r.fR - c_l.fR) / (t_r - t_l),
264cb93a386Sopenharmony_ci        (c_r.fG - c_l.fG) / (t_r - t_l),
265cb93a386Sopenharmony_ci        (c_r.fB - c_l.fB) / (t_r - t_l),
266cb93a386Sopenharmony_ci        (c_r.fA - c_l.fA) / (t_r - t_l),
267cb93a386Sopenharmony_ci    };
268cb93a386Sopenharmony_ci    SkPMColor4f Bs = {
269cb93a386Sopenharmony_ci        c_l.fR - Fs.fR*t_l,
270cb93a386Sopenharmony_ci        c_l.fG - Fs.fG*t_l,
271cb93a386Sopenharmony_ci        c_l.fB - Fs.fB*t_l,
272cb93a386Sopenharmony_ci        c_l.fA - Fs.fA*t_l,
273cb93a386Sopenharmony_ci    };
274cb93a386Sopenharmony_ci    ctx->ts[stop] = t_l;
275cb93a386Sopenharmony_ci    add_stop_color(ctx, stop, Fs, Bs);
276cb93a386Sopenharmony_ci}
277cb93a386Sopenharmony_ci
278cb93a386Sopenharmony_cibool SkGradientShaderBase::onAppendStages(const SkStageRec& rec) const {
279cb93a386Sopenharmony_ci    SkRasterPipeline* p = rec.fPipeline;
280cb93a386Sopenharmony_ci    SkArenaAlloc* alloc = rec.fAlloc;
281cb93a386Sopenharmony_ci    SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
282cb93a386Sopenharmony_ci
283cb93a386Sopenharmony_ci    SkMatrix matrix;
284cb93a386Sopenharmony_ci    if (!this->computeTotalInverse(rec.fMatrixProvider.localToDevice(), rec.fLocalM, &matrix)) {
285cb93a386Sopenharmony_ci        return false;
286cb93a386Sopenharmony_ci    }
287cb93a386Sopenharmony_ci    matrix.postConcat(fPtsToUnit);
288cb93a386Sopenharmony_ci
289cb93a386Sopenharmony_ci    SkRasterPipeline_<256> postPipeline;
290cb93a386Sopenharmony_ci
291cb93a386Sopenharmony_ci    p->append(SkRasterPipeline::seed_shader);
292cb93a386Sopenharmony_ci    p->append_matrix(alloc, matrix);
293cb93a386Sopenharmony_ci    this->appendGradientStages(alloc, p, &postPipeline);
294cb93a386Sopenharmony_ci
295cb93a386Sopenharmony_ci    switch(fTileMode) {
296cb93a386Sopenharmony_ci        case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x_1); break;
297cb93a386Sopenharmony_ci        case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x_1); break;
298cb93a386Sopenharmony_ci        case SkTileMode::kDecal:
299cb93a386Sopenharmony_ci            decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
300cb93a386Sopenharmony_ci            decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1);
301cb93a386Sopenharmony_ci            // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask
302cb93a386Sopenharmony_ci            p->append(SkRasterPipeline::decal_x, decal_ctx);
303cb93a386Sopenharmony_ci            [[fallthrough]];
304cb93a386Sopenharmony_ci
305cb93a386Sopenharmony_ci        case SkTileMode::kClamp:
306cb93a386Sopenharmony_ci            if (!fOrigPos) {
307cb93a386Sopenharmony_ci                // We clamp only when the stops are evenly spaced.
308cb93a386Sopenharmony_ci                // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1.
309cb93a386Sopenharmony_ci                // In that case, we must make sure we're using the general "gradient" stage,
310cb93a386Sopenharmony_ci                // which is the only stage that will correctly handle unclamped t.
311cb93a386Sopenharmony_ci                p->append(SkRasterPipeline::clamp_x_1);
312cb93a386Sopenharmony_ci            }
313cb93a386Sopenharmony_ci            break;
314cb93a386Sopenharmony_ci    }
315cb93a386Sopenharmony_ci
316cb93a386Sopenharmony_ci    const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
317cb93a386Sopenharmony_ci
318cb93a386Sopenharmony_ci    // Transform all of the colors to destination color space
319cb93a386Sopenharmony_ci    SkColor4fXformer xformedColors(fOrigColors4f, fColorCount, fColorSpace.get(), rec.fDstCS);
320cb93a386Sopenharmony_ci
321cb93a386Sopenharmony_ci    auto prepareColor = [premulGrad, &xformedColors](int i) {
322cb93a386Sopenharmony_ci        SkColor4f c = xformedColors.fColors[i];
323cb93a386Sopenharmony_ci        return premulGrad ? c.premul()
324cb93a386Sopenharmony_ci                          : SkPMColor4f{ c.fR, c.fG, c.fB, c.fA };
325cb93a386Sopenharmony_ci    };
326cb93a386Sopenharmony_ci
327cb93a386Sopenharmony_ci    // The two-stop case with stops at 0 and 1.
328cb93a386Sopenharmony_ci    if (fColorCount == 2 && fOrigPos == nullptr) {
329cb93a386Sopenharmony_ci        const SkPMColor4f c_l = prepareColor(0),
330cb93a386Sopenharmony_ci                          c_r = prepareColor(1);
331cb93a386Sopenharmony_ci
332cb93a386Sopenharmony_ci        // See F and B below.
333cb93a386Sopenharmony_ci        auto ctx = alloc->make<SkRasterPipeline_EvenlySpaced2StopGradientCtx>();
334cb93a386Sopenharmony_ci        (Sk4f::Load(c_r.vec()) - Sk4f::Load(c_l.vec())).store(ctx->f);
335cb93a386Sopenharmony_ci        (                        Sk4f::Load(c_l.vec())).store(ctx->b);
336cb93a386Sopenharmony_ci        ctx->interpolatedInPremul = premulGrad;
337cb93a386Sopenharmony_ci
338cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, ctx);
339cb93a386Sopenharmony_ci    } else {
340cb93a386Sopenharmony_ci        auto* ctx = alloc->make<SkRasterPipeline_GradientCtx>();
341cb93a386Sopenharmony_ci        ctx->interpolatedInPremul = premulGrad;
342cb93a386Sopenharmony_ci
343cb93a386Sopenharmony_ci        // Note: In order to handle clamps in search, the search assumes a stop conceptully placed
344cb93a386Sopenharmony_ci        // at -inf. Therefore, the max number of stops is fColorCount+1.
345cb93a386Sopenharmony_ci        for (int i = 0; i < 4; i++) {
346cb93a386Sopenharmony_ci            // Allocate at least at for the AVX2 gather from a YMM register.
347cb93a386Sopenharmony_ci            ctx->fs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
348cb93a386Sopenharmony_ci            ctx->bs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
349cb93a386Sopenharmony_ci        }
350cb93a386Sopenharmony_ci
351cb93a386Sopenharmony_ci        if (fOrigPos == nullptr) {
352cb93a386Sopenharmony_ci            // Handle evenly distributed stops.
353cb93a386Sopenharmony_ci
354cb93a386Sopenharmony_ci            size_t stopCount = fColorCount;
355cb93a386Sopenharmony_ci            float gapCount = stopCount - 1;
356cb93a386Sopenharmony_ci
357cb93a386Sopenharmony_ci            SkPMColor4f c_l = prepareColor(0);
358cb93a386Sopenharmony_ci            for (size_t i = 0; i < stopCount - 1; i++) {
359cb93a386Sopenharmony_ci                SkPMColor4f c_r = prepareColor(i + 1);
360cb93a386Sopenharmony_ci                init_stop_evenly(ctx, gapCount, i, c_l, c_r);
361cb93a386Sopenharmony_ci                c_l = c_r;
362cb93a386Sopenharmony_ci            }
363cb93a386Sopenharmony_ci            add_const_color(ctx, stopCount - 1, c_l);
364cb93a386Sopenharmony_ci
365cb93a386Sopenharmony_ci            ctx->stopCount = stopCount;
366cb93a386Sopenharmony_ci            p->append(SkRasterPipeline::evenly_spaced_gradient, ctx);
367cb93a386Sopenharmony_ci        } else {
368cb93a386Sopenharmony_ci            // Handle arbitrary stops.
369cb93a386Sopenharmony_ci
370cb93a386Sopenharmony_ci            ctx->ts = alloc->makeArray<float>(fColorCount+1);
371cb93a386Sopenharmony_ci
372cb93a386Sopenharmony_ci            // Remove the default stops inserted by SkGradientShaderBase::SkGradientShaderBase
373cb93a386Sopenharmony_ci            // because they are naturally handled by the search method.
374cb93a386Sopenharmony_ci            int firstStop;
375cb93a386Sopenharmony_ci            int lastStop;
376cb93a386Sopenharmony_ci            if (fColorCount > 2) {
377cb93a386Sopenharmony_ci                firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
378cb93a386Sopenharmony_ci                lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
379cb93a386Sopenharmony_ci                           ? fColorCount - 1 : fColorCount - 2;
380cb93a386Sopenharmony_ci            } else {
381cb93a386Sopenharmony_ci                firstStop = 0;
382cb93a386Sopenharmony_ci                lastStop = 1;
383cb93a386Sopenharmony_ci            }
384cb93a386Sopenharmony_ci
385cb93a386Sopenharmony_ci            size_t stopCount = 0;
386cb93a386Sopenharmony_ci            float  t_l = fOrigPos[firstStop];
387cb93a386Sopenharmony_ci            SkPMColor4f c_l = prepareColor(firstStop);
388cb93a386Sopenharmony_ci            add_const_color(ctx, stopCount++, c_l);
389cb93a386Sopenharmony_ci            // N.B. lastStop is the index of the last stop, not one after.
390cb93a386Sopenharmony_ci            for (int i = firstStop; i < lastStop; i++) {
391cb93a386Sopenharmony_ci                float  t_r = fOrigPos[i + 1];
392cb93a386Sopenharmony_ci                SkPMColor4f c_r = prepareColor(i + 1);
393cb93a386Sopenharmony_ci                SkASSERT(t_l <= t_r);
394cb93a386Sopenharmony_ci                if (t_l < t_r) {
395cb93a386Sopenharmony_ci                    init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r);
396cb93a386Sopenharmony_ci                    stopCount += 1;
397cb93a386Sopenharmony_ci                }
398cb93a386Sopenharmony_ci                t_l = t_r;
399cb93a386Sopenharmony_ci                c_l = c_r;
400cb93a386Sopenharmony_ci            }
401cb93a386Sopenharmony_ci
402cb93a386Sopenharmony_ci            ctx->ts[stopCount] = t_l;
403cb93a386Sopenharmony_ci            add_const_color(ctx, stopCount++, c_l);
404cb93a386Sopenharmony_ci
405cb93a386Sopenharmony_ci            ctx->stopCount = stopCount;
406cb93a386Sopenharmony_ci            p->append(SkRasterPipeline::gradient, ctx);
407cb93a386Sopenharmony_ci        }
408cb93a386Sopenharmony_ci    }
409cb93a386Sopenharmony_ci
410cb93a386Sopenharmony_ci    if (decal_ctx) {
411cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
412cb93a386Sopenharmony_ci    }
413cb93a386Sopenharmony_ci
414cb93a386Sopenharmony_ci    if (!premulGrad && !this->colorsAreOpaque()) {
415cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::premul);
416cb93a386Sopenharmony_ci    }
417cb93a386Sopenharmony_ci
418cb93a386Sopenharmony_ci    p->extend(postPipeline);
419cb93a386Sopenharmony_ci
420cb93a386Sopenharmony_ci    return true;
421cb93a386Sopenharmony_ci}
422cb93a386Sopenharmony_ci
423cb93a386Sopenharmony_ciskvm::Color SkGradientShaderBase::onProgram(skvm::Builder* p,
424cb93a386Sopenharmony_ci                                            skvm::Coord device, skvm::Coord local,
425cb93a386Sopenharmony_ci                                            skvm::Color /*paint*/,
426cb93a386Sopenharmony_ci                                            const SkMatrixProvider& mats, const SkMatrix* localM,
427cb93a386Sopenharmony_ci                                            const SkColorInfo& dstInfo,
428cb93a386Sopenharmony_ci                                            skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
429cb93a386Sopenharmony_ci    SkMatrix inv;
430cb93a386Sopenharmony_ci    if (!this->computeTotalInverse(mats.localToDevice(), localM, &inv)) {
431cb93a386Sopenharmony_ci        return {};
432cb93a386Sopenharmony_ci    }
433cb93a386Sopenharmony_ci    inv.postConcat(fPtsToUnit);
434cb93a386Sopenharmony_ci    inv.normalizePerspective();
435cb93a386Sopenharmony_ci
436cb93a386Sopenharmony_ci    local = SkShaderBase::ApplyMatrix(p, inv, local, uniforms);
437cb93a386Sopenharmony_ci
438cb93a386Sopenharmony_ci    skvm::I32 mask = p->splat(~0);
439cb93a386Sopenharmony_ci    skvm::F32 t = this->transformT(p,uniforms, local, &mask);
440cb93a386Sopenharmony_ci
441cb93a386Sopenharmony_ci    // Perhaps unexpectedly, clamping is handled naturally by our search, so we
442cb93a386Sopenharmony_ci    // don't explicitly clamp t to [0,1].  That clamp would break hard stops
443cb93a386Sopenharmony_ci    // right at 0 or 1 boundaries in kClamp mode.  (kRepeat and kMirror always
444cb93a386Sopenharmony_ci    // produce values in [0,1].)
445cb93a386Sopenharmony_ci    switch(fTileMode) {
446cb93a386Sopenharmony_ci        case SkTileMode::kClamp:
447cb93a386Sopenharmony_ci            break;
448cb93a386Sopenharmony_ci
449cb93a386Sopenharmony_ci        case SkTileMode::kDecal:
450cb93a386Sopenharmony_ci            mask &= (t == clamp01(t));
451cb93a386Sopenharmony_ci            break;
452cb93a386Sopenharmony_ci
453cb93a386Sopenharmony_ci        case SkTileMode::kRepeat:
454cb93a386Sopenharmony_ci            t = fract(t);
455cb93a386Sopenharmony_ci            break;
456cb93a386Sopenharmony_ci
457cb93a386Sopenharmony_ci        case SkTileMode::kMirror: {
458cb93a386Sopenharmony_ci            // t = | (t-1) - 2*(floor( (t-1)*0.5 )) - 1 |
459cb93a386Sopenharmony_ci            //       {-A-}      {--------B-------}
460cb93a386Sopenharmony_ci            skvm::F32 A = t - 1.0f,
461cb93a386Sopenharmony_ci                      B = floor(A * 0.5f);
462cb93a386Sopenharmony_ci            t = abs(A - (B + B) - 1.0f);
463cb93a386Sopenharmony_ci        } break;
464cb93a386Sopenharmony_ci    }
465cb93a386Sopenharmony_ci
466cb93a386Sopenharmony_ci    // Transform our colors as we want them interpolated, in dst color space, possibly premul.
467cb93a386Sopenharmony_ci    SkImageInfo common = SkImageInfo::Make(fColorCount,1, kRGBA_F32_SkColorType
468cb93a386Sopenharmony_ci                                                        , kUnpremul_SkAlphaType),
469cb93a386Sopenharmony_ci                src    = common.makeColorSpace(fColorSpace),
470cb93a386Sopenharmony_ci                dst    = common.makeColorSpace(dstInfo.refColorSpace());
471cb93a386Sopenharmony_ci    if (fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) {
472cb93a386Sopenharmony_ci        dst = dst.makeAlphaType(kPremul_SkAlphaType);
473cb93a386Sopenharmony_ci    }
474cb93a386Sopenharmony_ci
475cb93a386Sopenharmony_ci    std::vector<float> rgba(4*fColorCount);  // TODO: SkSTArray?
476cb93a386Sopenharmony_ci    SkAssertResult(SkConvertPixels(dst,   rgba.data(), dst.minRowBytes(),
477cb93a386Sopenharmony_ci                                   src, fOrigColors4f, src.minRowBytes()));
478cb93a386Sopenharmony_ci
479cb93a386Sopenharmony_ci    // Transform our colors into a scale factor f and bias b such that for
480cb93a386Sopenharmony_ci    // any t between stops i and i+1, the color we want is mad(t, f[i], b[i]).
481cb93a386Sopenharmony_ci    using F4 = skvx::Vec<4,float>;
482cb93a386Sopenharmony_ci    struct FB { F4 f,b; };
483cb93a386Sopenharmony_ci    skvm::Color color;
484cb93a386Sopenharmony_ci
485cb93a386Sopenharmony_ci    auto uniformF = [&](float x) { return p->uniformF(uniforms->pushF(x)); };
486cb93a386Sopenharmony_ci
487cb93a386Sopenharmony_ci    if (fColorCount == 2) {
488cb93a386Sopenharmony_ci        // 2-stop gradients have colors at 0 and 1, and so must be evenly spaced.
489cb93a386Sopenharmony_ci        SkASSERT(fOrigPos == nullptr);
490cb93a386Sopenharmony_ci
491cb93a386Sopenharmony_ci        // With 2 stops, we upload the single FB as uniforms and interpolate directly with t.
492cb93a386Sopenharmony_ci        F4 lo = F4::Load(rgba.data() + 0),
493cb93a386Sopenharmony_ci           hi = F4::Load(rgba.data() + 4);
494cb93a386Sopenharmony_ci        F4 F = hi - lo,
495cb93a386Sopenharmony_ci           B = lo;
496cb93a386Sopenharmony_ci
497cb93a386Sopenharmony_ci        auto T = clamp01(t);
498cb93a386Sopenharmony_ci        color = {
499cb93a386Sopenharmony_ci            T * uniformF(F[0]) + uniformF(B[0]),
500cb93a386Sopenharmony_ci            T * uniformF(F[1]) + uniformF(B[1]),
501cb93a386Sopenharmony_ci            T * uniformF(F[2]) + uniformF(B[2]),
502cb93a386Sopenharmony_ci            T * uniformF(F[3]) + uniformF(B[3]),
503cb93a386Sopenharmony_ci        };
504cb93a386Sopenharmony_ci    } else {
505cb93a386Sopenharmony_ci        // To handle clamps in search we add a conceptual stop at t=-inf, so we
506cb93a386Sopenharmony_ci        // may need up to fColorCount+1 FBs and fColorCount t stops between them:
507cb93a386Sopenharmony_ci        //
508cb93a386Sopenharmony_ci        //   FBs:         [color 0]  [color 0->1]  [color 1->2]  [color 2->3]  ...
509cb93a386Sopenharmony_ci        //   stops:  (-inf)        t0            t1            t2  ...
510cb93a386Sopenharmony_ci        //
511cb93a386Sopenharmony_ci        // Both these arrays could end up shorter if any hard stops share the same t.
512cb93a386Sopenharmony_ci        FB* fb = alloc->makeArrayDefault<FB>(fColorCount+1);
513cb93a386Sopenharmony_ci        std::vector<float> stops;  // TODO: SkSTArray?
514cb93a386Sopenharmony_ci        stops.reserve(fColorCount);
515cb93a386Sopenharmony_ci
516cb93a386Sopenharmony_ci        // Here's our conceptual stop at t=-inf covering all t<=0, clamping to our first color.
517cb93a386Sopenharmony_ci        float  t_lo = this->getPos(0);
518cb93a386Sopenharmony_ci        F4 color_lo = F4::Load(rgba.data());
519cb93a386Sopenharmony_ci        fb[0] = { 0.0f, color_lo };
520cb93a386Sopenharmony_ci        // N.B. No stops[] entry for this implicit -inf.
521cb93a386Sopenharmony_ci
522cb93a386Sopenharmony_ci        // Now the non-edge cases, calculating scale and bias between adjacent normal stops.
523cb93a386Sopenharmony_ci        for (int i = 1; i < fColorCount; i++) {
524cb93a386Sopenharmony_ci            float  t_hi = this->getPos(i);
525cb93a386Sopenharmony_ci            F4 color_hi = F4::Load(rgba.data() + 4*i);
526cb93a386Sopenharmony_ci
527cb93a386Sopenharmony_ci            // If t_lo == t_hi, we're on a hard stop, and transition immediately to the next color.
528cb93a386Sopenharmony_ci            SkASSERT(t_lo <= t_hi);
529cb93a386Sopenharmony_ci            if (t_lo < t_hi) {
530cb93a386Sopenharmony_ci                F4 f = (color_hi - color_lo) / (t_hi - t_lo),
531cb93a386Sopenharmony_ci                   b = color_lo - f*t_lo;
532cb93a386Sopenharmony_ci                stops.push_back(t_lo);
533cb93a386Sopenharmony_ci                fb[stops.size()] = {f,b};
534cb93a386Sopenharmony_ci            }
535cb93a386Sopenharmony_ci
536cb93a386Sopenharmony_ci            t_lo = t_hi;
537cb93a386Sopenharmony_ci            color_lo = color_hi;
538cb93a386Sopenharmony_ci        }
539cb93a386Sopenharmony_ci        // Anything >= our final t clamps to our final color.
540cb93a386Sopenharmony_ci        stops.push_back(t_lo);
541cb93a386Sopenharmony_ci        fb[stops.size()] = { 0.0f, color_lo };
542cb93a386Sopenharmony_ci
543cb93a386Sopenharmony_ci        // We'll gather FBs from that array we just created.
544cb93a386Sopenharmony_ci        skvm::Uniform fbs = uniforms->pushPtr(fb);
545cb93a386Sopenharmony_ci
546cb93a386Sopenharmony_ci        // Find the two stops we need to interpolate.
547cb93a386Sopenharmony_ci        skvm::I32 ix;
548cb93a386Sopenharmony_ci        if (fOrigPos == nullptr) {
549cb93a386Sopenharmony_ci            // Evenly spaced stops... we can calculate ix directly.
550cb93a386Sopenharmony_ci            // Of note: we need to clamp t and skip over that conceptual -inf stop we made up.
551cb93a386Sopenharmony_ci            ix = trunc(clamp01(t) * uniformF(stops.size() - 1) + 1.0f);
552cb93a386Sopenharmony_ci        } else {
553cb93a386Sopenharmony_ci            // Starting ix at 0 bakes in our conceptual first stop at -inf.
554cb93a386Sopenharmony_ci            // TODO: good place to experiment with a loop in skvm.... stops.size() can be huge.
555cb93a386Sopenharmony_ci            ix = p->splat(0);
556cb93a386Sopenharmony_ci            for (float stop : stops) {
557cb93a386Sopenharmony_ci                // ix += (t >= stop) ? +1 : 0 ~~>
558cb93a386Sopenharmony_ci                // ix -= (t >= stop) ? -1 : 0
559cb93a386Sopenharmony_ci                ix -= (t >= uniformF(stop));
560cb93a386Sopenharmony_ci            }
561cb93a386Sopenharmony_ci            // TODO: we could skip any of the default stops GradientShaderBase's ctor added
562cb93a386Sopenharmony_ci            // to ensure the full [0,1] span is covered.  This linear search doesn't need
563cb93a386Sopenharmony_ci            // them for correctness, and it'd be up to two fewer stops to check.
564cb93a386Sopenharmony_ci            // N.B. we do still need those stops for the fOrigPos == nullptr direct math path.
565cb93a386Sopenharmony_ci        }
566cb93a386Sopenharmony_ci
567cb93a386Sopenharmony_ci        // A scale factor and bias for each lane, 8 total.
568cb93a386Sopenharmony_ci        // TODO: simpler, faster, tidier to push 8 uniform pointers, one for each struct lane?
569cb93a386Sopenharmony_ci        ix = shl(ix, 3);
570cb93a386Sopenharmony_ci        skvm::F32 Fr = gatherF(fbs, ix + 0);
571cb93a386Sopenharmony_ci        skvm::F32 Fg = gatherF(fbs, ix + 1);
572cb93a386Sopenharmony_ci        skvm::F32 Fb = gatherF(fbs, ix + 2);
573cb93a386Sopenharmony_ci        skvm::F32 Fa = gatherF(fbs, ix + 3);
574cb93a386Sopenharmony_ci
575cb93a386Sopenharmony_ci        skvm::F32 Br = gatherF(fbs, ix + 4);
576cb93a386Sopenharmony_ci        skvm::F32 Bg = gatherF(fbs, ix + 5);
577cb93a386Sopenharmony_ci        skvm::F32 Bb = gatherF(fbs, ix + 6);
578cb93a386Sopenharmony_ci        skvm::F32 Ba = gatherF(fbs, ix + 7);
579cb93a386Sopenharmony_ci
580cb93a386Sopenharmony_ci        // This is what we've been building towards!
581cb93a386Sopenharmony_ci        color = {
582cb93a386Sopenharmony_ci            t * Fr + Br,
583cb93a386Sopenharmony_ci            t * Fg + Bg,
584cb93a386Sopenharmony_ci            t * Fb + Bb,
585cb93a386Sopenharmony_ci            t * Fa + Ba,
586cb93a386Sopenharmony_ci        };
587cb93a386Sopenharmony_ci    }
588cb93a386Sopenharmony_ci
589cb93a386Sopenharmony_ci    // If we interpolated unpremul, premul now to match our output convention.
590cb93a386Sopenharmony_ci    if (0 == (fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
591cb93a386Sopenharmony_ci            && !fColorsAreOpaque) {
592cb93a386Sopenharmony_ci        color = premul(color);
593cb93a386Sopenharmony_ci    }
594cb93a386Sopenharmony_ci
595cb93a386Sopenharmony_ci    return {
596cb93a386Sopenharmony_ci        pun_to_F32(mask & pun_to_I32(color.r)),
597cb93a386Sopenharmony_ci        pun_to_F32(mask & pun_to_I32(color.g)),
598cb93a386Sopenharmony_ci        pun_to_F32(mask & pun_to_I32(color.b)),
599cb93a386Sopenharmony_ci        pun_to_F32(mask & pun_to_I32(color.a)),
600cb93a386Sopenharmony_ci    };
601cb93a386Sopenharmony_ci}
602cb93a386Sopenharmony_ci
603cb93a386Sopenharmony_ci
604cb93a386Sopenharmony_cibool SkGradientShaderBase::isOpaque() const {
605cb93a386Sopenharmony_ci    return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal);
606cb93a386Sopenharmony_ci}
607cb93a386Sopenharmony_ci
608cb93a386Sopenharmony_cistatic unsigned rounded_divide(unsigned numer, unsigned denom) {
609cb93a386Sopenharmony_ci    return (numer + (denom >> 1)) / denom;
610cb93a386Sopenharmony_ci}
611cb93a386Sopenharmony_ci
612cb93a386Sopenharmony_cibool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
613cb93a386Sopenharmony_ci    // we just compute an average color.
614cb93a386Sopenharmony_ci    // possibly we could weight this based on the proportional width for each color
615cb93a386Sopenharmony_ci    //   assuming they are not evenly distributed in the fPos array.
616cb93a386Sopenharmony_ci    int r = 0;
617cb93a386Sopenharmony_ci    int g = 0;
618cb93a386Sopenharmony_ci    int b = 0;
619cb93a386Sopenharmony_ci    const int n = fColorCount;
620cb93a386Sopenharmony_ci    // TODO: use linear colors?
621cb93a386Sopenharmony_ci    for (int i = 0; i < n; ++i) {
622cb93a386Sopenharmony_ci        SkColor c = this->getLegacyColor(i);
623cb93a386Sopenharmony_ci        r += SkColorGetR(c);
624cb93a386Sopenharmony_ci        g += SkColorGetG(c);
625cb93a386Sopenharmony_ci        b += SkColorGetB(c);
626cb93a386Sopenharmony_ci    }
627cb93a386Sopenharmony_ci    *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n));
628cb93a386Sopenharmony_ci    return true;
629cb93a386Sopenharmony_ci}
630cb93a386Sopenharmony_ci
631cb93a386Sopenharmony_ciSkColor4fXformer::SkColor4fXformer(const SkColor4f* colors, int colorCount,
632cb93a386Sopenharmony_ci                                   SkColorSpace* src, SkColorSpace* dst) {
633cb93a386Sopenharmony_ci    fColors = colors;
634cb93a386Sopenharmony_ci
635cb93a386Sopenharmony_ci    if (dst && !SkColorSpace::Equals(src, dst)) {
636cb93a386Sopenharmony_ci        fStorage.reset(colorCount);
637cb93a386Sopenharmony_ci
638cb93a386Sopenharmony_ci        auto info = SkImageInfo::Make(colorCount,1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType);
639cb93a386Sopenharmony_ci
640cb93a386Sopenharmony_ci        auto dstInfo = info.makeColorSpace(sk_ref_sp(dst));
641cb93a386Sopenharmony_ci        auto srcInfo = info.makeColorSpace(sk_ref_sp(src));
642cb93a386Sopenharmony_ci        SkAssertResult(SkConvertPixels(dstInfo, fStorage.begin(), info.minRowBytes(),
643cb93a386Sopenharmony_ci                                       srcInfo, fColors         , info.minRowBytes()));
644cb93a386Sopenharmony_ci
645cb93a386Sopenharmony_ci        fColors = fStorage.begin();
646cb93a386Sopenharmony_ci    }
647cb93a386Sopenharmony_ci}
648cb93a386Sopenharmony_ci
649cb93a386Sopenharmony_civoid SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
650cb93a386Sopenharmony_ci    if (info) {
651cb93a386Sopenharmony_ci        if (info->fColorCount >= fColorCount) {
652cb93a386Sopenharmony_ci            if (info->fColors) {
653cb93a386Sopenharmony_ci                for (int i = 0; i < fColorCount; ++i) {
654cb93a386Sopenharmony_ci                    info->fColors[i] = this->getLegacyColor(i);
655cb93a386Sopenharmony_ci                }
656cb93a386Sopenharmony_ci            }
657cb93a386Sopenharmony_ci            if (info->fColorOffsets) {
658cb93a386Sopenharmony_ci                for (int i = 0; i < fColorCount; ++i) {
659cb93a386Sopenharmony_ci                    info->fColorOffsets[i] = this->getPos(i);
660cb93a386Sopenharmony_ci                }
661cb93a386Sopenharmony_ci            }
662cb93a386Sopenharmony_ci        }
663cb93a386Sopenharmony_ci        info->fColorCount = fColorCount;
664cb93a386Sopenharmony_ci        info->fTileMode = fTileMode;
665cb93a386Sopenharmony_ci        info->fGradientFlags = fGradFlags;
666cb93a386Sopenharmony_ci    }
667cb93a386Sopenharmony_ci}
668cb93a386Sopenharmony_ci
669cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
670cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
671cb93a386Sopenharmony_ci
672cb93a386Sopenharmony_ci// Return true if these parameters are valid/legal/safe to construct a gradient
673cb93a386Sopenharmony_ci//
674cb93a386Sopenharmony_cistatic bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count,
675cb93a386Sopenharmony_ci                       SkTileMode tileMode) {
676cb93a386Sopenharmony_ci    return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount;
677cb93a386Sopenharmony_ci}
678cb93a386Sopenharmony_ci
679cb93a386Sopenharmony_cistatic void desc_init(SkGradientShaderBase::Descriptor* desc,
680cb93a386Sopenharmony_ci                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
681cb93a386Sopenharmony_ci                      const SkScalar pos[], int colorCount,
682cb93a386Sopenharmony_ci                      SkTileMode mode, uint32_t flags, const SkMatrix* localMatrix) {
683cb93a386Sopenharmony_ci    SkASSERT(colorCount > 1);
684cb93a386Sopenharmony_ci
685cb93a386Sopenharmony_ci    desc->fColors       = colors;
686cb93a386Sopenharmony_ci    desc->fColorSpace   = std::move(colorSpace);
687cb93a386Sopenharmony_ci    desc->fPos          = pos;
688cb93a386Sopenharmony_ci    desc->fCount        = colorCount;
689cb93a386Sopenharmony_ci    desc->fTileMode     = mode;
690cb93a386Sopenharmony_ci    desc->fGradFlags    = flags;
691cb93a386Sopenharmony_ci    desc->fLocalMatrix  = localMatrix;
692cb93a386Sopenharmony_ci}
693cb93a386Sopenharmony_ci
694cb93a386Sopenharmony_cistatic SkColor4f average_gradient_color(const SkColor4f colors[], const SkScalar pos[],
695cb93a386Sopenharmony_ci                                        int colorCount) {
696cb93a386Sopenharmony_ci    // The gradient is a piecewise linear interpolation between colors. For a given interval,
697cb93a386Sopenharmony_ci    // the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that
698cb93a386Sopenharmony_ci    // intervals average color. The overall average color is thus the sum of each piece. The thing
699cb93a386Sopenharmony_ci    // to keep in mind is that the provided gradient definition may implicitly use p=0 and p=1.
700cb93a386Sopenharmony_ci    Sk4f blend(0.0f);
701cb93a386Sopenharmony_ci    for (int i = 0; i < colorCount - 1; ++i) {
702cb93a386Sopenharmony_ci        // Calculate the average color for the interval between pos(i) and pos(i+1)
703cb93a386Sopenharmony_ci        Sk4f c0 = Sk4f::Load(&colors[i]);
704cb93a386Sopenharmony_ci        Sk4f c1 = Sk4f::Load(&colors[i + 1]);
705cb93a386Sopenharmony_ci
706cb93a386Sopenharmony_ci        // when pos == null, there are colorCount uniformly distributed stops, going from 0 to 1,
707cb93a386Sopenharmony_ci        // so pos[i + 1] - pos[i] = 1/(colorCount-1)
708cb93a386Sopenharmony_ci        SkScalar w;
709cb93a386Sopenharmony_ci        if (pos) {
710cb93a386Sopenharmony_ci            // Match position fixing in SkGradientShader's constructor, clamping positions outside
711cb93a386Sopenharmony_ci            // [0, 1] and forcing the sequence to be monotonic
712cb93a386Sopenharmony_ci            SkScalar p0 = SkTPin(pos[i], 0.f, 1.f);
713cb93a386Sopenharmony_ci            SkScalar p1 = SkTPin(pos[i + 1], p0, 1.f);
714cb93a386Sopenharmony_ci            w = p1 - p0;
715cb93a386Sopenharmony_ci
716cb93a386Sopenharmony_ci            // And account for any implicit intervals at the start or end of the positions
717cb93a386Sopenharmony_ci            if (i == 0) {
718cb93a386Sopenharmony_ci                if (p0 > 0.0f) {
719cb93a386Sopenharmony_ci                    // The first color is fixed between p = 0 to pos[0], so 0.5*(ci + cj)*(pj - pi)
720cb93a386Sopenharmony_ci                    // becomes 0.5*(c + c)*(pj - 0) = c * pj
721cb93a386Sopenharmony_ci                    Sk4f c = Sk4f::Load(&colors[0]);
722cb93a386Sopenharmony_ci                    blend += p0 * c;
723cb93a386Sopenharmony_ci                }
724cb93a386Sopenharmony_ci            }
725cb93a386Sopenharmony_ci            if (i == colorCount - 2) {
726cb93a386Sopenharmony_ci                if (p1 < 1.f) {
727cb93a386Sopenharmony_ci                    // The last color is fixed between pos[n-1] to p = 1, so 0.5*(ci + cj)*(pj - pi)
728cb93a386Sopenharmony_ci                    // becomes 0.5*(c + c)*(1 - pi) = c * (1 - pi)
729cb93a386Sopenharmony_ci                    Sk4f c = Sk4f::Load(&colors[colorCount - 1]);
730cb93a386Sopenharmony_ci                    blend += (1.f - p1) * c;
731cb93a386Sopenharmony_ci                }
732cb93a386Sopenharmony_ci            }
733cb93a386Sopenharmony_ci        } else {
734cb93a386Sopenharmony_ci            w = 1.f / (colorCount - 1);
735cb93a386Sopenharmony_ci        }
736cb93a386Sopenharmony_ci
737cb93a386Sopenharmony_ci        blend += 0.5f * w * (c1 + c0);
738cb93a386Sopenharmony_ci    }
739cb93a386Sopenharmony_ci
740cb93a386Sopenharmony_ci    SkColor4f avg;
741cb93a386Sopenharmony_ci    blend.store(&avg);
742cb93a386Sopenharmony_ci    return avg;
743cb93a386Sopenharmony_ci}
744cb93a386Sopenharmony_ci
745cb93a386Sopenharmony_ci// The default SkScalarNearlyZero threshold of .0024 is too big and causes regressions for svg
746cb93a386Sopenharmony_ci// gradients defined in the wild.
747cb93a386Sopenharmony_cistatic constexpr SkScalar kDegenerateThreshold = SK_Scalar1 / (1 << 15);
748cb93a386Sopenharmony_ci
749cb93a386Sopenharmony_ci// Except for special circumstances of clamped gradients, every gradient shape--when degenerate--
750cb93a386Sopenharmony_ci// can be mapped to the same fallbacks. The specific shape factories must account for special
751cb93a386Sopenharmony_ci// clamped conditions separately, this will always return the last color for clamped gradients.
752cb93a386Sopenharmony_cistatic sk_sp<SkShader> make_degenerate_gradient(const SkColor4f colors[], const SkScalar pos[],
753cb93a386Sopenharmony_ci                                                int colorCount, sk_sp<SkColorSpace> colorSpace,
754cb93a386Sopenharmony_ci                                                SkTileMode mode) {
755cb93a386Sopenharmony_ci    switch(mode) {
756cb93a386Sopenharmony_ci        case SkTileMode::kDecal:
757cb93a386Sopenharmony_ci            // normally this would reject the area outside of the interpolation region, so since
758cb93a386Sopenharmony_ci            // inside region is empty when the radii are equal, the entire draw region is empty
759cb93a386Sopenharmony_ci            return SkShaders::Empty();
760cb93a386Sopenharmony_ci        case SkTileMode::kRepeat:
761cb93a386Sopenharmony_ci        case SkTileMode::kMirror:
762cb93a386Sopenharmony_ci            // repeat and mirror are treated the same: the border colors are never visible,
763cb93a386Sopenharmony_ci            // but approximate the final color as infinite repetitions of the colors, so
764cb93a386Sopenharmony_ci            // it can be represented as the average color of the gradient.
765cb93a386Sopenharmony_ci            return SkShaders::Color(
766cb93a386Sopenharmony_ci                    average_gradient_color(colors, pos, colorCount), std::move(colorSpace));
767cb93a386Sopenharmony_ci        case SkTileMode::kClamp:
768cb93a386Sopenharmony_ci            // Depending on how the gradient shape degenerates, there may be a more specialized
769cb93a386Sopenharmony_ci            // fallback representation for the factories to use, but this is a reasonable default.
770cb93a386Sopenharmony_ci            return SkShaders::Color(colors[colorCount - 1], std::move(colorSpace));
771cb93a386Sopenharmony_ci    }
772cb93a386Sopenharmony_ci    SkDEBUGFAIL("Should not be reached");
773cb93a386Sopenharmony_ci    return nullptr;
774cb93a386Sopenharmony_ci}
775cb93a386Sopenharmony_ci
776cb93a386Sopenharmony_ci// assumes colors is SkColor4f* and pos is SkScalar*
777cb93a386Sopenharmony_ci#define EXPAND_1_COLOR(count)                \
778cb93a386Sopenharmony_ci     SkColor4f tmp[2];                       \
779cb93a386Sopenharmony_ci     do {                                    \
780cb93a386Sopenharmony_ci         if (1 == count) {                   \
781cb93a386Sopenharmony_ci             tmp[0] = tmp[1] = colors[0];    \
782cb93a386Sopenharmony_ci             colors = tmp;                   \
783cb93a386Sopenharmony_ci             pos = nullptr;                  \
784cb93a386Sopenharmony_ci             count = 2;                      \
785cb93a386Sopenharmony_ci         }                                   \
786cb93a386Sopenharmony_ci     } while (0)
787cb93a386Sopenharmony_ci
788cb93a386Sopenharmony_cistruct ColorStopOptimizer {
789cb93a386Sopenharmony_ci    ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos, int count, SkTileMode mode)
790cb93a386Sopenharmony_ci        : fColors(colors)
791cb93a386Sopenharmony_ci        , fPos(pos)
792cb93a386Sopenharmony_ci        , fCount(count) {
793cb93a386Sopenharmony_ci
794cb93a386Sopenharmony_ci            if (!pos || count != 3) {
795cb93a386Sopenharmony_ci                return;
796cb93a386Sopenharmony_ci            }
797cb93a386Sopenharmony_ci
798cb93a386Sopenharmony_ci            if (SkScalarNearlyEqual(pos[0], 0.0f) &&
799cb93a386Sopenharmony_ci                SkScalarNearlyEqual(pos[1], 0.0f) &&
800cb93a386Sopenharmony_ci                SkScalarNearlyEqual(pos[2], 1.0f)) {
801cb93a386Sopenharmony_ci
802cb93a386Sopenharmony_ci                if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
803cb93a386Sopenharmony_ci                    colors[0] == colors[1]) {
804cb93a386Sopenharmony_ci
805cb93a386Sopenharmony_ci                    // Ignore the leftmost color/pos.
806cb93a386Sopenharmony_ci                    fColors += 1;
807cb93a386Sopenharmony_ci                    fPos    += 1;
808cb93a386Sopenharmony_ci                    fCount   = 2;
809cb93a386Sopenharmony_ci                }
810cb93a386Sopenharmony_ci            } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
811cb93a386Sopenharmony_ci                       SkScalarNearlyEqual(pos[1], 1.0f) &&
812cb93a386Sopenharmony_ci                       SkScalarNearlyEqual(pos[2], 1.0f)) {
813cb93a386Sopenharmony_ci
814cb93a386Sopenharmony_ci                if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
815cb93a386Sopenharmony_ci                    colors[1] == colors[2]) {
816cb93a386Sopenharmony_ci
817cb93a386Sopenharmony_ci                    // Ignore the rightmost color/pos.
818cb93a386Sopenharmony_ci                    fCount  = 2;
819cb93a386Sopenharmony_ci                }
820cb93a386Sopenharmony_ci            }
821cb93a386Sopenharmony_ci    }
822cb93a386Sopenharmony_ci
823cb93a386Sopenharmony_ci    const SkColor4f* fColors;
824cb93a386Sopenharmony_ci    const SkScalar*  fPos;
825cb93a386Sopenharmony_ci    int              fCount;
826cb93a386Sopenharmony_ci};
827cb93a386Sopenharmony_ci
828cb93a386Sopenharmony_cistruct ColorConverter {
829cb93a386Sopenharmony_ci    ColorConverter(const SkColor* colors, int count) {
830cb93a386Sopenharmony_ci        const float ONE_OVER_255 = 1.f / 255;
831cb93a386Sopenharmony_ci        for (int i = 0; i < count; ++i) {
832cb93a386Sopenharmony_ci            fColors4f.push_back({
833cb93a386Sopenharmony_ci                SkColorGetR(colors[i]) * ONE_OVER_255,
834cb93a386Sopenharmony_ci                SkColorGetG(colors[i]) * ONE_OVER_255,
835cb93a386Sopenharmony_ci                SkColorGetB(colors[i]) * ONE_OVER_255,
836cb93a386Sopenharmony_ci                SkColorGetA(colors[i]) * ONE_OVER_255 });
837cb93a386Sopenharmony_ci        }
838cb93a386Sopenharmony_ci    }
839cb93a386Sopenharmony_ci
840cb93a386Sopenharmony_ci    SkSTArray<2, SkColor4f, true> fColors4f;
841cb93a386Sopenharmony_ci};
842cb93a386Sopenharmony_ci
843cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
844cb93a386Sopenharmony_ci                                             const SkColor colors[],
845cb93a386Sopenharmony_ci                                             const SkScalar pos[], int colorCount,
846cb93a386Sopenharmony_ci                                             SkTileMode mode,
847cb93a386Sopenharmony_ci                                             uint32_t flags,
848cb93a386Sopenharmony_ci                                             const SkMatrix* localMatrix) {
849cb93a386Sopenharmony_ci    ColorConverter converter(colors, colorCount);
850cb93a386Sopenharmony_ci    return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
851cb93a386Sopenharmony_ci                      localMatrix);
852cb93a386Sopenharmony_ci}
853cb93a386Sopenharmony_ci
854cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
855cb93a386Sopenharmony_ci                                             const SkColor4f colors[],
856cb93a386Sopenharmony_ci                                             sk_sp<SkColorSpace> colorSpace,
857cb93a386Sopenharmony_ci                                             const SkScalar pos[], int colorCount,
858cb93a386Sopenharmony_ci                                             SkTileMode mode,
859cb93a386Sopenharmony_ci                                             uint32_t flags,
860cb93a386Sopenharmony_ci                                             const SkMatrix* localMatrix) {
861cb93a386Sopenharmony_ci    if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
862cb93a386Sopenharmony_ci        return nullptr;
863cb93a386Sopenharmony_ci    }
864cb93a386Sopenharmony_ci    if (!valid_grad(colors, pos, colorCount, mode)) {
865cb93a386Sopenharmony_ci        return nullptr;
866cb93a386Sopenharmony_ci    }
867cb93a386Sopenharmony_ci    if (1 == colorCount) {
868cb93a386Sopenharmony_ci        return SkShaders::Color(colors[0], std::move(colorSpace));
869cb93a386Sopenharmony_ci    }
870cb93a386Sopenharmony_ci    if (localMatrix && !localMatrix->invert(nullptr)) {
871cb93a386Sopenharmony_ci        return nullptr;
872cb93a386Sopenharmony_ci    }
873cb93a386Sopenharmony_ci
874cb93a386Sopenharmony_ci    if (SkScalarNearlyZero((pts[1] - pts[0]).length(), kDegenerateThreshold)) {
875cb93a386Sopenharmony_ci        // Degenerate gradient, the only tricky complication is when in clamp mode, the limit of
876cb93a386Sopenharmony_ci        // the gradient approaches two half planes of solid color (first and last). However, they
877cb93a386Sopenharmony_ci        // are divided by the line perpendicular to the start and end point, which becomes undefined
878cb93a386Sopenharmony_ci        // once start and end are exactly the same, so just use the end color for a stable solution.
879cb93a386Sopenharmony_ci        return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
880cb93a386Sopenharmony_ci    }
881cb93a386Sopenharmony_ci
882cb93a386Sopenharmony_ci    ColorStopOptimizer opt(colors, pos, colorCount, mode);
883cb93a386Sopenharmony_ci
884cb93a386Sopenharmony_ci    SkGradientShaderBase::Descriptor desc;
885cb93a386Sopenharmony_ci    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
886cb93a386Sopenharmony_ci              localMatrix);
887cb93a386Sopenharmony_ci    return sk_make_sp<SkLinearGradient>(pts, desc);
888cb93a386Sopenharmony_ci}
889cb93a386Sopenharmony_ci
890cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
891cb93a386Sopenharmony_ci                                             const SkColor colors[],
892cb93a386Sopenharmony_ci                                             const SkScalar pos[], int colorCount,
893cb93a386Sopenharmony_ci                                             SkTileMode mode,
894cb93a386Sopenharmony_ci                                             uint32_t flags,
895cb93a386Sopenharmony_ci                                             const SkMatrix* localMatrix) {
896cb93a386Sopenharmony_ci    ColorConverter converter(colors, colorCount);
897cb93a386Sopenharmony_ci    return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode,
898cb93a386Sopenharmony_ci                      flags, localMatrix);
899cb93a386Sopenharmony_ci}
900cb93a386Sopenharmony_ci
901cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
902cb93a386Sopenharmony_ci                                             const SkColor4f colors[],
903cb93a386Sopenharmony_ci                                             sk_sp<SkColorSpace> colorSpace,
904cb93a386Sopenharmony_ci                                             const SkScalar pos[], int colorCount,
905cb93a386Sopenharmony_ci                                             SkTileMode mode,
906cb93a386Sopenharmony_ci                                             uint32_t flags,
907cb93a386Sopenharmony_ci                                             const SkMatrix* localMatrix) {
908cb93a386Sopenharmony_ci    if (radius < 0) {
909cb93a386Sopenharmony_ci        return nullptr;
910cb93a386Sopenharmony_ci    }
911cb93a386Sopenharmony_ci    if (!valid_grad(colors, pos, colorCount, mode)) {
912cb93a386Sopenharmony_ci        return nullptr;
913cb93a386Sopenharmony_ci    }
914cb93a386Sopenharmony_ci    if (1 == colorCount) {
915cb93a386Sopenharmony_ci        return SkShaders::Color(colors[0], std::move(colorSpace));
916cb93a386Sopenharmony_ci    }
917cb93a386Sopenharmony_ci    if (localMatrix && !localMatrix->invert(nullptr)) {
918cb93a386Sopenharmony_ci        return nullptr;
919cb93a386Sopenharmony_ci    }
920cb93a386Sopenharmony_ci
921cb93a386Sopenharmony_ci    if (SkScalarNearlyZero(radius, kDegenerateThreshold)) {
922cb93a386Sopenharmony_ci        // Degenerate gradient optimization, and no special logic needed for clamped radial gradient
923cb93a386Sopenharmony_ci        return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
924cb93a386Sopenharmony_ci    }
925cb93a386Sopenharmony_ci
926cb93a386Sopenharmony_ci    ColorStopOptimizer opt(colors, pos, colorCount, mode);
927cb93a386Sopenharmony_ci
928cb93a386Sopenharmony_ci    SkGradientShaderBase::Descriptor desc;
929cb93a386Sopenharmony_ci    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
930cb93a386Sopenharmony_ci              localMatrix);
931cb93a386Sopenharmony_ci    return sk_make_sp<SkRadialGradient>(center, radius, desc);
932cb93a386Sopenharmony_ci}
933cb93a386Sopenharmony_ci
934cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
935cb93a386Sopenharmony_ci                                                      SkScalar startRadius,
936cb93a386Sopenharmony_ci                                                      const SkPoint& end,
937cb93a386Sopenharmony_ci                                                      SkScalar endRadius,
938cb93a386Sopenharmony_ci                                                      const SkColor colors[],
939cb93a386Sopenharmony_ci                                                      const SkScalar pos[],
940cb93a386Sopenharmony_ci                                                      int colorCount,
941cb93a386Sopenharmony_ci                                                      SkTileMode mode,
942cb93a386Sopenharmony_ci                                                      uint32_t flags,
943cb93a386Sopenharmony_ci                                                      const SkMatrix* localMatrix) {
944cb93a386Sopenharmony_ci    ColorConverter converter(colors, colorCount);
945cb93a386Sopenharmony_ci    return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(),
946cb93a386Sopenharmony_ci                               nullptr, pos, colorCount, mode, flags, localMatrix);
947cb93a386Sopenharmony_ci}
948cb93a386Sopenharmony_ci
949cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
950cb93a386Sopenharmony_ci                                                      SkScalar startRadius,
951cb93a386Sopenharmony_ci                                                      const SkPoint& end,
952cb93a386Sopenharmony_ci                                                      SkScalar endRadius,
953cb93a386Sopenharmony_ci                                                      const SkColor4f colors[],
954cb93a386Sopenharmony_ci                                                      sk_sp<SkColorSpace> colorSpace,
955cb93a386Sopenharmony_ci                                                      const SkScalar pos[],
956cb93a386Sopenharmony_ci                                                      int colorCount,
957cb93a386Sopenharmony_ci                                                      SkTileMode mode,
958cb93a386Sopenharmony_ci                                                      uint32_t flags,
959cb93a386Sopenharmony_ci                                                      const SkMatrix* localMatrix) {
960cb93a386Sopenharmony_ci    if (startRadius < 0 || endRadius < 0) {
961cb93a386Sopenharmony_ci        return nullptr;
962cb93a386Sopenharmony_ci    }
963cb93a386Sopenharmony_ci    if (!valid_grad(colors, pos, colorCount, mode)) {
964cb93a386Sopenharmony_ci        return nullptr;
965cb93a386Sopenharmony_ci    }
966cb93a386Sopenharmony_ci    if (SkScalarNearlyZero((start - end).length(), kDegenerateThreshold)) {
967cb93a386Sopenharmony_ci        // If the center positions are the same, then the gradient is the radial variant of a 2 pt
968cb93a386Sopenharmony_ci        // conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate
969cb93a386Sopenharmony_ci        // (startRadius == endRadius).
970cb93a386Sopenharmony_ci        if (SkScalarNearlyEqual(startRadius, endRadius, kDegenerateThreshold)) {
971cb93a386Sopenharmony_ci            // Degenerate case, where the interpolation region area approaches zero. The proper
972cb93a386Sopenharmony_ci            // behavior depends on the tile mode, which is consistent with the default degenerate
973cb93a386Sopenharmony_ci            // gradient behavior, except when mode = clamp and the radii > 0.
974cb93a386Sopenharmony_ci            if (mode == SkTileMode::kClamp && endRadius > kDegenerateThreshold) {
975cb93a386Sopenharmony_ci                // The interpolation region becomes an infinitely thin ring at the radius, so the
976cb93a386Sopenharmony_ci                // final gradient will be the first color repeated from p=0 to 1, and then a hard
977cb93a386Sopenharmony_ci                // stop switching to the last color at p=1.
978cb93a386Sopenharmony_ci                static constexpr SkScalar circlePos[3] = {0, 1, 1};
979cb93a386Sopenharmony_ci                SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
980cb93a386Sopenharmony_ci                return MakeRadial(start, endRadius, reColors, std::move(colorSpace),
981cb93a386Sopenharmony_ci                                  circlePos, 3, mode, flags, localMatrix);
982cb93a386Sopenharmony_ci            } else {
983cb93a386Sopenharmony_ci                // Otherwise use the default degenerate case
984cb93a386Sopenharmony_ci                return make_degenerate_gradient(
985cb93a386Sopenharmony_ci                        colors, pos, colorCount, std::move(colorSpace), mode);
986cb93a386Sopenharmony_ci            }
987cb93a386Sopenharmony_ci        } else if (SkScalarNearlyZero(startRadius, kDegenerateThreshold)) {
988cb93a386Sopenharmony_ci            // We can treat this gradient as radial, which is faster. If we got here, we know
989cb93a386Sopenharmony_ci            // that endRadius is not equal to 0, so this produces a meaningful gradient
990cb93a386Sopenharmony_ci            return MakeRadial(start, endRadius, colors, std::move(colorSpace), pos, colorCount,
991cb93a386Sopenharmony_ci                              mode, flags, localMatrix);
992cb93a386Sopenharmony_ci        }
993cb93a386Sopenharmony_ci        // Else it's the 2pt conical radial variant with no degenerate radii, so fall through to the
994cb93a386Sopenharmony_ci        // regular 2pt constructor.
995cb93a386Sopenharmony_ci    }
996cb93a386Sopenharmony_ci
997cb93a386Sopenharmony_ci    if (localMatrix && !localMatrix->invert(nullptr)) {
998cb93a386Sopenharmony_ci        return nullptr;
999cb93a386Sopenharmony_ci    }
1000cb93a386Sopenharmony_ci    EXPAND_1_COLOR(colorCount);
1001cb93a386Sopenharmony_ci
1002cb93a386Sopenharmony_ci    ColorStopOptimizer opt(colors, pos, colorCount, mode);
1003cb93a386Sopenharmony_ci
1004cb93a386Sopenharmony_ci    SkGradientShaderBase::Descriptor desc;
1005cb93a386Sopenharmony_ci    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
1006cb93a386Sopenharmony_ci              localMatrix);
1007cb93a386Sopenharmony_ci    return SkTwoPointConicalGradient::Create(start, startRadius, end, endRadius, desc);
1008cb93a386Sopenharmony_ci}
1009cb93a386Sopenharmony_ci
1010cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
1011cb93a386Sopenharmony_ci                                            const SkColor colors[],
1012cb93a386Sopenharmony_ci                                            const SkScalar pos[],
1013cb93a386Sopenharmony_ci                                            int colorCount,
1014cb93a386Sopenharmony_ci                                            SkTileMode mode,
1015cb93a386Sopenharmony_ci                                            SkScalar startAngle,
1016cb93a386Sopenharmony_ci                                            SkScalar endAngle,
1017cb93a386Sopenharmony_ci                                            uint32_t flags,
1018cb93a386Sopenharmony_ci                                            const SkMatrix* localMatrix) {
1019cb93a386Sopenharmony_ci    ColorConverter converter(colors, colorCount);
1020cb93a386Sopenharmony_ci    return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount,
1021cb93a386Sopenharmony_ci                     mode, startAngle, endAngle, flags, localMatrix);
1022cb93a386Sopenharmony_ci}
1023cb93a386Sopenharmony_ci
1024cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
1025cb93a386Sopenharmony_ci                                            const SkColor4f colors[],
1026cb93a386Sopenharmony_ci                                            sk_sp<SkColorSpace> colorSpace,
1027cb93a386Sopenharmony_ci                                            const SkScalar pos[],
1028cb93a386Sopenharmony_ci                                            int colorCount,
1029cb93a386Sopenharmony_ci                                            SkTileMode mode,
1030cb93a386Sopenharmony_ci                                            SkScalar startAngle,
1031cb93a386Sopenharmony_ci                                            SkScalar endAngle,
1032cb93a386Sopenharmony_ci                                            uint32_t flags,
1033cb93a386Sopenharmony_ci                                            const SkMatrix* localMatrix) {
1034cb93a386Sopenharmony_ci    if (!valid_grad(colors, pos, colorCount, mode)) {
1035cb93a386Sopenharmony_ci        return nullptr;
1036cb93a386Sopenharmony_ci    }
1037cb93a386Sopenharmony_ci    if (1 == colorCount) {
1038cb93a386Sopenharmony_ci        return SkShaders::Color(colors[0], std::move(colorSpace));
1039cb93a386Sopenharmony_ci    }
1040cb93a386Sopenharmony_ci    if (!SkScalarIsFinite(startAngle) || !SkScalarIsFinite(endAngle) || startAngle > endAngle) {
1041cb93a386Sopenharmony_ci        return nullptr;
1042cb93a386Sopenharmony_ci    }
1043cb93a386Sopenharmony_ci    if (localMatrix && !localMatrix->invert(nullptr)) {
1044cb93a386Sopenharmony_ci        return nullptr;
1045cb93a386Sopenharmony_ci    }
1046cb93a386Sopenharmony_ci
1047cb93a386Sopenharmony_ci    if (SkScalarNearlyEqual(startAngle, endAngle, kDegenerateThreshold)) {
1048cb93a386Sopenharmony_ci        // Degenerate gradient, which should follow default degenerate behavior unless it is
1049cb93a386Sopenharmony_ci        // clamped and the angle is greater than 0.
1050cb93a386Sopenharmony_ci        if (mode == SkTileMode::kClamp && endAngle > kDegenerateThreshold) {
1051cb93a386Sopenharmony_ci            // In this case, the first color is repeated from 0 to the angle, then a hardstop
1052cb93a386Sopenharmony_ci            // switches to the last color (all other colors are compressed to the infinitely thin
1053cb93a386Sopenharmony_ci            // interpolation region).
1054cb93a386Sopenharmony_ci            static constexpr SkScalar clampPos[3] = {0, 1, 1};
1055cb93a386Sopenharmony_ci            SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
1056cb93a386Sopenharmony_ci            return MakeSweep(cx, cy, reColors, std::move(colorSpace), clampPos, 3, mode, 0,
1057cb93a386Sopenharmony_ci                             endAngle, flags, localMatrix);
1058cb93a386Sopenharmony_ci        } else {
1059cb93a386Sopenharmony_ci            return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
1060cb93a386Sopenharmony_ci        }
1061cb93a386Sopenharmony_ci    }
1062cb93a386Sopenharmony_ci
1063cb93a386Sopenharmony_ci    if (startAngle <= 0 && endAngle >= 360) {
1064cb93a386Sopenharmony_ci        // If the t-range includes [0,1], then we can always use clamping (presumably faster).
1065cb93a386Sopenharmony_ci        mode = SkTileMode::kClamp;
1066cb93a386Sopenharmony_ci    }
1067cb93a386Sopenharmony_ci
1068cb93a386Sopenharmony_ci    ColorStopOptimizer opt(colors, pos, colorCount, mode);
1069cb93a386Sopenharmony_ci
1070cb93a386Sopenharmony_ci    SkGradientShaderBase::Descriptor desc;
1071cb93a386Sopenharmony_ci    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
1072cb93a386Sopenharmony_ci              localMatrix);
1073cb93a386Sopenharmony_ci
1074cb93a386Sopenharmony_ci    const SkScalar t0 = startAngle / 360,
1075cb93a386Sopenharmony_ci                   t1 =   endAngle / 360;
1076cb93a386Sopenharmony_ci
1077cb93a386Sopenharmony_ci    return sk_make_sp<SkSweepGradient>(SkPoint::Make(cx, cy), t0, t1, desc);
1078cb93a386Sopenharmony_ci}
1079cb93a386Sopenharmony_ci
1080cb93a386Sopenharmony_civoid SkGradientShader::RegisterFlattenables() {
1081cb93a386Sopenharmony_ci    SK_REGISTER_FLATTENABLE(SkLinearGradient);
1082cb93a386Sopenharmony_ci    SK_REGISTER_FLATTENABLE(SkRadialGradient);
1083cb93a386Sopenharmony_ci    SK_REGISTER_FLATTENABLE(SkSweepGradient);
1084cb93a386Sopenharmony_ci    SK_REGISTER_FLATTENABLE(SkTwoPointConicalGradient);
1085cb93a386Sopenharmony_ci}
1086