• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 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 "gm.h"
9 #include "SkGradientShader.h"
10 
11 namespace skiagm {
12 
13 struct GradData {
14     int              fCount;
15     const SkColor*   fColors;
16     const SkColor4f* fColors4f;
17     const SkScalar*  fPos;
18 };
19 
20 constexpr SkColor gColors[] = {
21     SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
22 };
23 constexpr SkColor4f gColors4f[] ={
24     { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
25     { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
26     { 0.0f, 0.0f, 1.0f, 1.0f }, // Blue
27     { 1.0f, 1.0f, 1.0f, 1.0f }, // White
28     { 0.0f, 0.0f, 0.0f, 1.0f }  // Black
29 };
30 constexpr SkScalar gPos0[] = { 0, SK_Scalar1 };
31 constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
32 constexpr SkScalar gPos2[] = {
33     0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
34 };
35 
36 constexpr SkScalar gPosClamp[]   = {0.0f, 0.0f, 1.0f, 1.0f};
37 constexpr SkColor  gColorClamp[] = {
38     SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
39 };
40 constexpr SkColor4f gColor4fClamp[] ={
41     { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
42     { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
43     { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
44     { 0.0f, 0.0f, 1.0f, 1.0f }  // Blue
45 };
46 constexpr GradData gGradData[] = {
47     { 2, gColors, gColors4f, nullptr },
48     { 2, gColors, gColors4f, gPos0 },
49     { 2, gColors, gColors4f, gPos1 },
50     { 5, gColors, gColors4f, nullptr },
51     { 5, gColors, gColors4f, gPos2 },
52     { 4, gColorClamp, gColor4fClamp, gPosClamp }
53 };
54 
MakeLinear(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)55 static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data,
56                                   SkShader::TileMode tm, const SkMatrix& localMatrix) {
57     return SkGradientShader::MakeLinear(pts, data.fColors, data.fPos, data.fCount, tm, 0,
58                                         &localMatrix);
59 }
60 
MakeLinear4f(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)61 static sk_sp<SkShader> MakeLinear4f(const SkPoint pts[2], const GradData& data,
62                                     SkShader::TileMode tm, const SkMatrix& localMatrix) {
63     auto srgb = SkColorSpace::MakeSRGB();
64     return SkGradientShader::MakeLinear(pts, data.fColors4f, srgb, data.fPos, data.fCount, tm, 0,
65                                         &localMatrix);
66 }
67 
MakeRadial(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)68 static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data,
69                                   SkShader::TileMode tm, const SkMatrix& localMatrix) {
70     SkPoint center;
71     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
72                SkScalarAve(pts[0].fY, pts[1].fY));
73     return SkGradientShader::MakeRadial(center, center.fX, data.fColors, data.fPos, data.fCount,
74                                         tm, 0, &localMatrix);
75 }
76 
MakeRadial4f(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)77 static sk_sp<SkShader> MakeRadial4f(const SkPoint pts[2], const GradData& data,
78                                     SkShader::TileMode tm, const SkMatrix& localMatrix) {
79     SkPoint center;
80     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
81                SkScalarAve(pts[0].fY, pts[1].fY));
82     auto srgb = SkColorSpace::MakeSRGB();
83     return SkGradientShader::MakeRadial(center, center.fX, data.fColors4f, srgb, data.fPos,
84                                         data.fCount, tm, 0, &localMatrix);
85 }
86 
MakeSweep(const SkPoint pts[2],const GradData & data,SkShader::TileMode,const SkMatrix & localMatrix)87 static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data,
88                                  SkShader::TileMode, const SkMatrix& localMatrix) {
89     SkPoint center;
90     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
91                SkScalarAve(pts[0].fY, pts[1].fY));
92     return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount,
93                                        0, &localMatrix);
94 }
95 
MakeSweep4f(const SkPoint pts[2],const GradData & data,SkShader::TileMode,const SkMatrix & localMatrix)96 static sk_sp<SkShader> MakeSweep4f(const SkPoint pts[2], const GradData& data,
97                                    SkShader::TileMode, const SkMatrix& localMatrix) {
98     SkPoint center;
99     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
100                SkScalarAve(pts[0].fY, pts[1].fY));
101     auto srgb = SkColorSpace::MakeSRGB();
102     return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors4f, srgb, data.fPos,
103                                        data.fCount, 0, &localMatrix);
104 }
105 
Make2Radial(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)106 static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data,
107                                    SkShader::TileMode tm, const SkMatrix& localMatrix) {
108     SkPoint center0, center1;
109     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
110                 SkScalarAve(pts[0].fY, pts[1].fY));
111     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
112                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
113     return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
114                                                  center0, (pts[1].fX - pts[0].fX) / 2,
115                                                  data.fColors, data.fPos, data.fCount, tm,
116                                                  0, &localMatrix);
117 }
118 
Make2Radial4f(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)119 static sk_sp<SkShader> Make2Radial4f(const SkPoint pts[2], const GradData& data,
120                                      SkShader::TileMode tm, const SkMatrix& localMatrix) {
121     SkPoint center0, center1;
122     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
123                 SkScalarAve(pts[0].fY, pts[1].fY));
124     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3) / 5),
125                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1) / 4));
126     auto srgb = SkColorSpace::MakeSRGB();
127     return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
128                                                  center0, (pts[1].fX - pts[0].fX) / 2,
129                                                  data.fColors4f, srgb, data.fPos, data.fCount, tm,
130                                                  0, &localMatrix);
131 }
132 
Make2Conical(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)133 static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data,
134                                     SkShader::TileMode tm, const SkMatrix& localMatrix) {
135     SkPoint center0, center1;
136     SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
137     SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
138     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
139     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
140     return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
141                                                  data.fColors, data.fPos,
142                                                  data.fCount, tm, 0, &localMatrix);
143 }
144 
Make2Conical4f(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)145 static sk_sp<SkShader> Make2Conical4f(const SkPoint pts[2], const GradData& data,
146                                       SkShader::TileMode tm, const SkMatrix& localMatrix) {
147     SkPoint center0, center1;
148     SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
149     SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
150     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
151     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
152     auto srgb = SkColorSpace::MakeSRGB();
153     return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
154                                                  data.fColors4f, srgb, data.fPos,
155                                                  data.fCount, tm, 0, &localMatrix);
156 }
157 
158 typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data,
159                                      SkShader::TileMode tm, const SkMatrix& localMatrix);
160 constexpr GradMaker gGradMakers[] = {
161     MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
162 };
163 constexpr GradMaker gGradMakers4f[] ={
164     MakeLinear4f, MakeRadial4f, MakeSweep4f, Make2Radial4f, Make2Conical4f
165 };
166 
167 ///////////////////////////////////////////////////////////////////////////////
168 
169 class GradientsGM : public GM {
170 public:
GradientsGM(bool dither)171     GradientsGM(bool dither) : fDither(dither) {
172         this->setBGColor(0xFFDDDDDD);
173     }
174 
175 protected:
176 
onShortName()177     SkString onShortName() {
178         return SkString(fDither ? "gradients" : "gradients_nodither");
179     }
180 
onISize()181     virtual SkISize onISize() { return SkISize::Make(840, 815); }
182 
onDraw(SkCanvas * canvas)183     virtual void onDraw(SkCanvas* canvas) {
184 
185         SkPoint pts[2] = {
186             { 0, 0 },
187             { SkIntToScalar(100), SkIntToScalar(100) }
188         };
189         SkShader::TileMode tm = SkShader::kClamp_TileMode;
190         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
191         SkPaint paint;
192         paint.setAntiAlias(true);
193         paint.setDither(fDither);
194 
195         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
196         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
197             canvas->save();
198             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
199                 SkMatrix scale = SkMatrix::I();
200 
201                 if (i == 5) { // if the clamp case
202                     scale.setScale(0.5f, 0.5f);
203                     scale.postTranslate(25.f, 25.f);
204                 }
205 
206                 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, scale));
207                 canvas->drawRect(r, paint);
208                 canvas->translate(0, SkIntToScalar(120));
209             }
210             canvas->restore();
211             canvas->translate(SkIntToScalar(120), 0);
212         }
213     }
214 
215 protected:
216     bool fDither;
217 
218 private:
219     typedef GM INHERITED;
220 };
221 DEF_GM( return new GradientsGM(true); )
222 DEF_GM( return new GradientsGM(false); )
223 
224 // Like the original gradients GM, but using the SkColor4f shader factories. Should be identical.
225 class Gradients4fGM : public GM {
226 public:
Gradients4fGM(bool dither)227     Gradients4fGM(bool dither) : fDither(dither) {
228         this->setBGColor(0xFFDDDDDD);
229     }
230 
231 protected:
232 
onShortName()233     SkString onShortName() {
234         return SkString(fDither ? "gradients4f" : "gradients4f_nodither");
235     }
236 
onISize()237     virtual SkISize onISize() { return SkISize::Make(840, 815); }
238 
onDraw(SkCanvas * canvas)239     virtual void onDraw(SkCanvas* canvas) {
240 
241         SkPoint pts[2] ={
242             { 0, 0 },
243             { SkIntToScalar(100), SkIntToScalar(100) }
244         };
245         SkShader::TileMode tm = SkShader::kClamp_TileMode;
246         SkRect r ={ 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
247         SkPaint paint;
248         paint.setAntiAlias(true);
249         paint.setDither(fDither);
250 
251         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
252         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
253             canvas->save();
254             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers4f); j++) {
255                 SkMatrix scale = SkMatrix::I();
256 
257                 if (i == 5) { // if the clamp case
258                     scale.setScale(0.5f, 0.5f);
259                     scale.postTranslate(25.f, 25.f);
260                 }
261 
262                 paint.setShader(gGradMakers4f[j](pts, gGradData[i], tm, scale));
263                 canvas->drawRect(r, paint);
264                 canvas->translate(0, SkIntToScalar(120));
265             }
266             canvas->restore();
267             canvas->translate(SkIntToScalar(120), 0);
268         }
269     }
270 
271 protected:
272     bool fDither;
273 
274 private:
275     typedef GM INHERITED;
276 };
277 DEF_GM(return new Gradients4fGM(true); )
278 DEF_GM(return new Gradients4fGM(false); )
279 
280 // Based on the original gradient slide, but with perspective applied to the
281 // gradient shaders' local matrices
282 class GradientsLocalPerspectiveGM : public GM {
283 public:
GradientsLocalPerspectiveGM(bool dither)284     GradientsLocalPerspectiveGM(bool dither) : fDither(dither) {
285         this->setBGColor(0xFFDDDDDD);
286     }
287 
288 protected:
289 
onShortName()290     SkString onShortName() {
291         return SkString(fDither ? "gradients_local_perspective" :
292                                   "gradients_local_perspective_nodither");
293     }
294 
onISize()295     virtual SkISize onISize() { return SkISize::Make(840, 815); }
296 
onDraw(SkCanvas * canvas)297     virtual void onDraw(SkCanvas* canvas) {
298 
299         SkPoint pts[2] = {
300             { 0, 0 },
301             { SkIntToScalar(100), SkIntToScalar(100) }
302         };
303         SkShader::TileMode tm = SkShader::kClamp_TileMode;
304         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
305         SkPaint paint;
306         paint.setAntiAlias(true);
307         paint.setDither(fDither);
308 
309         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
310         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
311             canvas->save();
312             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
313                 // apply an increasing y perspective as we move to the right
314                 SkMatrix perspective;
315                 perspective.setIdentity();
316                 perspective.setPerspY(SkIntToScalar(i+1) / 500);
317                 perspective.setSkewX(SkIntToScalar(i+1) / 10);
318 
319                 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, perspective));
320                 canvas->drawRect(r, paint);
321                 canvas->translate(0, SkIntToScalar(120));
322             }
323             canvas->restore();
324             canvas->translate(SkIntToScalar(120), 0);
325         }
326     }
327 
328 private:
329     bool fDither;
330 
331     typedef GM INHERITED;
332 };
333 DEF_GM( return new GradientsLocalPerspectiveGM(true); )
334 DEF_GM( return new GradientsLocalPerspectiveGM(false); )
335 
336 // Based on the original gradient slide, but with perspective applied to
337 // the view matrix
338 class GradientsViewPerspectiveGM : public GradientsGM {
339 public:
GradientsViewPerspectiveGM(bool dither)340     GradientsViewPerspectiveGM(bool dither) : INHERITED(dither) { }
341 
342 protected:
onShortName()343     SkString onShortName() {
344         return SkString(fDither ? "gradients_view_perspective" :
345                                   "gradients_view_perspective_nodither");
346     }
347 
onISize()348     virtual SkISize onISize() { return SkISize::Make(840, 500); }
349 
onDraw(SkCanvas * canvas)350     virtual void onDraw(SkCanvas* canvas) {
351         SkMatrix perspective;
352         perspective.setIdentity();
353         perspective.setPerspY(0.001f);
354         perspective.setSkewX(SkIntToScalar(8) / 25);
355         canvas->concat(perspective);
356         INHERITED::onDraw(canvas);
357     }
358 
359 private:
360     typedef GradientsGM INHERITED;
361 };
362 DEF_GM( return new GradientsViewPerspectiveGM(true); )
363 DEF_GM( return new GradientsViewPerspectiveGM(false); )
364 
365 /*
366  Inspired by this <canvas> javascript, where we need to detect that we are not
367  solving a quadratic equation, but must instead solve a linear (since our X^2
368  coefficient is 0)
369 
370  ctx.fillStyle = '#f00';
371  ctx.fillRect(0, 0, 100, 50);
372 
373  var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
374  g.addColorStop(0, '#f00');
375  g.addColorStop(0.01, '#0f0');
376  g.addColorStop(0.99, '#0f0');
377  g.addColorStop(1, '#f00');
378  ctx.fillStyle = g;
379  ctx.fillRect(0, 0, 100, 50);
380  */
381 class GradientsDegenrate2PointGM : public GM {
382 public:
GradientsDegenrate2PointGM(bool dither)383     GradientsDegenrate2PointGM(bool dither) : fDither(dither) {}
384 
385 protected:
onShortName()386     SkString onShortName() {
387         return SkString(fDither ? "gradients_degenerate_2pt" : "gradients_degenerate_2pt_nodither");
388     }
389 
onISize()390     virtual SkISize onISize() { return SkISize::Make(320, 320); }
391 
drawBG(SkCanvas * canvas)392     void drawBG(SkCanvas* canvas) {
393         canvas->drawColor(SK_ColorBLUE);
394     }
395 
onDraw(SkCanvas * canvas)396     virtual void onDraw(SkCanvas* canvas) {
397         this->drawBG(canvas);
398 
399         SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
400         SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 };
401         SkPoint c0;
402         c0.iset(-80, 25);
403         SkScalar r0 = SkIntToScalar(70);
404         SkPoint c1;
405         c1.iset(0, 25);
406         SkScalar r1 = SkIntToScalar(150);
407         SkPaint paint;
408         paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors,
409                                                               pos, SK_ARRAY_COUNT(pos),
410                                                               SkShader::kClamp_TileMode));
411         paint.setDither(fDither);
412         canvas->drawPaint(paint);
413     }
414 
415 private:
416     bool fDither;
417 
418     typedef GM INHERITED;
419 };
420 DEF_GM( return new GradientsDegenrate2PointGM(true); )
DEF_GM(return new GradientsDegenrate2PointGM (false);)421 DEF_GM( return new GradientsDegenrate2PointGM(false); )
422 
423 /* bug.skia.org/517
424 <canvas id="canvas"></canvas>
425 <script>
426 var c = document.getElementById("canvas");
427 var ctx = c.getContext("2d");
428 ctx.fillStyle = '#ff0';
429 ctx.fillRect(0, 0, 100, 50);
430 
431 var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
432 g.addColorStop(0, '#0f0');
433 g.addColorStop(0.003, '#f00');  // 0.004 makes this work
434 g.addColorStop(1, '#ff0');
435 ctx.fillStyle = g;
436 ctx.fillRect(0, 0, 100, 50);
437 </script>
438 */
439 
440 // should draw only green
441 DEF_SIMPLE_GM(small_color_stop, canvas, 100, 150) {
442     SkColor colors[] = { SK_ColorGREEN, SK_ColorRED, SK_ColorYELLOW };
443     SkScalar pos[] = { 0, 0.003f, SK_Scalar1 };  // 0.004f makes this work
444     SkPoint c0 = { 200, 25 };
445     SkScalar r0 = 20;
446     SkPoint c1 = { 200, 25 };
447     SkScalar r1 = 10;
448 
449     SkPaint paint;
450     paint.setColor(SK_ColorYELLOW);
451     canvas->drawRect(SkRect::MakeWH(100, 150), paint);
452     paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors, pos,
453                                                           SK_ARRAY_COUNT(pos),
454                                                           SkShader::kClamp_TileMode));
455     canvas->drawRect(SkRect::MakeWH(100, 150), paint);
456 }
457 
458 
459 /// Tests correctness of *optimized* codepaths in gradients.
460 
461 class ClampedGradientsGM : public GM {
462 public:
ClampedGradientsGM(bool dither)463     ClampedGradientsGM(bool dither) : fDither(dither) {}
464 
465 protected:
onShortName()466     SkString onShortName() {
467         return SkString(fDither ? "clamped_gradients" : "clamped_gradients_nodither");
468     }
469 
onISize()470     virtual SkISize onISize() { return SkISize::Make(640, 510); }
471 
drawBG(SkCanvas * canvas)472     void drawBG(SkCanvas* canvas) {
473         canvas->drawColor(0xFFDDDDDD);
474     }
475 
onDraw(SkCanvas * canvas)476     virtual void onDraw(SkCanvas* canvas) {
477         this->drawBG(canvas);
478 
479         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
480         SkPaint paint;
481         paint.setDither(fDither);
482         paint.setAntiAlias(true);
483 
484         SkPoint center;
485         center.iset(0, 300);
486         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
487         paint.setShader(SkGradientShader::MakeRadial(
488             SkPoint(center),
489             SkIntToScalar(200), gColors, nullptr, 5,
490             SkShader::kClamp_TileMode));
491         canvas->drawRect(r, paint);
492     }
493 
494 private:
495     bool fDither;
496 
497     typedef GM INHERITED;
498 };
499 DEF_GM( return new ClampedGradientsGM(true); )
500 DEF_GM( return new ClampedGradientsGM(false); )
501 
502 /// Checks quality of large radial gradients, which may display
503 /// some banding.
504 
505 class RadialGradientGM : public GM {
506 public:
RadialGradientGM()507     RadialGradientGM() {}
508 
509 protected:
510 
onShortName()511     SkString onShortName() override { return SkString("radial_gradient"); }
onISize()512     SkISize onISize() override { return SkISize::Make(1280, 1280); }
drawBG(SkCanvas * canvas)513     void drawBG(SkCanvas* canvas) {
514         canvas->drawColor(0xFF000000);
515     }
onDraw(SkCanvas * canvas)516     void onDraw(SkCanvas* canvas) override {
517         const SkISize dim = this->getISize();
518 
519         this->drawBG(canvas);
520 
521         SkPaint paint;
522         paint.setDither(true);
523         SkPoint center;
524         center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
525         SkScalar radius = SkIntToScalar(dim.width())/2;
526         const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
527         const SkScalar pos[] = { 0.0f,
528                              0.35f,
529                              1.0f };
530         paint.setShader(SkGradientShader::MakeRadial(center, radius, colors, pos,
531                                                      SK_ARRAY_COUNT(pos),
532                                                      SkShader::kClamp_TileMode));
533         SkRect r = {
534             0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
535         };
536         canvas->drawRect(r, paint);
537     }
538 private:
539     typedef GM INHERITED;
540 };
541 DEF_GM( return new RadialGradientGM; )
542 
543 class RadialGradient2GM : public GM {
544 public:
RadialGradient2GM(bool dither)545     RadialGradient2GM(bool dither) : fDither(dither) {}
546 
547 protected:
548 
onShortName()549     SkString onShortName() override {
550         return SkString(fDither ? "radial_gradient2" : "radial_gradient2_nodither");
551     }
552 
onISize()553     SkISize onISize() override { return SkISize::Make(800, 400); }
drawBG(SkCanvas * canvas)554     void drawBG(SkCanvas* canvas) {
555         canvas->drawColor(0xFF000000);
556     }
557 
558     // Reproduces the example given in bug 7671058.
onDraw(SkCanvas * canvas)559     void onDraw(SkCanvas* canvas) override {
560         SkPaint paint1, paint2, paint3;
561         paint1.setStyle(SkPaint::kFill_Style);
562         paint2.setStyle(SkPaint::kFill_Style);
563         paint3.setStyle(SkPaint::kFill_Style);
564 
565         const SkColor sweep_colors[] =
566             { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 };
567         const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 };
568         const SkColor colors2[] = { 0xFF000000, 0x00000000 };
569 
570         const SkScalar cx = 200, cy = 200, radius = 150;
571         SkPoint center;
572         center.set(cx, cy);
573 
574         // We can either interpolate endpoints and premultiply each point (default, more precision),
575         // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap).
576         const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag };
577 
578         for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) {
579             paint1.setShader(SkGradientShader::MakeSweep(cx, cy, sweep_colors,
580                                                          nullptr, SK_ARRAY_COUNT(sweep_colors),
581                                                          flags[i], nullptr));
582             paint2.setShader(SkGradientShader::MakeRadial(center, radius, colors1,
583                                                           nullptr, SK_ARRAY_COUNT(colors1),
584                                                           SkShader::kClamp_TileMode,
585                                                           flags[i], nullptr));
586             paint3.setShader(SkGradientShader::MakeRadial(center, radius, colors2,
587                                                           nullptr, SK_ARRAY_COUNT(colors2),
588                                                           SkShader::kClamp_TileMode,
589                                                           flags[i], nullptr));
590             paint1.setDither(fDither);
591             paint2.setDither(fDither);
592             paint3.setDither(fDither);
593 
594             canvas->drawCircle(cx, cy, radius, paint1);
595             canvas->drawCircle(cx, cy, radius, paint3);
596             canvas->drawCircle(cx, cy, radius, paint2);
597 
598             canvas->translate(400, 0);
599         }
600     }
601 
602 private:
603     bool fDither;
604 
605     typedef GM INHERITED;
606 };
607 DEF_GM( return new RadialGradient2GM(true); )
608 DEF_GM( return new RadialGradient2GM(false); )
609 
610 // Shallow radial (shows banding on raster)
611 class RadialGradient3GM : public GM {
612 public:
RadialGradient3GM(bool dither)613     RadialGradient3GM(bool dither) : fDither(dither) { }
614 
615 protected:
onShortName()616     SkString onShortName() override {
617         return SkString(fDither ? "radial_gradient3" : "radial_gradient3_nodither");
618     }
619 
onISize()620     SkISize onISize() override { return SkISize::Make(500, 500); }
621 
runAsBench() const622     bool runAsBench() const override { return true; }
623 
onOnceBeforeDraw()624     void onOnceBeforeDraw() override {
625         const SkPoint center = { 0, 0 };
626         const SkScalar kRadius = 3000;
627         const SkColor gColors[] = { 0xFFFFFFFF, 0xFF000000 };
628         fShader = SkGradientShader::MakeRadial(center, kRadius, gColors, nullptr, 2,
629                                                SkShader::kClamp_TileMode);
630     }
631 
onDraw(SkCanvas * canvas)632     void onDraw(SkCanvas* canvas) override {
633         SkPaint paint;
634         paint.setShader(fShader);
635         paint.setDither(fDither);
636         canvas->drawRect(SkRect::MakeWH(500, 500), paint);
637     }
638 
639 private:
640     sk_sp<SkShader> fShader;
641     bool fDither;
642 
643     typedef GM INHERITED;
644 };
645 DEF_GM( return new RadialGradient3GM(true); )
646 DEF_GM( return new RadialGradient3GM(false); )
647 
648 class RadialGradient4GM : public GM {
649 public:
RadialGradient4GM(bool dither)650     RadialGradient4GM(bool dither) : fDither(dither) { }
651 
652 protected:
onShortName()653     SkString onShortName() override {
654         return SkString(fDither ? "radial_gradient4" : "radial_gradient4_nodither");
655     }
656 
onISize()657     SkISize onISize() override { return SkISize::Make(500, 500); }
658 
onOnceBeforeDraw()659     void onOnceBeforeDraw() override {
660         const SkPoint center = { 250, 250 };
661         const SkScalar kRadius = 250;
662         const SkColor colors[] = { SK_ColorRED, SK_ColorRED, SK_ColorWHITE, SK_ColorWHITE,
663                 SK_ColorRED };
664         const SkScalar pos[] = { 0, .4f, .4f, .8f, .8f, 1 };
665         fShader = SkGradientShader::MakeRadial(center, kRadius, colors, pos,
666                                                SK_ARRAY_COUNT(gColors), SkShader::kClamp_TileMode);
667     }
668 
onDraw(SkCanvas * canvas)669     void onDraw(SkCanvas* canvas) override {
670         SkPaint paint;
671         paint.setAntiAlias(true);
672         paint.setDither(fDither);
673         paint.setShader(fShader);
674         canvas->drawRect(SkRect::MakeWH(500, 500), paint);
675     }
676 
677 private:
678     sk_sp<SkShader> fShader;
679     bool fDither;
680 
681     typedef GM INHERITED;
682 };
683 DEF_GM( return new RadialGradient4GM(true); )
684 DEF_GM( return new RadialGradient4GM(false); )
685 
686 class LinearGradientGM : public GM {
687 public:
LinearGradientGM(bool dither)688     LinearGradientGM(bool dither) : fDither(dither) { }
689 
690 protected:
onShortName()691     SkString onShortName() override {
692         return SkString(fDither ? "linear_gradient" : "linear_gradient_nodither");
693     }
694 
695     const SkScalar kWidthBump = 30.f;
696     const SkScalar kHeight = 5.f;
697     const SkScalar kMinWidth = 540.f;
698 
onISize()699     SkISize onISize() override { return SkISize::Make(500, 500); }
700 
onOnceBeforeDraw()701     void onOnceBeforeDraw() override {
702         SkPoint pts[2] = { {0, 0}, {0, 0} };
703         const SkColor colors[] = { SK_ColorWHITE, SK_ColorWHITE, 0xFF008200, 0xFF008200,
704                 SK_ColorWHITE, SK_ColorWHITE };
705         const SkScalar unitPos[] = { 0, 50, 70, 500, 540 };
706         SkScalar pos[6];
707         pos[5] = 1;
708         for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) {
709             pts[1].fX = 500.f + index * kWidthBump;
710             for (int inner = 0; inner < (int) SK_ARRAY_COUNT(unitPos); ++inner) {
711                 pos[inner] = unitPos[inner] / (kMinWidth + index * kWidthBump);
712             }
713             fShader[index] = SkGradientShader::MakeLinear(pts, colors, pos,
714                     SK_ARRAY_COUNT(gColors), SkShader::kClamp_TileMode);
715         }
716     }
717 
onDraw(SkCanvas * canvas)718     void onDraw(SkCanvas* canvas) override {
719         SkPaint paint;
720         paint.setAntiAlias(true);
721         paint.setDither(fDither);
722         for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) {
723             paint.setShader(fShader[index]);
724             canvas->drawRect(SkRect::MakeLTRB(0, index * kHeight, kMinWidth + index * kWidthBump,
725                     (index + 1) * kHeight), paint);
726         }
727     }
728 
729 private:
730     sk_sp<SkShader> fShader[100];
731     bool fDither;
732 
733     typedef GM INHERITED;
734 };
735 DEF_GM( return new LinearGradientGM(true); )
736 DEF_GM( return new LinearGradientGM(false); )
737 
738 class LinearGradientTinyGM : public GM {
739 public:
LinearGradientTinyGM(uint32_t flags,const char * suffix=nullptr)740     LinearGradientTinyGM(uint32_t flags, const char* suffix = nullptr)
741     : fName("linear_gradient_tiny")
742     , fFlags(flags) {
743         fName.append(suffix);
744     }
745 
746 protected:
onShortName()747     SkString onShortName() override {
748         return fName;
749     }
750 
onISize()751     SkISize onISize() override {
752         return SkISize::Make(600, 500);
753     }
754 
onDraw(SkCanvas * canvas)755     void onDraw(SkCanvas* canvas) override {
756         const SkScalar kRectSize = 100;
757         const unsigned kStopCount = 3;
758         const SkColor colors[kStopCount] = { SK_ColorGREEN, SK_ColorRED, SK_ColorGREEN };
759         const struct {
760             SkPoint pts[2];
761             SkScalar pos[kStopCount];
762         } configs[] = {
763             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.999999f,    1 }},
764             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.000001f,    1 }},
765             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.999999999f, 1 }},
766             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.000000001f, 1 }},
767 
768             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.999999f,    1 }},
769             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.000001f,    1 }},
770             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.999999999f, 1 }},
771             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.000000001f, 1 }},
772 
773             { { SkPoint::Make(0, 0),        SkPoint::Make(0.00001f, 0) }, { 0, 0.5f, 1 }},
774             { { SkPoint::Make(9.99999f, 0), SkPoint::Make(10, 0) },       { 0, 0.5f, 1 }},
775             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 0.00001f) }, { 0, 0.5f, 1 }},
776             { { SkPoint::Make(0, 9.99999f), SkPoint::Make(0, 10) },       { 0, 0.5f, 1 }},
777         };
778 
779         SkPaint paint;
780         for (unsigned i = 0; i < SK_ARRAY_COUNT(configs); ++i) {
781             SkAutoCanvasRestore acr(canvas, true);
782             paint.setShader(SkGradientShader::MakeLinear(configs[i].pts, colors, configs[i].pos,
783                                                          kStopCount, SkShader::kClamp_TileMode,
784                                                          fFlags, nullptr));
785             canvas->translate(kRectSize * ((i % 4) * 1.5f + 0.25f),
786                               kRectSize * ((i / 4) * 1.5f + 0.25f));
787 
788             canvas->drawRect(SkRect::MakeWH(kRectSize, kRectSize), paint);
789         }
790     }
791 
792 private:
793     typedef GM INHERITED;
794 
795     SkString fName;
796     uint32_t fFlags;
797 };
798 DEF_GM( return new LinearGradientTinyGM(0); )
799 }
800 
801 ///////////////////////////////////////////////////////////////////////////////////////////////////
802 
803 struct GradRun {
804     SkColor  fColors[4];
805     SkScalar fPos[4];
806     int      fCount;
807 };
808 
809 #define SIZE 121
810 
make_linear(const GradRun & run,SkShader::TileMode mode)811 static sk_sp<SkShader> make_linear(const GradRun& run, SkShader::TileMode mode) {
812     const SkPoint pts[] { { 30, 30 }, { SIZE - 30, SIZE - 30 } };
813     return SkGradientShader::MakeLinear(pts, run.fColors, run.fPos, run.fCount, mode);
814 }
815 
make_radial(const GradRun & run,SkShader::TileMode mode)816 static sk_sp<SkShader> make_radial(const GradRun& run, SkShader::TileMode mode) {
817     const SkScalar half = SIZE * 0.5f;
818     return SkGradientShader::MakeRadial({half,half}, half - 10, run.fColors, run.fPos,
819                                         run.fCount, mode);
820 }
821 
make_conical(const GradRun & run,SkShader::TileMode mode)822 static sk_sp<SkShader> make_conical(const GradRun& run, SkShader::TileMode mode) {
823     const SkScalar half = SIZE * 0.5f;
824     const SkPoint center { half, half };
825     return SkGradientShader::MakeTwoPointConical(center, 20, center, half - 10,
826                                                  run.fColors, run.fPos, run.fCount, mode);
827 }
828 
make_sweep(const GradRun & run,SkShader::TileMode)829 static sk_sp<SkShader> make_sweep(const GradRun& run, SkShader::TileMode) {
830     const SkScalar half = SIZE * 0.5f;
831     return SkGradientShader::MakeSweep(half, half, run.fColors, run.fPos, run.fCount);
832 }
833 
834 /*
835  *  Exercise duplicate color-stops, at the ends, and in the middle
836  *
837  *  At the time of this writing, only Linear correctly deals with duplicates at the ends,
838  *  and then only correctly on CPU backend.
839  */
840 DEF_SIMPLE_GM(gradients_dup_color_stops, canvas, 704, 564) {
841     const SkColor preColor  = 0xFFFF0000;   // clamp color before start
842     const SkColor postColor = 0xFF0000FF;   // clamp color after end
843     const SkColor color0    = 0xFF000000;
844     const SkColor color1    = 0xFF00FF00;
845     const SkColor badColor  = 0xFF3388BB;   // should never be seen, fills out fixed-size array
846 
847     const GradRun runs[] = {
848         {   { color0, color1, badColor, badColor },
849             { 0, 1, -1, -1 },
850             2,
851         },
852         {   { preColor, color0, color1, badColor },
853             { 0, 0, 1, -1 },
854             3,
855         },
856         {   { color0, color1, postColor, badColor },
857             { 0, 1, 1, -1 },
858             3,
859         },
860         {   { preColor, color0, color1, postColor },
861             { 0, 0, 1, 1 },
862             4,
863         },
864         {   { color0, color0, color1, color1 },
865             { 0, 0.5f, 0.5f, 1 },
866             4,
867         },
868     };
869     sk_sp<SkShader> (*factories[])(const GradRun&, SkShader::TileMode) {
870         make_linear, make_radial, make_conical, make_sweep
871     };
872 
873     const SkRect rect = SkRect::MakeWH(SIZE, SIZE);
874     const SkScalar dx = SIZE + 20;
875     const SkScalar dy = SIZE + 20;
876     const SkShader::TileMode mode = SkShader::kClamp_TileMode;
877 
878     SkPaint paint;
879     canvas->translate(10, 10 - dy);
880     for (auto factory : factories) {
881         canvas->translate(0, dy);
882         SkAutoCanvasRestore acr(canvas, true);
883         for (const auto& run : runs) {
884             paint.setShader(factory(run, mode));
885             canvas->drawRect(rect, paint);
886             canvas->translate(dx, 0);
887         }
888     }
889 }
890 
draw_many_stops(SkCanvas * canvas)891 static void draw_many_stops(SkCanvas* canvas) {
892     const unsigned kStopCount = 200;
893     const SkPoint pts[] = { {50, 50}, {450, 465}};
894 
895     SkColor colors[kStopCount];
896     for (unsigned i = 0; i < kStopCount; i++) {
897         switch (i % 5) {
898         case 0: colors[i] = SK_ColorRED; break;
899         case 1: colors[i] = SK_ColorGREEN; break;
900         case 2: colors[i] = SK_ColorGREEN; break;
901         case 3: colors[i] = SK_ColorBLUE; break;
902         case 4: colors[i] = SK_ColorRED; break;
903         }
904     }
905 
906     SkPaint p;
907     p.setShader(SkGradientShader::MakeLinear(
908         pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode));
909 
910     canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p);
911 }
912 
913 DEF_SIMPLE_GM(gradient_many_stops, canvas, 500, 500) {
914     draw_many_stops(canvas);
915 }
916 
917 #include "SkPictureRecorder.h"
918 
draw_circle_shader(SkCanvas * canvas,SkScalar cx,SkScalar cy,SkScalar r,sk_sp<SkShader> (* shaderFunc)())919 static void draw_circle_shader(SkCanvas* canvas, SkScalar cx, SkScalar cy, SkScalar r,
920                                sk_sp<SkShader> (*shaderFunc)()) {
921     SkPaint p;
922     p.setAntiAlias(true);
923     p.setShader(shaderFunc());
924     canvas->drawCircle(cx, cy, r, p);
925 
926     p.setShader(nullptr);
927     p.setColor(SK_ColorGRAY);
928     p.setStyle(SkPaint::kStroke_Style);
929     p.setStrokeWidth(2);
930     canvas->drawCircle(cx, cy, r, p);
931 }
932 
933 DEF_SIMPLE_GM(fancy_gradients, canvas, 800, 300) {
__anon259aafa60202() 934     draw_circle_shader(canvas, 150, 150, 100, []() -> sk_sp<SkShader> {
935         // Checkerboard using two linear gradients + picture shader.
936         SkScalar kTileSize = 80 / sqrtf(2);
937         SkColor colors1[] = { 0xff000000, 0xff000000,
938                               0xffffffff, 0xffffffff,
939                               0xff000000, 0xff000000 };
940         SkColor colors2[] = { 0xff000000, 0xff000000,
941                               0x00000000, 0x00000000,
942                               0xff000000, 0xff000000 };
943         SkScalar pos[] = { 0, .25f, .25f, .75f, .75f, 1 };
944         static_assert(SK_ARRAY_COUNT(colors1) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
945         static_assert(SK_ARRAY_COUNT(colors2) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
946 
947         SkPictureRecorder recorder;
948         recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize));
949 
950         SkPaint p;
951 
952         SkPoint pts1[] = { { 0, 0 }, { kTileSize, kTileSize }};
953         p.setShader(SkGradientShader::MakeLinear(pts1, colors1, pos, SK_ARRAY_COUNT(colors1),
954                                                  SkShader::kClamp_TileMode, 0, nullptr));
955         recorder.getRecordingCanvas()->drawPaint(p);
956 
957         SkPoint pts2[] = { { 0, kTileSize }, { kTileSize, 0 }};
958         p.setShader(SkGradientShader::MakeLinear(pts2, colors2, pos, SK_ARRAY_COUNT(colors2),
959                                                  SkShader::kClamp_TileMode, 0, nullptr));
960         recorder.getRecordingCanvas()->drawPaint(p);
961 
962         SkMatrix m = SkMatrix::I();
963         m.preRotate(45);
964         return SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
965                                            SkShader::kRepeat_TileMode,
966                                            SkShader::kRepeat_TileMode, &m, nullptr);
967     });
968 
__anon259aafa60302() 969     draw_circle_shader(canvas, 400, 150, 100, []() -> sk_sp<SkShader> {
970         // Checkerboard using a sweep gradient + picture shader.
971         SkScalar kTileSize = 80;
972         SkColor colors[] = { 0xff000000, 0xff000000,
973                              0xffffffff, 0xffffffff,
974                              0xff000000, 0xff000000,
975                              0xffffffff, 0xffffffff };
976         SkScalar pos[] = { 0, .25f, .25f, .5f, .5f, .75f, .75f, 1 };
977         static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
978 
979         SkPaint p;
980         p.setShader(SkGradientShader::MakeSweep(kTileSize / 2, kTileSize / 2,
981                                                 colors, pos, SK_ARRAY_COUNT(colors), 0, nullptr));
982         SkPictureRecorder recorder;
983         recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize))->drawPaint(p);
984 
985         return SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
986                                            SkShader::kRepeat_TileMode,
987                                            SkShader::kRepeat_TileMode, nullptr, nullptr);
988     });
989 
__anon259aafa60402() 990     draw_circle_shader(canvas, 650, 150, 100, []() -> sk_sp<SkShader> {
991         // Dartboard using sweep + radial.
992         const SkColor a = 0xffffffff;
993         const SkColor b = 0xff000000;
994         SkColor colors[] = { a, a, b, b, a, a, b, b, a, a, b, b, a, a, b, b};
995         SkScalar pos[] = { 0, .125f, .125f, .25f, .25f, .375f, .375f, .5f, .5f,
996                            .625f, .625f, .75f, .75f, .875f, .875f, 1};
997         static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
998 
999         SkPoint center = { 650, 150 };
1000         sk_sp<SkShader> sweep1 = SkGradientShader::MakeSweep(center.x(), center.y(), colors, pos,
1001                                                              SK_ARRAY_COUNT(colors), 0, nullptr);
1002         SkMatrix m = SkMatrix::I();
1003         m.preRotate(22.5f, center.x(), center.y());
1004         sk_sp<SkShader> sweep2 = SkGradientShader::MakeSweep(center.x(), center.y(), colors, pos,
1005                                                              SK_ARRAY_COUNT(colors), 0, &m);
1006 
1007         sk_sp<SkShader> sweep(SkShader::MakeComposeShader(sweep1, sweep2, SkBlendMode::kExclusion));
1008 
1009         SkScalar radialPos[] = { 0, .02f, .02f, .04f, .04f, .08f, .08f, .16f, .16f, .31f, .31f,
1010                                  .62f, .62f, 1, 1, 1 };
1011         static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(radialPos),
1012                       "color/pos size mismatch");
1013 
1014         return SkShader::MakeComposeShader(sweep,
1015                                            SkGradientShader::MakeRadial(center, 100, colors,
1016                                                                         radialPos,
1017                                                                         SK_ARRAY_COUNT(radialPos),
1018                                                                         SkShader::kClamp_TileMode),
1019                                            SkBlendMode::kExclusion);
1020     });
1021 }
1022 
1023 DEF_SIMPLE_GM(sweep_tiling, canvas, 690, 512) {
1024     static constexpr SkScalar size = 160;
1025     static constexpr SkColor colors[] = { SK_ColorBLUE, SK_ColorYELLOW, SK_ColorGREEN };
1026     static constexpr SkScalar   pos[] = { 0, .25f, .50f };
1027     static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "size mismatch");
1028 
1029     static constexpr SkShader::TileMode modes[] = { SkShader::kClamp_TileMode,
1030                                                     SkShader::kRepeat_TileMode,
1031                                                     SkShader::kMirror_TileMode };
1032 
1033     static const struct {
1034         SkScalar start, end;
1035     } angles[] = {
1036         { -330, -270 },
1037         {   30,   90 },
1038         {  390,  450 },
1039         {  -30,  800 },
1040     };
1041 
1042     SkPaint p;
1043     const SkRect r = SkRect::MakeWH(size, size);
1044 
1045     for (auto mode : modes) {
1046         {
1047             SkAutoCanvasRestore acr(canvas, true);
1048 
1049             for (auto angle : angles) {
1050                 p.setShader(SkGradientShader::MakeSweep(size / 2, size / 2, colors, pos,
1051                                                         SK_ARRAY_COUNT(colors), mode,
1052                                                         angle.start, angle.end, 0, nullptr));
1053 
1054                 canvas->drawRect(r, p);
1055                 canvas->translate(size * 1.1f, 0);
1056             }
1057         }
1058         canvas->translate(0, size * 1.1f);
1059     }
1060 }
1061 
1062 // Exercises the special-case Ganesh gradient effects.
1063 DEF_SIMPLE_GM(gradients_interesting, canvas, 640, 1300) {
1064     static const SkColor colors2[] = { SK_ColorRED, SK_ColorBLUE };
1065     static const SkColor colors3[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorBLUE };
1066     static const SkColor colors4[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorYELLOW, SK_ColorBLUE };
1067 
1068     static const SkScalar softRight[]  = { 0, .999f,   1 }; // Based on Android launcher "clipping"
1069     static const SkScalar hardLeft[]   = { 0,     0,   1 };
1070     static const SkScalar hardRight[]  = { 0,     1,   1 };
1071     static const SkScalar hardCenter[] = { 0,   .5f, .5f, 1 };
1072 
1073     static const struct {
1074         const SkColor*  colors;
1075         const SkScalar* pos;
1076         int             count;
1077     } configs[] = {
1078         { colors2,    nullptr, 2 }, // kTwo_ColorType
1079         { colors3,    nullptr, 3 }, // kThree_ColorType (simple)
1080         { colors3,  softRight, 3 }, // kThree_ColorType (tricky)
1081         { colors3,   hardLeft, 3 }, // kHardStopLeftEdged_ColorType
1082         { colors3,  hardRight, 3 }, // kHardStopRightEdged_ColorType
1083         { colors4, hardCenter, 4 }, // kSingleHardStop_ColorType
1084     };
1085 
1086     static const SkShader::TileMode modes[] = {
1087         SkShader::kClamp_TileMode,
1088         SkShader::kRepeat_TileMode,
1089         SkShader::kMirror_TileMode,
1090     };
1091 
1092     static constexpr SkScalar size = 200;
1093     static const SkPoint pts[] = { { size / 3, size / 3 }, { size * 2 / 3, size * 2 / 3} };
1094 
1095     SkPaint p;
1096     for (const auto& cfg : configs) {
1097         {
1098             SkAutoCanvasRestore acr(canvas, true);
1099             for (auto mode : modes) {
1100                 p.setShader(SkGradientShader::MakeLinear(pts, cfg.colors, cfg.pos, cfg.count,
1101                                                          mode));
1102                 canvas->drawRect(SkRect::MakeWH(size, size), p);
1103                 canvas->translate(size * 1.1f, 0);
1104             }
1105         }
1106         canvas->translate(0, size * 1.1f);
1107     }
1108 }
1109