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