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 "SkRadialGradient.h"
9 #include "SkNx.h"
10
11 namespace {
12
13 // GCC doesn't like using static functions as template arguments. So force these to be non-static.
mirror_tileproc_nonstatic(SkFixed x)14 inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
15 return mirror_tileproc(x);
16 }
17
repeat_tileproc_nonstatic(SkFixed x)18 inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
19 return repeat_tileproc(x);
20 }
21
rad_to_unit_matrix(const SkPoint & center,SkScalar radius)22 SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
23 SkScalar inv = SkScalarInvert(radius);
24
25 SkMatrix matrix;
26 matrix.setTranslate(-center.fX, -center.fY);
27 matrix.postScale(inv, inv);
28 return matrix;
29 }
30
31
32 } // namespace
33
34 /////////////////////////////////////////////////////////////////////
35
SkRadialGradient(const SkPoint & center,SkScalar radius,const Descriptor & desc)36 SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
37 : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius))
38 , fCenter(center)
39 , fRadius(radius) {
40 }
41
onContextSize(const ContextRec &) const42 size_t SkRadialGradient::onContextSize(const ContextRec&) const {
43 return sizeof(RadialGradientContext);
44 }
45
onCreateContext(const ContextRec & rec,void * storage) const46 SkShader::Context* SkRadialGradient::onCreateContext(const ContextRec& rec, void* storage) const {
47 return CheckedCreateContext<RadialGradientContext>(storage, *this, rec);
48 }
49
RadialGradientContext(const SkRadialGradient & shader,const ContextRec & rec)50 SkRadialGradient::RadialGradientContext::RadialGradientContext(
51 const SkRadialGradient& shader, const ContextRec& rec)
52 : INHERITED(shader, rec) {}
53
asAGradient(GradientInfo * info) const54 SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
55 if (info) {
56 commonAsAGradient(info);
57 info->fPoint[0] = fCenter;
58 info->fRadius[0] = fRadius;
59 }
60 return kRadial_GradientType;
61 }
62
CreateProc(SkReadBuffer & buffer)63 sk_sp<SkFlattenable> SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
64 DescriptorScope desc;
65 if (!desc.unflatten(buffer)) {
66 return nullptr;
67 }
68 const SkPoint center = buffer.readPoint();
69 const SkScalar radius = buffer.readScalar();
70 return SkGradientShader::MakeRadial(center, radius, desc.fColors, std::move(desc.fColorSpace),
71 desc.fPos, desc.fCount, desc.fTileMode, desc.fGradFlags,
72 desc.fLocalMatrix);
73 }
74
flatten(SkWriteBuffer & buffer) const75 void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
76 this->INHERITED::flatten(buffer);
77 buffer.writePoint(fCenter);
78 buffer.writeScalar(fRadius);
79 }
80
81 namespace {
82
radial_completely_pinned(SkScalar fx,SkScalar dx,SkScalar fy,SkScalar dy)83 inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) {
84 // fast, overly-conservative test: checks unit square instead of unit circle
85 bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0);
86 bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0);
87 return xClamped || yClamped;
88 }
89
90 typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
91 SkScalar sfy, SkScalar sdy,
92 SkPMColor* dstC, const SkPMColor* cache,
93 int count, int toggle);
94
fast_sqrt(const Sk4f & R)95 static inline Sk4f fast_sqrt(const Sk4f& R) {
96 return R * R.rsqrt();
97 }
98
sum_squares(const Sk4f & a,const Sk4f & b)99 static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) {
100 return a * a + b * b;
101 }
102
shadeSpan_radial_clamp2(SkScalar sfx,SkScalar sdx,SkScalar sfy,SkScalar sdy,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int count,int toggle)103 void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy,
104 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
105 int count, int toggle) {
106 if (radial_completely_pinned(sfx, sdx, sfy, sdy)) {
107 unsigned fi = SkGradientShaderBase::kCache32Count - 1;
108 sk_memset32_dither(dstC,
109 cache[toggle + fi],
110 cache[next_dither_toggle(toggle) + fi],
111 count);
112 } else {
113 const Sk4f min(SK_ScalarNearlyZero);
114 const Sk4f max(255);
115 const float scale = 255;
116 sfx *= scale;
117 sfy *= scale;
118 sdx *= scale;
119 sdy *= scale;
120 const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx);
121 const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy);
122 const Sk4f dx4(sdx * 4);
123 const Sk4f dy4(sdy * 4);
124
125 Sk4f tmpxy = fx4 * dx4 + fy4 * dy4;
126 Sk4f tmpdxdy = sum_squares(dx4, dy4);
127 Sk4f R = Sk4f::Max(sum_squares(fx4, fy4), min);
128 Sk4f dR = tmpxy + tmpxy + tmpdxdy;
129 const Sk4f ddR = tmpdxdy + tmpdxdy;
130
131 for (int i = 0; i < (count >> 2); ++i) {
132 Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
133 R = Sk4f::Max(R + dR, min);
134 dR = dR + ddR;
135
136 uint8_t fi[4];
137 SkNx_cast<uint8_t>(dist).store(fi);
138
139 for (int i = 0; i < 4; i++) {
140 *dstC++ = cache[toggle + fi[i]];
141 toggle = next_dither_toggle(toggle);
142 }
143 }
144 count &= 3;
145 if (count) {
146 Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
147
148 uint8_t fi[4];
149 SkNx_cast<uint8_t>(dist).store(fi);
150 for (int i = 0; i < count; i++) {
151 *dstC++ = cache[toggle + fi[i]];
152 toggle = next_dither_toggle(toggle);
153 }
154 }
155 }
156 }
157
158 // Unrolling this loop doesn't seem to help (when float); we're stalling to
159 // get the results of the sqrt (?), and don't have enough extra registers to
160 // have many in flight.
161 template <SkFixed (*TileProc)(SkFixed)>
shadeSpan_radial(SkScalar fx,SkScalar dx,SkScalar fy,SkScalar dy,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int count,int toggle)162 void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
163 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
164 int count, int toggle) {
165 do {
166 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
167 const unsigned fi = TileProc(dist);
168 SkASSERT(fi <= 0xFFFF);
169 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
170 toggle = next_dither_toggle(toggle);
171 fx += dx;
172 fy += dy;
173 } while (--count != 0);
174 }
175
shadeSpan_radial_mirror(SkScalar fx,SkScalar dx,SkScalar fy,SkScalar dy,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int count,int toggle)176 void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
177 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
178 int count, int toggle) {
179 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
180 }
181
shadeSpan_radial_repeat(SkScalar fx,SkScalar dx,SkScalar fy,SkScalar dy,SkPMColor * SK_RESTRICT dstC,const SkPMColor * SK_RESTRICT cache,int count,int toggle)182 void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
183 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
184 int count, int toggle) {
185 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
186 }
187
188 } // namespace
189
shadeSpan(int x,int y,SkPMColor * SK_RESTRICT dstC,int count)190 void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
191 SkPMColor* SK_RESTRICT dstC, int count) {
192 SkASSERT(count > 0);
193
194 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
195
196 SkPoint srcPt;
197 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
198 TileProc proc = radialGradient.fTileProc;
199 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
200 int toggle = init_dither_toggle(x, y);
201
202 if (fDstToIndexClass != kPerspective_MatrixClass) {
203 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
204 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
205 SkScalar sdx = fDstToIndex.getScaleX();
206 SkScalar sdy = fDstToIndex.getSkewY();
207
208 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
209 const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
210 sdx = step.fX;
211 sdy = step.fY;
212 } else {
213 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
214 }
215
216 RadialShadeProc shadeProc = shadeSpan_radial_repeat;
217 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
218 shadeProc = shadeSpan_radial_clamp2;
219 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
220 shadeProc = shadeSpan_radial_mirror;
221 } else {
222 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
223 }
224 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
225 } else { // perspective case
226 SkScalar dstX = SkIntToScalar(x);
227 SkScalar dstY = SkIntToScalar(y);
228 do {
229 dstProc(fDstToIndex, dstX, dstY, &srcPt);
230 unsigned fi = proc(SkScalarToFixed(srcPt.length()));
231 SkASSERT(fi <= 0xFFFF);
232 *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
233 dstX += SK_Scalar1;
234 } while (--count != 0);
235 }
236 }
237
238 /////////////////////////////////////////////////////////////////////
239
240 #if SK_SUPPORT_GPU
241
242 #include "SkGr.h"
243 #include "glsl/GrGLSLCaps.h"
244 #include "glsl/GrGLSLFragmentShaderBuilder.h"
245
246 class GrRadialGradient : public GrGradientEffect {
247 public:
248 class GLSLRadialProcessor;
249
Make(const CreateArgs & args)250 static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
251 return sk_sp<GrFragmentProcessor>(new GrRadialGradient(args));
252 }
253
~GrRadialGradient()254 virtual ~GrRadialGradient() { }
255
name() const256 const char* name() const override { return "Radial Gradient"; }
257
258 private:
GrRadialGradient(const CreateArgs & args)259 GrRadialGradient(const CreateArgs& args)
260 : INHERITED(args) {
261 this->initClassID<GrRadialGradient>();
262 }
263
264 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
265
266 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
267 GrProcessorKeyBuilder* b) const override;
268
269 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
270
271 typedef GrGradientEffect INHERITED;
272 };
273
274 /////////////////////////////////////////////////////////////////////
275
276 class GrRadialGradient::GLSLRadialProcessor : public GrGradientEffect::GLSLProcessor {
277 public:
GLSLRadialProcessor(const GrProcessor &)278 GLSLRadialProcessor(const GrProcessor&) {}
~GLSLRadialProcessor()279 virtual ~GLSLRadialProcessor() { }
280
281 virtual void emitCode(EmitArgs&) override;
282
GenKey(const GrProcessor & processor,const GrGLSLCaps &,GrProcessorKeyBuilder * b)283 static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
284 b->add32(GenBaseGradientKey(processor));
285 }
286
287 private:
288 typedef GrGradientEffect::GLSLProcessor INHERITED;
289
290 };
291
292 /////////////////////////////////////////////////////////////////////
293
onCreateGLSLInstance() const294 GrGLSLFragmentProcessor* GrRadialGradient::onCreateGLSLInstance() const {
295 return new GrRadialGradient::GLSLRadialProcessor(*this);
296 }
297
onGetGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const298 void GrRadialGradient::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
299 GrProcessorKeyBuilder* b) const {
300 GrRadialGradient::GLSLRadialProcessor::GenKey(*this, caps, b);
301 }
302
303 /////////////////////////////////////////////////////////////////////
304
305 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
306
TestCreate(GrProcessorTestData * d)307 sk_sp<GrFragmentProcessor> GrRadialGradient::TestCreate(GrProcessorTestData* d) {
308 SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
309 SkScalar radius = d->fRandom->nextUScalar1();
310
311 SkColor colors[kMaxRandomGradientColors];
312 SkScalar stopsArray[kMaxRandomGradientColors];
313 SkScalar* stops = stopsArray;
314 SkShader::TileMode tm;
315 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
316 auto shader = SkGradientShader::MakeRadial(center, radius, colors, stops, colorCount, tm);
317 SkMatrix viewMatrix = GrTest::TestMatrix(d->fRandom);
318 auto dstColorSpace = GrTest::TestColorSpace(d->fRandom);
319 sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(SkShader::AsFPArgs(
320 d->fContext, &viewMatrix, NULL, kNone_SkFilterQuality, dstColorSpace.get(),
321 SkSourceGammaTreatment::kRespect));
322 GrAlwaysAssert(fp);
323 return fp;
324 }
325
326 /////////////////////////////////////////////////////////////////////
327
emitCode(EmitArgs & args)328 void GrRadialGradient::GLSLRadialProcessor::emitCode(EmitArgs& args) {
329 const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>();
330 this->emitUniforms(args.fUniformHandler, ge);
331 SkString t("length(");
332 t.append(args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]));
333 t.append(")");
334 this->emitColor(args.fFragBuilder,
335 args.fUniformHandler,
336 args.fGLSLCaps,
337 ge, t.c_str(),
338 args.fOutputColor,
339 args.fInputColor,
340 args.fTexSamplers);
341 }
342
343 /////////////////////////////////////////////////////////////////////
344
asFragmentProcessor(const AsFPArgs & args) const345 sk_sp<GrFragmentProcessor> SkRadialGradient::asFragmentProcessor(const AsFPArgs& args) const {
346 SkASSERT(args.fContext);
347
348 SkMatrix matrix;
349 if (!this->getLocalMatrix().invert(&matrix)) {
350 return nullptr;
351 }
352 if (args.fLocalMatrix) {
353 SkMatrix inv;
354 if (!args.fLocalMatrix->invert(&inv)) {
355 return nullptr;
356 }
357 matrix.postConcat(inv);
358 }
359 matrix.postConcat(fPtsToUnit);
360 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
361 args.fDstColorSpace);
362 sk_sp<GrFragmentProcessor> inner(GrRadialGradient::Make(
363 GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
364 std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
365 return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
366 }
367
368 #endif
369
370 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const371 void SkRadialGradient::toString(SkString* str) const {
372 str->append("SkRadialGradient: (");
373
374 str->append("center: (");
375 str->appendScalar(fCenter.fX);
376 str->append(", ");
377 str->appendScalar(fCenter.fY);
378 str->append(") radius: ");
379 str->appendScalar(fRadius);
380 str->append(" ");
381
382 this->INHERITED::toString(str);
383
384 str->append(")");
385 }
386 #endif
387