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 "SkSweepGradient.h"
9 
translate(SkScalar dx,SkScalar dy)10 static SkMatrix translate(SkScalar dx, SkScalar dy) {
11     SkMatrix matrix;
12     matrix.setTranslate(dx, dy);
13     return matrix;
14 }
15 
SkSweepGradient(SkScalar cx,SkScalar cy,const Descriptor & desc)16 SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor& desc)
17     : SkGradientShaderBase(desc, translate(-cx, -cy))
18     , fCenter(SkPoint::Make(cx, cy))
19 {
20     // overwrite the tilemode to a canonical value (since sweep ignores it)
21     fTileMode = SkShader::kClamp_TileMode;
22 }
23 
asAGradient(GradientInfo * info) const24 SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const {
25     if (info) {
26         commonAsAGradient(info);
27         info->fPoint[0] = fCenter;
28     }
29     return kSweep_GradientType;
30 }
31 
CreateProc(SkReadBuffer & buffer)32 sk_sp<SkFlattenable> SkSweepGradient::CreateProc(SkReadBuffer& buffer) {
33     DescriptorScope desc;
34     if (!desc.unflatten(buffer)) {
35         return nullptr;
36     }
37     const SkPoint center = buffer.readPoint();
38     return SkGradientShader::MakeSweep(center.x(), center.y(), desc.fColors,
39                                        std::move(desc.fColorSpace), desc.fPos, desc.fCount,
40                                        desc.fGradFlags, desc.fLocalMatrix);
41 }
42 
flatten(SkWriteBuffer & buffer) const43 void SkSweepGradient::flatten(SkWriteBuffer& buffer) const {
44     this->INHERITED::flatten(buffer);
45     buffer.writePoint(fCenter);
46 }
47 
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const48 SkShader::Context* SkSweepGradient::onMakeContext(
49     const ContextRec& rec, SkArenaAlloc* alloc) const
50 {
51     return CheckedMakeContext<SweepGradientContext>(alloc, *this, rec);
52 }
53 
SweepGradientContext(const SkSweepGradient & shader,const ContextRec & rec)54 SkSweepGradient::SweepGradientContext::SweepGradientContext(
55         const SkSweepGradient& shader, const ContextRec& rec)
56     : INHERITED(shader, rec) {}
57 
58 //  returns angle in a circle [0..2PI) -> [0..255]
SkATan2_255(float y,float x)59 static unsigned SkATan2_255(float y, float x) {
60     //    static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
61     static const float g255Over2PI = 40.584510488433314f;
62 
63     float result = sk_float_atan2(y, x);
64     if (!SkScalarIsFinite(result)) {
65         return 0;
66     }
67     if (result < 0) {
68         result += 2 * SK_ScalarPI;
69     }
70     SkASSERT(result >= 0);
71     // since our value is always >= 0, we can cast to int, which is faster than
72     // calling floorf()
73     int ir = (int)(result * g255Over2PI);
74     SkASSERT(ir >= 0 && ir <= 255);
75     return ir;
76 }
77 
shadeSpan(int x,int y,SkPMColor * SK_RESTRICT dstC,int count)78 void SkSweepGradient::SweepGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
79                                                       int count) {
80     SkMatrix::MapXYProc proc = fDstToIndexProc;
81     const SkMatrix&     matrix = fDstToIndex;
82     const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
83     int                 toggle = init_dither_toggle(x, y);
84     SkPoint             srcPt;
85 
86     if (fDstToIndexClass != kPerspective_MatrixClass) {
87         proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
88                      SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
89         SkScalar dx, fx = srcPt.fX;
90         SkScalar dy, fy = srcPt.fY;
91 
92         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
93             const auto step = matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf);
94             dx = step.fX;
95             dy = step.fY;
96         } else {
97             SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
98             dx = matrix.getScaleX();
99             dy = matrix.getSkewY();
100         }
101 
102         for (; count > 0; --count) {
103             *dstC++ = cache[toggle + SkATan2_255(fy, fx)];
104             fx += dx;
105             fy += dy;
106             toggle = next_dither_toggle(toggle);
107         }
108     } else {  // perspective case
109         for (int stop = x + count; x < stop; x++) {
110             proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
111                          SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
112             *dstC++ = cache[toggle + SkATan2_255(srcPt.fY, srcPt.fX)];
113             toggle = next_dither_toggle(toggle);
114         }
115     }
116 }
117 
118 /////////////////////////////////////////////////////////////////////
119 
120 #if SK_SUPPORT_GPU
121 
122 #include "SkGr.h"
123 #include "GrShaderCaps.h"
124 #include "gl/GrGLContext.h"
125 #include "glsl/GrGLSLFragmentShaderBuilder.h"
126 
127 class GrSweepGradient : public GrGradientEffect {
128 public:
129     class GLSLSweepProcessor;
130 
Make(const CreateArgs & args)131     static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
132         return sk_sp<GrFragmentProcessor>(new GrSweepGradient(args));
133     }
~GrSweepGradient()134     ~GrSweepGradient() override {}
135 
name() const136     const char* name() const override { return "Sweep Gradient"; }
137 
138 private:
GrSweepGradient(const CreateArgs & args)139     GrSweepGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
140         this->initClassID<GrSweepGradient>();
141     }
142 
143     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
144 
145     virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
146                                        GrProcessorKeyBuilder* b) const override;
147 
148     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
149 
150     typedef GrGradientEffect INHERITED;
151 };
152 
153 /////////////////////////////////////////////////////////////////////
154 
155 class GrSweepGradient::GLSLSweepProcessor : public GrGradientEffect::GLSLProcessor {
156 public:
GLSLSweepProcessor(const GrProcessor &)157     GLSLSweepProcessor(const GrProcessor&) {}
~GLSLSweepProcessor()158     ~GLSLSweepProcessor() override {}
159 
160     virtual void emitCode(EmitArgs&) override;
161 
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)162     static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
163         b->add32(GenBaseGradientKey(processor));
164     }
165 
166 private:
167     typedef GrGradientEffect::GLSLProcessor INHERITED;
168 
169 };
170 
171 /////////////////////////////////////////////////////////////////////
172 
onCreateGLSLInstance() const173 GrGLSLFragmentProcessor* GrSweepGradient::onCreateGLSLInstance() const {
174     return new GrSweepGradient::GLSLSweepProcessor(*this);
175 }
176 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const177 void GrSweepGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
178                                             GrProcessorKeyBuilder* b) const {
179     GrSweepGradient::GLSLSweepProcessor::GenKey(*this, caps, b);
180 }
181 
182 
183 /////////////////////////////////////////////////////////////////////
184 
185 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSweepGradient);
186 
187 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)188 sk_sp<GrFragmentProcessor> GrSweepGradient::TestCreate(GrProcessorTestData* d) {
189     SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
190 
191     RandomGradientParams params(d->fRandom);
192     auto shader = params.fUseColors4f ?
193         SkGradientShader::MakeSweep(center.fX, center.fY, params.fColors4f, params.fColorSpace,
194                                     params.fStops, params.fColorCount) :
195         SkGradientShader::MakeSweep(center.fX, center.fY,  params.fColors,
196                                     params.fStops, params.fColorCount);
197     GrTest::TestAsFPArgs asFPArgs(d);
198     sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
199     GrAlwaysAssert(fp);
200     return fp;
201 }
202 #endif
203 
204 /////////////////////////////////////////////////////////////////////
205 
emitCode(EmitArgs & args)206 void GrSweepGradient::GLSLSweepProcessor::emitCode(EmitArgs& args) {
207     const GrSweepGradient& ge = args.fFp.cast<GrSweepGradient>();
208     this->emitUniforms(args.fUniformHandler, ge);
209     SkString coords2D = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
210     SkString t;
211     // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
212     if (args.fShaderCaps->atan2ImplementedAsAtanYOverX()) {
213         // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
214         // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in
215         // (sqrt(x^2 + y^2) + x) as the second parameter to atan2 in these cases. We let the device
216         // handle the undefined behavior of the second paramenter being 0 instead of doing the
217         // divide ourselves and using atan instead.
218         t.printf("(2.0 * atan(- %s.y, length(%s) - %s.x) * 0.1591549430918 + 0.5)",
219                  coords2D.c_str(), coords2D.c_str(), coords2D.c_str());
220     } else {
221         t.printf("(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5)",
222                  coords2D.c_str(), coords2D.c_str());
223     }
224     this->emitColor(args.fFragBuilder,
225                     args.fUniformHandler,
226                     args.fShaderCaps,
227                     ge, t.c_str(),
228                     args.fOutputColor,
229                     args.fInputColor,
230                     args.fTexSamplers);
231 }
232 
233 /////////////////////////////////////////////////////////////////////
234 
asFragmentProcessor(const AsFPArgs & args) const235 sk_sp<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(const AsFPArgs& args) const {
236 
237     SkMatrix matrix;
238     if (!this->getLocalMatrix().invert(&matrix)) {
239         return nullptr;
240     }
241     if (args.fLocalMatrix) {
242         SkMatrix inv;
243         if (!args.fLocalMatrix->invert(&inv)) {
244             return nullptr;
245         }
246         matrix.postConcat(inv);
247     }
248     matrix.postConcat(fPtsToUnit);
249 
250     sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
251                                                                        args.fDstColorSpace);
252     sk_sp<GrFragmentProcessor> inner(GrSweepGradient::Make(
253         GrGradientEffect::CreateArgs(args.fContext, this, &matrix, SkShader::kClamp_TileMode,
254                                      std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
255     return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
256 }
257 
258 #endif
259 
260 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const261 void SkSweepGradient::toString(SkString* str) const {
262     str->append("SkSweepGradient: (");
263 
264     str->append("center: (");
265     str->appendScalar(fCenter.fX);
266     str->append(", ");
267     str->appendScalar(fCenter.fY);
268     str->append(") ");
269 
270     this->INHERITED::toString(str);
271 
272     str->append(")");
273 }
274 #endif
275