1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkLinearGradient.h"
9
10#include "Sk4fLinearGradient.h"
11#include "SkColorSpaceXformer.h"
12#include "SkReadBuffer.h"
13#include "SkWriteBuffer.h"
14
15static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
16    SkVector    vec = pts[1] - pts[0];
17    SkScalar    mag = vec.length();
18    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
19
20    vec.scale(inv);
21    SkMatrix matrix;
22    matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
23    matrix.postTranslate(-pts[0].fX, -pts[0].fY);
24    matrix.postScale(inv, inv);
25    return matrix;
26}
27
28///////////////////////////////////////////////////////////////////////////////
29
30SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
31    : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
32    , fStart(pts[0])
33    , fEnd(pts[1]) {
34}
35
36sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
37    DescriptorScope desc;
38    if (!desc.unflatten(buffer)) {
39        return nullptr;
40    }
41    SkPoint pts[2];
42    pts[0] = buffer.readPoint();
43    pts[1] = buffer.readPoint();
44    return SkGradientShader::MakeLinear(pts, desc.fColors, std::move(desc.fColorSpace), desc.fPos,
45                                        desc.fCount, desc.fTileMode, desc.fGradFlags,
46                                        desc.fLocalMatrix);
47}
48
49void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
50    this->INHERITED::flatten(buffer);
51    buffer.writePoint(fStart);
52    buffer.writePoint(fEnd);
53}
54
55SkShaderBase::Context* SkLinearGradient::onMakeContext(
56    const ContextRec& rec, SkArenaAlloc* alloc) const
57{
58    return CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec);
59}
60
61SkShaderBase::Context* SkLinearGradient::onMakeBurstPipelineContext(
62    const ContextRec& rec, SkArenaAlloc* alloc) const {
63
64    // Raster pipeline has a 2-stop specialization faster than our burst.
65    return fColorCount > 2 ? CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec)
66                           : nullptr;
67}
68
69void SkLinearGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline*,
70                                            SkRasterPipeline*) const {
71    // No extra stage needed for linear gradients.
72}
73
74sk_sp<SkShader> SkLinearGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
75    const AutoXformColors xformedColors(*this, xformer);
76    SkPoint pts[2] = { fStart, fEnd };
77    return SkGradientShader::MakeLinear(pts, xformedColors.fColors.get(), fOrigPos, fColorCount,
78                                        fTileMode, fGradFlags, &this->getLocalMatrix());
79}
80
81SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
82    if (info) {
83        commonAsAGradient(info);
84        info->fPoint[0] = fStart;
85        info->fPoint[1] = fEnd;
86    }
87    return kLinear_GradientType;
88}
89
90#if SK_SUPPORT_GPU
91
92#include "GrShaderCaps.h"
93#include "glsl/GrGLSLFragmentShaderBuilder.h"
94#include "SkGr.h"
95
96/////////////////////////////////////////////////////////////////////
97
98class GrLinearGradient : public GrGradientEffect {
99public:
100    class GLSLLinearProcessor;
101
102    static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args) {
103        return GrGradientEffect::AdjustFP(std::unique_ptr<GrLinearGradient>(
104                new GrLinearGradient(args)),
105                args);
106    }
107
108    const char* name() const override { return "Linear Gradient"; }
109
110    std::unique_ptr<GrFragmentProcessor> clone() const override {
111        return std::unique_ptr<GrFragmentProcessor>(new GrLinearGradient(*this));
112    }
113
114private:
115    explicit GrLinearGradient(const CreateArgs& args)
116            : INHERITED(kGrLinearGradient_ClassID, args, args.fShader->colorsAreOpaque()) {
117    }
118
119    explicit GrLinearGradient(const GrLinearGradient& that) : INHERITED(that) {}
120
121    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
122
123    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
124
125    typedef GrGradientEffect INHERITED;
126};
127
128/////////////////////////////////////////////////////////////////////
129
130class GrLinearGradient::GLSLLinearProcessor : public GrGradientEffect::GLSLProcessor {
131public:
132    GLSLLinearProcessor(const GrProcessor&) {}
133
134    virtual void emitCode(EmitArgs&) override;
135
136private:
137    typedef GrGradientEffect::GLSLProcessor INHERITED;
138};
139
140/////////////////////////////////////////////////////////////////////
141
142GrGLSLFragmentProcessor* GrLinearGradient::onCreateGLSLInstance() const {
143    return new GrLinearGradient::GLSLLinearProcessor(*this);
144}
145
146/////////////////////////////////////////////////////////////////////
147
148GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient);
149
150#if GR_TEST_UTILS
151std::unique_ptr<GrFragmentProcessor> GrLinearGradient::TestCreate(GrProcessorTestData* d) {
152    SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()},
153                        {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}};
154
155    RandomGradientParams params(d->fRandom);
156    auto shader = params.fUseColors4f ?
157        SkGradientShader::MakeLinear(points, params.fColors4f, params.fColorSpace, params.fStops,
158                                     params.fColorCount, params.fTileMode) :
159        SkGradientShader::MakeLinear(points, params.fColors, params.fStops,
160                                     params.fColorCount, params.fTileMode);
161    GrTest::TestAsFPArgs asFPArgs(d);
162    std::unique_ptr<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
163    GrAlwaysAssert(fp);
164    return fp;
165}
166#endif
167
168/////////////////////////////////////////////////////////////////////
169
170void GrLinearGradient::GLSLLinearProcessor::emitCode(EmitArgs& args) {
171    const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>();
172    this->emitUniforms(args.fUniformHandler, ge);
173    SkString t = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
174    t.append(".x");
175    this->emitColor(args.fFragBuilder,
176                    args.fUniformHandler,
177                    args.fShaderCaps,
178                    ge,
179                    t.c_str(),
180                    args.fOutputColor,
181                    args.fInputColor,
182                    args.fTexSamplers);
183}
184
185/////////////////////////////////////////////////////////////////////
186
187std::unique_ptr<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(
188        const GrFPArgs& args) const {
189    SkASSERT(args.fContext);
190
191    SkMatrix matrix;
192    if (!this->getLocalMatrix().invert(&matrix)) {
193        return nullptr;
194    }
195    if (args.fLocalMatrix) {
196        SkMatrix inv;
197        if (!args.fLocalMatrix->invert(&inv)) {
198            return nullptr;
199        }
200        matrix.postConcat(inv);
201    }
202    matrix.postConcat(fPtsToUnit);
203
204    return GrLinearGradient::Make(GrGradientEffect::CreateArgs(
205            args.fContext, this, &matrix, fTileMode, args.fDstColorSpaceInfo->colorSpace()));
206}
207
208
209#endif
210
211#ifndef SK_IGNORE_TO_STRING
212void SkLinearGradient::toString(SkString* str) const {
213    str->append("SkLinearGradient (");
214
215    str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
216    str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
217
218    this->INHERITED::toString(str);
219
220    str->append(")");
221}
222#endif
223