1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2006 The Android Open Source Project
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include <algorithm>
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "Sk4fLinearGradient.h"
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkColorSpace_XYZ.h"
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkColorSpaceXformer.h"
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkFloatBits.h"
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkGradientBitmapCache.h"
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkGradientShaderPriv.h"
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkHalf.h"
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkLinearGradient.h"
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkMallocPixelRef.h"
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkRadialGradient.h"
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkReadBuffer.h"
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkSweepGradient.h"
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTwoPointConicalGradient.h"
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkWriteBuffer.h"
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "../../jumper/SkJumper.h"
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotenum GradientSerializationFlags {
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Bits 29:31 used for various boolean flags
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    kHasPosition_GSF    = 0x80000000,
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    kHasLocalMatrix_GSF = 0x40000000,
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    kHasColorSpace_GSF  = 0x20000000,
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Bits 12:28 unused
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Bits 8:11 for fTileMode
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    kTileModeShift_GSF  = 8,
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    kTileModeMask_GSF   = 0xF,
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80)
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    kGradFlagsShift_GSF = 0,
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    kGradFlagsMask_GSF  = 0xFF,
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t flags = 0;
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fPos) {
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        flags |= kHasPosition_GSF;
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fLocalMatrix) {
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        flags |= kHasLocalMatrix_GSF;
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (colorSpaceData) {
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        flags |= kHasColorSpace_GSF;
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    flags |= (fTileMode << kTileModeShift_GSF);
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(fGradFlags <= kGradFlagsMask_GSF);
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    flags |= (fGradFlags << kGradFlagsShift_GSF);
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    buffer.writeUInt(flags);
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    buffer.writeColor4fArray(fColors, fCount);
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (colorSpaceData) {
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        buffer.writeDataAsByteArray(colorSpaceData.get());
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fPos) {
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        buffer.writeScalarArray(fPos, fCount);
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fLocalMatrix) {
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        buffer.writeMatrix(*fLocalMatrix);
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // New gradient format. Includes floating point color, color space, densely packed flags
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t flags = buffer.readUInt();
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fTileMode = (SkShader::TileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF;
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fCount = buffer.getArrayCount();
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fCount > kStorageCount) {
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount;
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fDynamicStorage.reset(allocSize);
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fColors = (SkColor4f*)fDynamicStorage.get();
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fPos = (SkScalar*)(fColors + fCount);
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fColors = fColorStorage;
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fPos = fPosStorage;
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!buffer.readColor4fArray(mutableColors(), fCount)) {
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SkToBool(flags & kHasColorSpace_GSF)) {
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sk_sp<SkData> data = buffer.readByteArrayAsData();
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fColorSpace = SkColorSpace::Deserialize(data->data(), data->size());
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fColorSpace = nullptr;
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SkToBool(flags & kHasPosition_GSF)) {
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!buffer.readScalarArray(mutablePos(), fCount)) {
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return false;
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fPos = nullptr;
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SkToBool(flags & kHasLocalMatrix_GSF)) {
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fLocalMatrix = &fLocalMatrixStorage;
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        buffer.readMatrix(&fLocalMatrixStorage);
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fLocalMatrix = nullptr;
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return buffer.isValid();
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot////////////////////////////////////////////////////////////////////////////////////////////
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    : INHERITED(desc.fLocalMatrix)
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fPtsToUnit(ptsToUnit)
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fColorsAreOpaque(true)
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot{
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fPtsToUnit.getType();  // Precache so reads are threadsafe.
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(desc.fCount > 1);
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fGradFlags = static_cast<uint8_t>(desc.fGradFlags);
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT((unsigned)desc.fTileMode < SkShader::kTileModeCount);
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fTileMode = desc.fTileMode;
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    /*  Note: we let the caller skip the first and/or last position.
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        i.e. pos[0] = 0.3, pos[1] = 0.7
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        In these cases, we insert dummy entries to ensure that the final data
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        will be bracketed by [0, 1].
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        Thus colorCount (the caller's value, and fColorCount (our value) may
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        differ by up to 2. In the above example:
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            colorCount = 2
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fColorCount = 4
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     */
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fColorCount = desc.fCount;
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // check if we need to add in dummy start and/or end position/colors
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool dummyFirst = false;
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool dummyLast = false;
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (desc.fPos) {
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dummyFirst = desc.fPos[0] != 0;
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dummyLast = desc.fPos[desc.fCount - 1] != SK_Scalar1;
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fColorCount += dummyFirst + dummyLast;
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    size_t storageSize = fColorCount * (sizeof(SkColor4f) + (desc.fPos ? sizeof(SkScalar) : 0));
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fOrigColors4f      = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fOrigPos           = desc.fPos ? reinterpret_cast<SkScalar*>(fOrigColors4f + fColorCount)
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   : nullptr;
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Now copy over the colors, adding the dummies as needed
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkColor4f* origColors = fOrigColors4f;
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (dummyFirst) {
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        *origColors++ = desc.fColors[0];
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < desc.fCount; ++i) {
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        origColors[i] = desc.fColors[i];
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1);
165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (dummyLast) {
167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        origColors += desc.fCount;
168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        *origColors = desc.fColors[desc.fCount - 1];
169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!desc.fColorSpace) {
172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // This happens if we were constructed from SkColors, so our colors are really sRGB
173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fColorSpace = SkColorSpace::MakeSRGBLinear();
174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // The color space refers to the float colors, so it must be linear gamma
176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // TODO: GPU code no longer requires this (see GrGradientEffect). Remove this restriction?
177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(desc.fColorSpace->gammaIsLinear());
178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fColorSpace = desc.fColorSpace;
179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (desc.fPos) {
182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar prev = 0;
183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar* origPosPtr = fOrigPos;
184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        *origPosPtr++ = prev; // force the first pos to 0
185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int startIndex = dummyFirst ? 0 : 1;
187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int count = desc.fCount + dummyLast;
188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        bool uniformStops = true;
190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkScalar uniformStep = desc.fPos[startIndex] - prev;
191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int i = startIndex; i < count; i++) {
192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Pin the last value to 1.0, and make sure pos is monotonic.
193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            auto curr = (i == desc.fCount) ? 1 : SkScalarPin(desc.fPos[i], prev, 1);
194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev);
195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            *origPosPtr++ = prev = curr;
197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // If the stops are uniform, treat them as implicit.
200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (uniformStops) {
201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fOrigPos = nullptr;
202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkGradientShaderBase::~SkGradientShaderBase() {}
207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    Descriptor desc;
210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc.fColors = fOrigColors4f;
211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc.fColorSpace = fColorSpace;
212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc.fPos = fOrigPos;
213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc.fCount = fColorCount;
214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc.fTileMode = fTileMode;
215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc.fGradFlags = fGradFlags;
216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkMatrix& m = this->getLocalMatrix();
218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc.fLocalMatrix = m.isIdentity() ? nullptr : &m;
219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc.flatten(buffer);
220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void add_stop_color(SkJumper_GradientCtx* ctx, size_t stop, SkPM4f Fs, SkPM4f Bs) {
223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (ctx->fs[0])[stop] = Fs.r();
224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (ctx->fs[1])[stop] = Fs.g();
225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (ctx->fs[2])[stop] = Fs.b();
226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (ctx->fs[3])[stop] = Fs.a();
227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (ctx->bs[0])[stop] = Bs.r();
228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (ctx->bs[1])[stop] = Bs.g();
229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (ctx->bs[2])[stop] = Bs.b();
230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (ctx->bs[3])[stop] = Bs.a();
231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void add_const_color(SkJumper_GradientCtx* ctx, size_t stop, SkPM4f color) {
234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    add_stop_color(ctx, stop, SkPM4f::FromPremulRGBA(0,0,0,0), color);
235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Calculate a factor F and a bias B so that color = F*t + B when t is in range of
238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// the stop. Assume that the distance between stops is 1/gapCount.
239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void init_stop_evenly(
240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkJumper_GradientCtx* ctx, float gapCount, size_t stop, SkPM4f c_l, SkPM4f c_r) {
241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPM4f Fs = {{
243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (c_r.r() - c_l.r()) * gapCount,
244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (c_r.g() - c_l.g()) * gapCount,
245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (c_r.b() - c_l.b()) * gapCount,
246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (c_r.a() - c_l.a()) * gapCount,
247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }};
248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPM4f Bs = {{
249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        c_l.r() - Fs.r()*(stop/gapCount),
250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        c_l.g() - Fs.g()*(stop/gapCount),
251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        c_l.b() - Fs.b()*(stop/gapCount),
252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        c_l.a() - Fs.a()*(stop/gapCount),
253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }};
254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    add_stop_color(ctx, stop, Fs, Bs);
255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// For each stop we calculate a bias B and a scale factor F, such that
258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void init_stop_pos(
260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkJumper_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPM4f c_l, SkPM4f c_r) {
261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // See note about Clankium's old compiler in init_stop_evenly().
262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPM4f Fs = {{
263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (c_r.r() - c_l.r()) / (t_r - t_l),
264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (c_r.g() - c_l.g()) / (t_r - t_l),
265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (c_r.b() - c_l.b()) / (t_r - t_l),
266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (c_r.a() - c_l.a()) / (t_r - t_l),
267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }};
268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPM4f Bs = {{
269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        c_l.r() - Fs.r()*t_l,
270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        c_l.g() - Fs.g()*t_l,
271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        c_l.b() - Fs.b()*t_l,
272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        c_l.a() - Fs.a()*t_l,
273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }};
274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ctx->ts[stop] = t_l;
275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    add_stop_color(ctx, stop, Fs, Bs);
276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkGradientShaderBase::onAppendStages(const StageRec& rec) const {
279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRasterPipeline* p = rec.fPipeline;
280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkArenaAlloc* alloc = rec.fAlloc;
281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkColorSpace* dstCS = rec.fDstCS;
282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix matrix;
284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &matrix)) {
285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    matrix.postConcat(fPtsToUnit);
288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRasterPipeline_<256> postPipeline;
290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    p->append_seed_shader();
292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    p->append_matrix(alloc, matrix);
293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->appendGradientStages(alloc, p, &postPipeline);
294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch(fTileMode) {
296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x_1); break;
297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x_1); break;
298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case kClamp_TileMode:
299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (!fOrigPos) {
300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // We clamp only when the stops are evenly spaced.
301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1.
302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // In that case, we must make sure we're using the general "gradient" stage,
303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // which is the only stage that will correctly handle unclamped t.
304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                p->append(SkRasterPipeline::clamp_x_1);
305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto prepareColor = [premulGrad, dstCS, this](int i) {
310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkColor4f c = this->getXformedColor(i, dstCS);
311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return premulGrad ? c.premul()
312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          : SkPM4f::From4f(Sk4f::Load(&c));
313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The two-stop case with stops at 0 and 1.
316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fColorCount == 2 && fOrigPos == nullptr) {
317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkPM4f c_l = prepareColor(0),
318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                     c_r = prepareColor(1);
319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // See F and B below.
321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto* f_and_b = alloc->makeArrayDefault<SkPM4f>(2);
322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        f_and_b[0] = SkPM4f::From4f(c_r.to4f() - c_l.to4f());
323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        f_and_b[1] = c_l;
324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, f_and_b);
326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto* ctx = alloc->make<SkJumper_GradientCtx>();
328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Note: In order to handle clamps in search, the search assumes a stop conceptully placed
330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // at -inf. Therefore, the max number of stops is fColorCount+1.
331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int i = 0; i < 4; i++) {
332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Allocate at least at for the AVX2 gather from a YMM register.
333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ctx->fs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ctx->bs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fOrigPos == nullptr) {
338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Handle evenly distributed stops.
339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            size_t stopCount = fColorCount;
341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            float gapCount = stopCount - 1;
342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPM4f c_l = prepareColor(0);
344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (size_t i = 0; i < stopCount - 1; i++) {
345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPM4f c_r = prepareColor(i + 1);
346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                init_stop_evenly(ctx, gapCount, i, c_l, c_r);
347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                c_l = c_r;
348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            add_const_color(ctx, stopCount - 1, c_l);
350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ctx->stopCount = stopCount;
352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            p->append(SkRasterPipeline::evenly_spaced_gradient, ctx);
353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Handle arbitrary stops.
355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ctx->ts = alloc->makeArray<float>(fColorCount+1);
357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase
359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // because they are naturally handled by the search method.
360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            int firstStop;
361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            int lastStop;
362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (fColorCount > 2) {
363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           ? fColorCount - 1 : fColorCount - 2;
366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            } else {
367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                firstStop = 0;
368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                lastStop = 1;
369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            size_t stopCount = 0;
372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            float  t_l = fOrigPos[firstStop];
373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPM4f c_l = prepareColor(firstStop);
374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            add_const_color(ctx, stopCount++, c_l);
375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // N.B. lastStop is the index of the last stop, not one after.
376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (int i = firstStop; i < lastStop; i++) {
377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                float  t_r = fOrigPos[i + 1];
378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPM4f c_r = prepareColor(i + 1);
379fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkASSERT(t_l <= t_r);
380fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (t_l < t_r) {
381fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r);
382fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    stopCount += 1;
383fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
384fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                t_l = t_r;
385fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                c_l = c_r;
386fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
387fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
388fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ctx->ts[stopCount] = t_l;
389fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            add_const_color(ctx, stopCount++, c_l);
390fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
391fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ctx->stopCount = stopCount;
392fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            p->append(SkRasterPipeline::gradient, ctx);
393fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
394fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
395fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
396fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!premulGrad && !this->colorsAreOpaque()) {
397fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        p->append(SkRasterPipeline::premul);
398fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
399fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
400fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    p->extend(postPipeline);
401fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
402fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
403fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
404fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
405fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
406fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkGradientShaderBase::isOpaque() const {
407fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fColorsAreOpaque;
408fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
409fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
410fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic unsigned rounded_divide(unsigned numer, unsigned denom) {
411fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return (numer + (denom >> 1)) / denom;
412fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
413fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
414fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
415fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // we just compute an average color.
416fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // possibly we could weight this based on the proportional width for each color
417fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //   assuming they are not evenly distributed in the fPos array.
418fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int r = 0;
419fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int g = 0;
420fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int b = 0;
421fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const int n = fColorCount;
422fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO: use linear colors?
423fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < n; ++i) {
424fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkColor c = this->getLegacyColor(i);
425fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        r += SkColorGetR(c);
426fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        g += SkColorGetG(c);
427fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        b += SkColorGetB(c);
428fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
429fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n));
430fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
431fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
432fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
433fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkGradientShaderBase::AutoXformColors::AutoXformColors(const SkGradientShaderBase& grad,
434fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                       SkColorSpaceXformer* xformer)
435fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    : fColors(grad.fColorCount) {
436fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO: stay in 4f to preserve precision?
437fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
438fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoSTMalloc<8, SkColor> origColors(grad.fColorCount);
439fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < grad.fColorCount; ++i) {
440fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        origColors[i] = grad.getLegacyColor(i);
441fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
442fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
443fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    xformer->apply(fColors.get(), origColors.get(), grad.fColorCount);
444fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
445fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
446fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic constexpr int kGradientTextureSize = 256;
447fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
448fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap, GradientBitmapType bitmapType) const {
449fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const bool interpInPremul = SkToBool(fGradFlags &
450fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                         SkGradientShader::kInterpolateColorsInPremul_Flag);
451fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkHalf* pixelsF16 = reinterpret_cast<SkHalf*>(bitmap->getPixels());
452fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t* pixels32 = reinterpret_cast<uint32_t*>(bitmap->getPixels());
453fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
454fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    typedef std::function<void(const Sk4f&, int)> pixelWriteFn_t;
455fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
456fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pixelWriteFn_t writeF16Pixel = [&](const Sk4f& x, int index) {
457fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        Sk4h c = SkFloatToHalf_finite_ftz(x);
458fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pixelsF16[4*index+0] = c[0];
459fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pixelsF16[4*index+1] = c[1];
460fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pixelsF16[4*index+2] = c[2];
461fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pixelsF16[4*index+3] = c[3];
462fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
463fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pixelWriteFn_t writeS32Pixel = [&](const Sk4f& c, int index) {
464fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pixels32[index] = Sk4f_toS32(c);
465fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
466fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pixelWriteFn_t writeL32Pixel = [&](const Sk4f& c, int index) {
467fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pixels32[index] = Sk4f_toL32(c);
468fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
469fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
470fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pixelWriteFn_t writeSizedPixel =
471fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (bitmapType == GradientBitmapType::kHalfFloat) ? writeF16Pixel :
472fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (bitmapType == GradientBitmapType::kSRGB     ) ? writeS32Pixel : writeL32Pixel;
473fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pixelWriteFn_t writeUnpremulPixel = [&](const Sk4f& c, int index) {
474fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        writeSizedPixel(c * Sk4f(c[3], c[3], c[3], 1.0f), index);
475fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
476fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
477fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    pixelWriteFn_t writePixel = interpInPremul ? writeSizedPixel : writeUnpremulPixel;
478fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
479fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // When not in legacy mode, we just want the original 4f colors - so we pass in
480fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // our own CS for identity/no transform.
481fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto* cs = bitmapType != GradientBitmapType::kLegacy ? fColorSpace.get() : nullptr;
482fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
483fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int prevIndex = 0;
484fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 1; i < fColorCount; i++) {
485fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Historically, stops have been mapped to [0, 256], with 256 then nudged to the
486fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // next smaller value, then truncate for the texture index. This seems to produce
487fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // the best results for some common distributions, so we preserve the behavior.
488fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int nextIndex = SkTMin(this->getPos(i) * kGradientTextureSize,
489fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                               SkIntToScalar(kGradientTextureSize - 1));
490fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
491fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (nextIndex > prevIndex) {
492fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkColor4f color0 = this->getXformedColor(i - 1, cs),
493fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                      color1 = this->getXformedColor(i    , cs);
494fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            Sk4f          c0 = Sk4f::Load(color0.vec()),
495fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          c1 = Sk4f::Load(color1.vec());
496fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
497fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (interpInPremul) {
498fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                c0 = c0 * Sk4f(c0[3], c0[3], c0[3], 1.0f);
499fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                c1 = c1 * Sk4f(c1[3], c1[3], c1[3], 1.0f);
500fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
501fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
502fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            Sk4f step = Sk4f(1.0f / static_cast<float>(nextIndex - prevIndex));
503fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            Sk4f delta = (c1 - c0) * step;
504fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
505fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (int curIndex = prevIndex; curIndex <= nextIndex; ++curIndex) {
506fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                writePixel(c0, curIndex);
507fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                c0 += delta;
508fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
509fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
510fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        prevIndex = nextIndex;
511fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
512fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(prevIndex == kGradientTextureSize - 1);
513fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
514fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
515fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkColor4f SkGradientShaderBase::getXformedColor(size_t i, SkColorSpace* dstCS) const {
516fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (dstCS) {
517fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS);
518fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
519fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
520fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Legacy/srgb color.
521fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // We quantize upfront to ensure stable SkColor round-trips.
522fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto rgb255 = sk_linear_to_srgb(Sk4f::Load(fOrigColors4f[i].vec()));
523fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto rgb    = SkNx_cast<float>(rgb255) * (1/255.0f);
524fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return { rgb[0], rgb[1], rgb[2], fOrigColors4f[i].fA };
525fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
526fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
527fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSK_DECLARE_STATIC_MUTEX(gGradientCacheMutex);
528fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
529fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *  Because our caller might rebuild the same (logically the same) gradient
530fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *  over and over, we'd like to return exactly the same "bitmap" if possible,
531fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *  allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
532fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *  To do that, we maintain a private cache of built-bitmaps, based on our
533fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *  colors and positions.
534fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
535fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap,
536fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                  GradientBitmapType bitmapType) const {
537fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // build our key: [numColors + colors[] + {positions[]} + flags + colorType ]
538fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static_assert(sizeof(SkColor4f) % sizeof(int32_t) == 0, "");
539fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const int colorsAsIntCount = fColorCount * sizeof(SkColor4f) / sizeof(int32_t);
540fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int count = 1 + colorsAsIntCount + 1 + 1;
541fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fColorCount > 2) {
542fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        count += fColorCount - 1;
543fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
544fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
545fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoSTMalloc<64, int32_t> storage(count);
546fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int32_t* buffer = storage.get();
547fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
548fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    *buffer++ = fColorCount;
549fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    memcpy(buffer, fOrigColors4f, fColorCount * sizeof(SkColor4f));
550fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    buffer += colorsAsIntCount;
551fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fColorCount > 2) {
552fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int i = 1; i < fColorCount; i++) {
553fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            *buffer++ = SkFloat2Bits(this->getPos(i));
554fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
555fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
556fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    *buffer++ = fGradFlags;
557fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    *buffer++ = static_cast<int32_t>(bitmapType);
558fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(buffer - storage.get() == count);
559fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
560fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ///////////////////////////////////
561fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
562fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static SkGradientBitmapCache* gCache;
563fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // each cache cost 1K or 2K of RAM, since each bitmap will be 1x256 at either 32bpp or 64bpp
564fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
565fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoMutexAcquire ama(gGradientCacheMutex);
566fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
567fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (nullptr == gCache) {
568fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        gCache = new SkGradientBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
569fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
570fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    size_t size = count * sizeof(int32_t);
571fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
572fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!gCache->find(storage.get(), size, bitmap)) {
573fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // For these cases we use the bitmap cache, but not the GradientShaderCache. So just
574fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // allocate and populate the bitmap's data directly.
575fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
576fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkImageInfo info;
577fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (bitmapType) {
578fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GradientBitmapType::kLegacy:
579fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            info = SkImageInfo::Make(kGradientTextureSize, 1, kRGBA_8888_SkColorType,
580fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                     kPremul_SkAlphaType);
581fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
582fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GradientBitmapType::kSRGB:
583fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            info = SkImageInfo::Make(kGradientTextureSize, 1, kRGBA_8888_SkColorType,
584fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                     kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
585fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
586fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GradientBitmapType::kHalfFloat:
587fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            info = SkImageInfo::Make(kGradientTextureSize, 1, kRGBA_F16_SkColorType,
588fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                     kPremul_SkAlphaType, SkColorSpace::MakeSRGBLinear());
589fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
590fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
591fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
592fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        bitmap->allocPixels(info);
593fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->initLinearBitmap(bitmap, bitmapType);
594fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        gCache->add(storage.get(), size, *bitmap);
595fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
596fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
597fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
598fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
599fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (info) {
600fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (info->fColorCount >= fColorCount) {
601fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (info->fColors) {
602fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                for (int i = 0; i < fColorCount; ++i) {
603fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    info->fColors[i] = this->getLegacyColor(i);
604fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
605fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
606fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (info->fColorOffsets) {
607fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                for (int i = 0; i < fColorCount; ++i) {
608fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    info->fColorOffsets[i] = this->getPos(i);
609fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
610fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
611fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
612fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        info->fColorCount = fColorCount;
613fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        info->fTileMode = fTileMode;
614fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        info->fGradientFlags = fGradFlags;
615fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
616fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
617fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
618fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#ifndef SK_IGNORE_TO_STRING
619fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkGradientShaderBase::toString(SkString* str) const {
620fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
621fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    str->appendf("%d colors: ", fColorCount);
622fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
623fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < fColorCount; ++i) {
624fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        str->appendHex(this->getLegacyColor(i), 8);
625fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (i < fColorCount-1) {
626fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            str->append(", ");
627fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
628fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
629fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
630fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fColorCount > 2) {
631fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        str->append(" points: (");
632fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int i = 0; i < fColorCount; ++i) {
633fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            str->appendScalar(this->getPos(i));
634fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (i < fColorCount-1) {
635fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                str->append(", ");
636fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
637fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
638fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        str->append(")");
639fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
640fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
641fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static const char* gTileModeName[SkShader::kTileModeCount] = {
642fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        "clamp", "repeat", "mirror"
643fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
644fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
645fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    str->append(" ");
646fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    str->append(gTileModeName[fTileMode]);
647fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
648fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->INHERITED::toString(str);
649fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
650fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif
651fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
652fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot///////////////////////////////////////////////////////////////////////////////
653fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot///////////////////////////////////////////////////////////////////////////////
654fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
655fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Return true if these parameters are valid/legal/safe to construct a gradient
656fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//
657fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count,
658fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       unsigned tileMode) {
659fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return nullptr != colors && count >= 1 && tileMode < (unsigned)SkShader::kTileModeCount;
660fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
661fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
662fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void desc_init(SkGradientShaderBase::Descriptor* desc,
663fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
664fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                      const SkScalar pos[], int colorCount,
665fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                      SkShader::TileMode mode, uint32_t flags, const SkMatrix* localMatrix) {
666fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(colorCount > 1);
667fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
668fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc->fColors       = colors;
669fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc->fColorSpace   = std::move(colorSpace);
670fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc->fPos          = pos;
671fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc->fCount        = colorCount;
672fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc->fTileMode     = mode;
673fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc->fGradFlags    = flags;
674fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc->fLocalMatrix  = localMatrix;
675fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
676fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
677fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// assumes colors is SkColor4f* and pos is SkScalar*
678fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#define EXPAND_1_COLOR(count)                \
679fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     SkColor4f tmp[2];                       \
680fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     do {                                    \
681fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot         if (1 == count) {                   \
682fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             tmp[0] = tmp[1] = colors[0];    \
683fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             colors = tmp;                   \
684fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             pos = nullptr;                  \
685fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             count = 2;                      \
686fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot         }                                   \
687fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     } while (0)
688fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
689fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstruct ColorStopOptimizer {
690fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos,
691fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       int count, SkShader::TileMode mode)
692fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : fColors(colors)
693fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fPos(pos)
694fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fCount(count) {
695fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
696fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (!pos || count != 3) {
697fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return;
698fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
699fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
700fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (SkScalarNearlyEqual(pos[0], 0.0f) &&
701fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkScalarNearlyEqual(pos[1], 0.0f) &&
702fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkScalarNearlyEqual(pos[2], 1.0f)) {
703fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
704fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (SkShader::kRepeat_TileMode == mode ||
705fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkShader::kMirror_TileMode == mode ||
706fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    colors[0] == colors[1]) {
707fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
708fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // Ignore the leftmost color/pos.
709fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fColors += 1;
710fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fPos    += 1;
711fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fCount   = 2;
712fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
713fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
714fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       SkScalarNearlyEqual(pos[1], 1.0f) &&
715fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       SkScalarNearlyEqual(pos[2], 1.0f)) {
716fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
717fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (SkShader::kRepeat_TileMode == mode ||
718fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkShader::kMirror_TileMode == mode ||
719fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    colors[1] == colors[2]) {
720fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
721fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // Ignore the rightmost color/pos.
722fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fCount  = 2;
723fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
724fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
725fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
726fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
727fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkColor4f* fColors;
728fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkScalar*  fPos;
729fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int              fCount;
730fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
731fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
732fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstruct ColorConverter {
733fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ColorConverter(const SkColor* colors, int count) {
734fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int i = 0; i < count; ++i) {
735fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fColors4f.push_back(SkColor4f::FromColor(colors[i]));
736fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
737fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
738fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
739fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkSTArray<2, SkColor4f, true> fColors4f;
740fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
741fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
742fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
743fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkColor colors[],
744fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkScalar pos[], int colorCount,
745fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             SkShader::TileMode mode,
746fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             uint32_t flags,
747fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkMatrix* localMatrix) {
748fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ColorConverter converter(colors, colorCount);
749fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
750fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                      localMatrix);
751fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
752fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
753fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
754fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkColor4f colors[],
755fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             sk_sp<SkColorSpace> colorSpace,
756fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkScalar pos[], int colorCount,
757fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             SkShader::TileMode mode,
758fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             uint32_t flags,
759fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkMatrix* localMatrix) {
760fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
761fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
762fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
763fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!valid_grad(colors, pos, colorCount, mode)) {
764fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
765fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
766fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (1 == colorCount) {
767fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
768fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
769fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (localMatrix && !localMatrix->invert(nullptr)) {
770fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
771fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
772fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
773fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ColorStopOptimizer opt(colors, pos, colorCount, mode);
774fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
775fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkGradientShaderBase::Descriptor desc;
776fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
777fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot              localMatrix);
778fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return sk_make_sp<SkLinearGradient>(pts, desc);
779fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
780fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
781fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
782fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkColor colors[],
783fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkScalar pos[], int colorCount,
784fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             SkShader::TileMode mode,
785fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             uint32_t flags,
786fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkMatrix* localMatrix) {
787fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ColorConverter converter(colors, colorCount);
788fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode,
789fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                      flags, localMatrix);
790fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
791fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
792fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
793fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkColor4f colors[],
794fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             sk_sp<SkColorSpace> colorSpace,
795fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkScalar pos[], int colorCount,
796fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             SkShader::TileMode mode,
797fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             uint32_t flags,
798fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkMatrix* localMatrix) {
799fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (radius <= 0) {
800fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
801fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
802fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!valid_grad(colors, pos, colorCount, mode)) {
803fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
804fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
805fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (1 == colorCount) {
806fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
807fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
808fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (localMatrix && !localMatrix->invert(nullptr)) {
809fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
810fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
811fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
812fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ColorStopOptimizer opt(colors, pos, colorCount, mode);
813fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
814fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkGradientShaderBase::Descriptor desc;
815fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
816fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot              localMatrix);
817fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return sk_make_sp<SkRadialGradient>(center, radius, desc);
818fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
819fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
820fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
821fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      SkScalar startRadius,
822fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      const SkPoint& end,
823fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      SkScalar endRadius,
824fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      const SkColor colors[],
825fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      const SkScalar pos[],
826fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      int colorCount,
827fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      SkShader::TileMode mode,
828fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      uint32_t flags,
829fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      const SkMatrix* localMatrix) {
830fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ColorConverter converter(colors, colorCount);
831fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(),
832fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                               nullptr, pos, colorCount, mode, flags, localMatrix);
833fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
834fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
835fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
836fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      SkScalar startRadius,
837fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      const SkPoint& end,
838fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      SkScalar endRadius,
839fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      const SkColor4f colors[],
840fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      sk_sp<SkColorSpace> colorSpace,
841fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      const SkScalar pos[],
842fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      int colorCount,
843fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      SkShader::TileMode mode,
844fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      uint32_t flags,
845fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                      const SkMatrix* localMatrix) {
846fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (startRadius < 0 || endRadius < 0) {
847fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
848fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
849fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SkScalarNearlyZero((start - end).length()) && SkScalarNearlyZero(startRadius)) {
850fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // We can treat this gradient as radial, which is faster.
851fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return MakeRadial(start, endRadius, colors, std::move(colorSpace), pos, colorCount,
852fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          mode, flags, localMatrix);
853fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
854fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!valid_grad(colors, pos, colorCount, mode)) {
855fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
856fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
857fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (startRadius == endRadius) {
858fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (start == end || startRadius == 0) {
859fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return SkShader::MakeEmptyShader();
860fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
861fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
862fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (localMatrix && !localMatrix->invert(nullptr)) {
863fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
864fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
865fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    EXPAND_1_COLOR(colorCount);
866fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
867fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ColorStopOptimizer opt(colors, pos, colorCount, mode);
868fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
869fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkGradientShaderBase::Descriptor desc;
870fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
871fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot              localMatrix);
872fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkTwoPointConicalGradient::Create(start, startRadius, end, endRadius, desc);
873fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
874fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
875fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
876fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            const SkColor colors[],
877fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            const SkScalar pos[],
878fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            int colorCount,
879fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            SkShader::TileMode mode,
880fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            SkScalar startAngle,
881fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            SkScalar endAngle,
882fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            uint32_t flags,
883fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            const SkMatrix* localMatrix) {
884fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ColorConverter converter(colors, colorCount);
885fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount,
886fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                     mode, startAngle, endAngle, flags, localMatrix);
887fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
888fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
889fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
890fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            const SkColor4f colors[],
891fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            sk_sp<SkColorSpace> colorSpace,
892fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            const SkScalar pos[],
893fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            int colorCount,
894fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            SkShader::TileMode mode,
895fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            SkScalar startAngle,
896fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            SkScalar endAngle,
897fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            uint32_t flags,
898fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            const SkMatrix* localMatrix) {
899fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!valid_grad(colors, pos, colorCount, mode)) {
900fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
901fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
902fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (1 == colorCount) {
903fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
904fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
905fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (startAngle >= endAngle) {
906fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
907fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
908fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (localMatrix && !localMatrix->invert(nullptr)) {
909fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
910fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
911fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
912fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (startAngle <= 0 && endAngle >= 360) {
913fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // If the t-range includes [0,1], then we can always use clamping (presumably faster).
914fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        mode = SkShader::kClamp_TileMode;
915fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
916fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
917fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ColorStopOptimizer opt(colors, pos, colorCount, mode);
918fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
919fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkGradientShaderBase::Descriptor desc;
920fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
921fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot              localMatrix);
922fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
923fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkScalar t0 = startAngle / 360,
924fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                   t1 =   endAngle / 360;
925fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
926fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return sk_make_sp<SkSweepGradient>(SkPoint::Make(cx, cy), t0, t1, desc);
927fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
928fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
929fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
930fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLinearGradient)
931fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRadialGradient)
932fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSweepGradient)
933fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTwoPointConicalGradient)
934fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
935fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
936fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot///////////////////////////////////////////////////////////////////////////////
937fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
938fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#if SK_SUPPORT_GPU
939fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
940fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrColorSpaceXform.h"
941fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrContext.h"
942fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrContextPriv.h"
943fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrShaderCaps.h"
944fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrTextureStripAtlas.h"
945fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "gl/GrGLContext.h"
946fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "glsl/GrGLSLFragmentShaderBuilder.h"
947fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "glsl/GrGLSLProgramDataManager.h"
948fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "glsl/GrGLSLUniformHandler.h"
949fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkGr.h"
950fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
951fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GrGradientEffect::GLSLProcessor::emitUniforms(GrGLSLUniformHandler* uniformHandler,
952fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                   const GrGradientEffect& ge) {
953fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (ge.fStrategy) {
954fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kThreshold:
955fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kThresholdClamp0:
956fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kThresholdClamp1:
957fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fThresholdUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
958fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                       kFloat_GrSLType,
959fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                       kHigh_GrSLPrecision,
960fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                       "Threshold");
961fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // fall through
962fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kSingle:
963fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fIntervalsUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag,
964fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                            kHalf4_GrSLType,
965fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                            "Intervals",
966fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                            ge.fIntervals.count());
967fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
968fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kTexture:
969fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fFSYUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
970fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                 "GradientYCoordFS");
971fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
972fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
973fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
974fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
975fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman,
976fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                const GrFragmentProcessor& processor) {
977fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const GrGradientEffect& e = processor.cast<GrGradientEffect>();
978fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
979fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (e.fStrategy) {
980fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kThreshold:
981fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kThresholdClamp0:
982fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kThresholdClamp1:
983fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            pdman.set1f(fThresholdUni, e.fThreshold);
984fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // fall through
985fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kSingle:
986fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            pdman.set4fv(fIntervalsUni, e.fIntervals.count(),
987fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                         reinterpret_cast<const float*>(e.fIntervals.begin()));
988fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
989fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kTexture:
990fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (e.fYCoord != fCachedYCoord) {
991fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                pdman.set1f(fFSYUni, e.fYCoord);
992fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fCachedYCoord = e.fYCoord;
993fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
994fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
995fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
996fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
997fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
998fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GrGradientEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const {
999fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    b->add32(GLSLProcessor::GenBaseGradientKey(*this));
1000fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1001fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1002fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotuint32_t GrGradientEffect::GLSLProcessor::GenBaseGradientKey(const GrProcessor& processor) {
1003fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const GrGradientEffect& e = processor.cast<GrGradientEffect>();
1004fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1005fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Build a key using the following bit allocation:
1006fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                static constexpr uint32_t kStrategyBits = 3;
1007fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                static constexpr uint32_t kPremulBits   = 1;
1008fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDEBUGCODE(static constexpr uint32_t kWrapModeBits = 2;)
1009fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1010fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t key = static_cast<uint32_t>(e.fStrategy);
1011fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(key < (1 << kStrategyBits));
1012fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1013fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // This is already baked into the table for texture gradients,
1014fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // and only changes behavior for analytical gradients.
1015fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (e.fStrategy != InterpolationStrategy::kTexture &&
1016fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        e.fPremulType == GrGradientEffect::kBeforeInterp_PremulType) {
1017fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        key |= 1 << kStrategyBits;
1018fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(key < (1 << (kStrategyBits + kPremulBits)));
1019fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1020fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1021fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    key |= static_cast<uint32_t>(e.fWrapMode) << (kStrategyBits + kPremulBits);
1022fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(key < (1 << (kStrategyBits + kPremulBits + kWrapModeBits)));
1023fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1024fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return key;
1025fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1026fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1027fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GrGradientEffect::GLSLProcessor::emitAnalyticalColor(GrGLSLFPFragmentBuilder* fragBuilder,
1028fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          GrGLSLUniformHandler* uniformHandler,
1029fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          const GrShaderCaps* shaderCaps,
1030fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          const GrGradientEffect& ge,
1031fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          const char* t,
1032fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          const char* outputColor,
1033fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          const char* inputColor) {
1034fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // First, apply tiling rules.
1035fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (ge.fWrapMode) {
1036fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrSamplerState::WrapMode::kClamp:
1037fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            switch (ge.fStrategy) {
1038fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                case GrGradientEffect::InterpolationStrategy::kThresholdClamp0:
1039fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // allow t > 1, in order to hit the clamp interval (1, inf)
1040fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fragBuilder->codeAppendf("half tiled_t = max(%s, 0.0);", t);
1041fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    break;
1042fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                case GrGradientEffect::InterpolationStrategy::kThresholdClamp1:
1043fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // allow t < 0, in order to hit the clamp interval (-inf, 0)
1044fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fragBuilder->codeAppendf("half tiled_t = min(%s, 1.0);", t);
1045fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    break;
1046fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                default:
1047fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // regular [0, 1] clamping
1048fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fragBuilder->codeAppendf("half tiled_t = clamp(%s, 0.0, 1.0);", t);
1049fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1050fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1051fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrSamplerState::WrapMode::kRepeat:
1052fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fragBuilder->codeAppendf("half tiled_t = fract(%s);", t);
1053fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1054fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrSamplerState::WrapMode::kMirrorRepeat:
1055fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fragBuilder->codeAppendf("half t_1 = %s - 1.0;", t);
1056fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fragBuilder->codeAppendf("half tiled_t = t_1 - 2.0 * floor(t_1 * 0.5) - 1.0;");
1057fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (shaderCaps->mustDoOpBetweenFloorAndAbs()) {
1058fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // At this point the expected value of tiled_t should between -1 and 1, so this
1059fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // clamp has no effect other than to break up the floor and abs calls and make sure
1060fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // the compiler doesn't merge them back together.
1061fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fragBuilder->codeAppendf("tiled_t = clamp(tiled_t, -1.0, 1.0);");
1062fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1063fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fragBuilder->codeAppendf("tiled_t = abs(tiled_t);");
1064fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1065fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1066fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1067fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Calculate the color.
1068fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char* intervals = uniformHandler->getUniformCStr(fIntervalsUni);
1069fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1070fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (ge.fStrategy) {
1071fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kSingle:
1072fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(ge.fIntervals.count() == 2);
1073fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fragBuilder->codeAppendf(
1074fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "half4 color_scale = %s[0],"
1075fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "      color_bias  = %s[1];"
1076fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                , intervals, intervals
1077fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            );
1078fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1079fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kThreshold:
1080fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kThresholdClamp0:
1081fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case GrGradientEffect::InterpolationStrategy::kThresholdClamp1:
1082fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        {
1083fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(ge.fIntervals.count() == 4);
1084fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const char* threshold = uniformHandler->getUniformCStr(fThresholdUni);
1085fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fragBuilder->codeAppendf(
1086fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "half4 color_scale, color_bias;"
1087fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "if (tiled_t < %s) {"
1088fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "    color_scale = %s[0];"
1089fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "    color_bias  = %s[1];"
1090fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "} else {"
1091fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "    color_scale = %s[2];"
1092fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "    color_bias  = %s[3];"
1093fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "}"
1094fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                , threshold, intervals, intervals, intervals, intervals
1095fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            );
1096fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }   break;
1097fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        default:
1098fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(false);
1099fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fragBuilder->codeAppend("half4 colorTemp = tiled_t * color_scale + color_bias;");
1103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // We could skip this step if all colors are known to be opaque. Two considerations:
1105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The gradient SkShader reporting opaque is more restrictive than necessary in the two
1106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // pt case. Make sure the key reflects this optimization (and note that it can use the
1107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // same shader as the kBeforeInterp case).
1108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (ge.fPremulType == GrGradientEffect::kAfterInterp_PremulType) {
1109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
1110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // If the input colors were floats, or there was a color space xform, we may end up out of
1113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // range. The simplest solution is to always clamp our (premul) value here. We only need to
1114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // clamp RGB, but that causes hangs on the Tegra3 Nexus7. Clamping RGBA avoids the problem.
1115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fragBuilder->codeAppend("colorTemp = clamp(colorTemp, 0, colorTemp.a);");
1116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor);
1118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GrGradientEffect::GLSLProcessor::emitColor(GrGLSLFPFragmentBuilder* fragBuilder,
1121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                GrGLSLUniformHandler* uniformHandler,
1122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                const GrShaderCaps* shaderCaps,
1123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                const GrGradientEffect& ge,
1124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                const char* gradientTValue,
1125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                const char* outputColor,
1126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                const char* inputColor,
1127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                const TextureSamplers& texSamplers) {
1128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (ge.fStrategy != InterpolationStrategy::kTexture) {
1129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->emitAnalyticalColor(fragBuilder, uniformHandler, shaderCaps, ge, gradientTValue,
1130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                  outputColor, inputColor);
1131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char* fsyuni = uniformHandler->getUniformCStr(fFSYUni);
1135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fragBuilder->codeAppendf("half2 coord = half2(%s, %s);", gradientTValue, fsyuni);
1137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fragBuilder->codeAppendf("%s = ", outputColor);
1138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord",
1139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                kFloat2_GrSLType);
1140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fragBuilder->codeAppend(";");
1141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/////////////////////////////////////////////////////////////////////
1144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotinline GrFragmentProcessor::OptimizationFlags GrGradientEffect::OptFlags(bool isOpaque) {
1146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return isOpaque
1147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                   ? kPreservesOpaqueInput_OptimizationFlag |
1148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             kCompatibleWithCoverageAsAlpha_OptimizationFlag
1149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                   : kCompatibleWithCoverageAsAlpha_OptimizationFlag;
1150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GrGradientEffect::addInterval(const SkGradientShaderBase& shader, size_t idx0, size_t idx1,
1153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   SkColorSpace* dstCS) {
1154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(idx0 <= idx1);
1155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const auto  c4f0 = shader.getXformedColor(idx0, dstCS),
1156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                c4f1 = shader.getXformedColor(idx1, dstCS);
1157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const auto    c0 = (fPremulType == kBeforeInterp_PremulType)
1158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                     ? c4f0.premul().to4f() :  Sk4f::Load(c4f0.vec()),
1159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                  c1 = (fPremulType == kBeforeInterp_PremulType)
1160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                     ? c4f1.premul().to4f() :  Sk4f::Load(c4f1.vec());
1161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const auto    t0 = shader.getPos(idx0),
1162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                  t1 = shader.getPos(idx1),
1163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                  dt = t1 - t0;
1164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(dt >= 0);
1165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // dt can be 0 for clamp intervals => in this case we want a scale == 0
1166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const auto scale = SkScalarNearlyZero(dt) ? 0 : (c1 - c0) / dt,
1167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                bias = c0 - t0 * scale;
1168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Intervals are stored as (scale, bias) tuples.
1170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!(fIntervals.count() & 1));
1171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fIntervals.emplace_back(scale[0], scale[1], scale[2], scale[3]);
1172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fIntervals.emplace_back( bias[0],  bias[1],  bias[2],  bias[3]);
1173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotGrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool isOpaque)
1176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    : INHERITED(classID, OptFlags(isOpaque))
1177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fWrapMode(args.fWrapMode)
1178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fRow(-1)
1179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fIsOpaque(args.fShader->isOpaque())
1180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fStrategy(InterpolationStrategy::kTexture)
1181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fThreshold(0) {
1182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkGradientShaderBase& shader(*args.fShader);
1184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fPremulType = (args.fShader->getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag)
1186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                ? kBeforeInterp_PremulType : kAfterInterp_PremulType;
1187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // First, determine the interpolation strategy and params.
1189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (shader.fColorCount) {
1190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case 2:
1191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(!shader.fOrigPos);
1192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fStrategy = InterpolationStrategy::kSingle;
1193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->addInterval(shader, 0, 1, args.fDstColorSpace);
1194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case 3:
1196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fThreshold = shader.getPos(1);
1197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (shader.fOrigPos) {
1199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkASSERT(SkScalarNearlyEqual(shader.fOrigPos[0], 0));
1200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkASSERT(SkScalarNearlyEqual(shader.fOrigPos[2], 1));
1201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (SkScalarNearlyEqual(shader.fOrigPos[1], 0)) {
1202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // hard stop on the left edge.
1203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    if (fWrapMode == GrSamplerState::WrapMode::kClamp) {
1204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fStrategy = InterpolationStrategy::kThresholdClamp1;
1205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        // Clamp interval (scale == 0, bias == colors[0]).
1206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        this->addInterval(shader, 0, 0, args.fDstColorSpace);
1207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    } else {
1208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        // We can ignore the hard stop when not clamping.
1209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fStrategy = InterpolationStrategy::kSingle;
1210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
1211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->addInterval(shader, 1, 2, args.fDstColorSpace);
1212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    break;
1213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
1214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (SkScalarNearlyEqual(shader.fOrigPos[1], 1)) {
1216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // hard stop on the right edge.
1217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->addInterval(shader, 0, 1, args.fDstColorSpace);
1218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    if (fWrapMode == GrSamplerState::WrapMode::kClamp) {
1219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fStrategy = InterpolationStrategy::kThresholdClamp0;
1220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        // Clamp interval (scale == 0, bias == colors[2]).
1221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        this->addInterval(shader, 2, 2, args.fDstColorSpace);
1222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    } else {
1223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        // We can ignore the hard stop when not clamping.
1224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fStrategy = InterpolationStrategy::kSingle;
1225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
1226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    break;
1227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
1228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Two arbitrary interpolation intervals.
1231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fStrategy = InterpolationStrategy::kThreshold;
1232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->addInterval(shader, 0, 1, args.fDstColorSpace);
1233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->addInterval(shader, 1, 2, args.fDstColorSpace);
1234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case 4:
1236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (shader.fOrigPos && SkScalarNearlyEqual(shader.fOrigPos[1], shader.fOrigPos[2])) {
1237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkASSERT(SkScalarNearlyEqual(shader.fOrigPos[0], 0));
1238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkASSERT(SkScalarNearlyEqual(shader.fOrigPos[3], 1));
1239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // Single hard stop => two arbitrary interpolation intervals.
1241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fStrategy = InterpolationStrategy::kThreshold;
1242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fThreshold = shader.getPos(1);
1243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                this->addInterval(shader, 0, 1, args.fDstColorSpace);
1244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                this->addInterval(shader, 2, 3, args.fDstColorSpace);
1245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        default:
1248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Now that we've locked down a strategy, adjust any dependent params.
1252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fStrategy != InterpolationStrategy::kTexture) {
1253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Analytical cases.
1254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fCoordTransform.reset(*args.fMatrix);
1255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
1256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkGradientShaderBase::GradientBitmapType bitmapType =
1257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkGradientShaderBase::GradientBitmapType::kLegacy;
1258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (args.fDstColorSpace) {
1259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Try to use F16 if we can
1260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (args.fContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
1261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                bitmapType = SkGradientShaderBase::GradientBitmapType::kHalfFloat;
1262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            } else if (args.fContext->caps()->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) {
1263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                bitmapType = SkGradientShaderBase::GradientBitmapType::kSRGB;
1264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            } else {
1265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // This can happen, but only if someone explicitly creates an unsupported
1266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // (eg sRGB) surface. Just fall back to legacy behavior.
1267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkBitmap bitmap;
1271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        shader.getGradientTableBitmap(&bitmap, bitmapType);
1272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width()));
1273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GrTextureStripAtlas::Desc desc;
1276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        desc.fWidth  = bitmap.width();
1277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        desc.fHeight = 32;
1278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        desc.fRowHeight = bitmap.height();
1279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        desc.fContext = args.fContext;
1280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *args.fContext->caps());
1281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fAtlas = GrTextureStripAtlas::GetAtlas(desc);
1282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fAtlas);
1283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // We always filter the gradient table. Each table is one row of a texture, always
1285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // y-clamp.
1286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GrSamplerState samplerState(args.fWrapMode, GrSamplerState::Filter::kBilerp);
1287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fRow = fAtlas->lockRow(bitmap);
1289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (-1 != fRow) {
1290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight();
1291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // This is 1/2 places where auto-normalization is disabled
1292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fCoordTransform.reset(*args.fMatrix, fAtlas->asTextureProxyRef().get(), false);
1293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fTextureSampler.reset(fAtlas->asTextureProxyRef(), samplerState);
1294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
1295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // In this instance we know the samplerState state is:
1296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            //   clampY, bilerp
1297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // and the proxy is:
1298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            //   exact fit, power of two in both dimensions
1299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Only the x-tileMode is unknown. However, given all the other knowns we know
1300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // that GrMakeCachedBitmapProxy is sufficient (i.e., it won't need to be
1301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // extracted to a subset or mipmapped).
1302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(
1303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                     args.fContext->contextPriv().proxyProvider(),
1304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                     bitmap);
1305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (!proxy) {
1306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkDebugf("Gradient won't draw. Could not create texture.");
1307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return;
1308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // This is 2/2 places where auto-normalization is disabled
1310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fCoordTransform.reset(*args.fMatrix, proxy.get(), false);
1311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fTextureSampler.reset(std::move(proxy), samplerState);
1312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fYCoord = SK_ScalarHalf;
1313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->addTextureSampler(&fTextureSampler);
1316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->addCoordTransform(&fCoordTransform);
1319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotGrGradientEffect::GrGradientEffect(const GrGradientEffect& that)
1322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : INHERITED(that.classID(), OptFlags(that.fIsOpaque))
1323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fIntervals(that.fIntervals)
1324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fWrapMode(that.fWrapMode)
1325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fCoordTransform(that.fCoordTransform)
1326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fTextureSampler(that.fTextureSampler)
1327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fYCoord(that.fYCoord)
1328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fAtlas(that.fAtlas)
1329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fRow(that.fRow)
1330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fIsOpaque(that.fIsOpaque)
1331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fStrategy(that.fStrategy)
1332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fThreshold(that.fThreshold)
1333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fPremulType(that.fPremulType) {
1334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->addCoordTransform(&fCoordTransform);
1335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fStrategy == InterpolationStrategy::kTexture) {
1336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->addTextureSampler(&fTextureSampler);
1337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (this->useAtlas()) {
1339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fAtlas->lockRow(fRow);
1340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotGrGradientEffect::~GrGradientEffect() {
1344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (this->useAtlas()) {
1345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fAtlas->unlockRow(fRow);
1346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const {
1350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const GrGradientEffect& ge = processor.cast<GrGradientEffect>();
1351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fWrapMode != ge.fWrapMode || fStrategy != ge.fStrategy) {
1353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
1354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(this->useAtlas() == ge.useAtlas());
1357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fStrategy == InterpolationStrategy::kTexture) {
1358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fYCoord != ge.fYCoord) {
1359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return false;
1360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
1362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fThreshold != ge.fThreshold ||
1363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fIntervals != ge.fIntervals ||
1364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fPremulType != ge.fPremulType) {
1365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return false;
1366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
1369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#if GR_TEST_UTILS
1372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotGrGradientEffect::RandomGradientParams::RandomGradientParams(SkRandom* random) {
1373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Set color count to min of 2 so that we don't trigger the const color optimization and make
1374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // a non-gradient processor.
1375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fColorCount = random->nextRangeU(2, kMaxRandomGradientColors);
1376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fUseColors4f = random->nextBool();
1377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // if one color, omit stops, otherwise randomly decide whether or not to
1379fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fColorCount == 1 || (fColorCount >= 2 && random->nextBool())) {
1380fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fStops = nullptr;
1381fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
1382fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fStops = fStopStorage;
1383fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1384fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1385fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // if using SkColor4f, attach a random (possibly null) color space (with linear gamma)
1386fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fUseColors4f) {
1387fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fColorSpace = GrTest::TestColorSpace(random);
1388fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fColorSpace) {
1389fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fColorSpace = fColorSpace->makeLinearGamma();
1390fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1391fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1392fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1393fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar stop = 0.f;
1394fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < fColorCount; ++i) {
1395fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fUseColors4f) {
1396fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fColors4f[i].fR = random->nextUScalar1();
1397fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fColors4f[i].fG = random->nextUScalar1();
1398fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fColors4f[i].fB = random->nextUScalar1();
1399fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fColors4f[i].fA = random->nextUScalar1();
1400fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
1401fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fColors[i] = random->nextU();
1402fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1403fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fStops) {
1404fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fStops[i] = stop;
1405fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            stop = i < fColorCount - 1 ? stop + random->nextUScalar1() * (1.f - stop) : 1.f;
1406fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1407fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1408fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fTileMode = static_cast<SkShader::TileMode>(random->nextULessThan(SkShader::kTileModeCount));
1409fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1410fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif
1411fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1412fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif
1413