• Home
  • History
  • Annotate
  • 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::MakeSRGBLinear();
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::MakeSRGBLinear();
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::MakeSRGBLinear();
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::MakeSRGBLinear();
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::MakeSRGBLinear();
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) {
__anon98014c940202() 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  
__anon98014c940302() 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  
__anon98014c940402() 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