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