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