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