Flutter Engine
The Flutter Engine
gradients.cpp
Go to the documentation of this file.
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/gm.h"
13#include "include/core/SkFont.h"
19#include "include/core/SkRect.h"
23#include "include/core/SkSize.h"
29#include "tools/ToolUtils.h"
31
32#include <initializer_list>
33#include <math.h>
34
35namespace {
36
37struct GradData {
38 int fCount;
39 const SkColor* fColors;
40 const SkColor4f* fColors4f;
41 const SkScalar* fPos;
42};
43
44constexpr SkColor gColors[] = {
46};
47constexpr SkColor4f gColors4f[] ={
48 { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
49 { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
50 { 0.0f, 0.0f, 1.0f, 1.0f }, // Blue
51 { 1.0f, 1.0f, 1.0f, 1.0f }, // White
52 { 0.0f, 0.0f, 0.0f, 1.0f } // Black
53};
54constexpr SkScalar gPos0[] = { 0, SK_Scalar1 };
55constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
56constexpr SkScalar gPos2[] = {
58};
59
60constexpr SkScalar gPosClamp[] = {0.0f, 0.0f, 1.0f, 1.0f};
61constexpr SkColor gColorClamp[] = {
63};
64constexpr SkColor4f gColor4fClamp[] ={
65 { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
66 { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
67 { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
68 { 0.0f, 0.0f, 1.0f, 1.0f } // Blue
69};
70constexpr GradData gGradData[] = {
71 { 2, gColors, gColors4f, nullptr },
72 { 2, gColors, gColors4f, gPos0 },
73 { 2, gColors, gColors4f, gPos1 },
74 { 5, gColors, gColors4f, nullptr },
75 { 5, gColors, gColors4f, gPos2 },
76 { 4, gColorClamp, gColor4fClamp, gPosClamp }
77};
78
79static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data,
80 SkTileMode tm, const SkMatrix& localMatrix) {
81 return SkGradientShader::MakeLinear(pts, data.fColors, data.fPos, data.fCount, tm, 0,
82 &localMatrix);
83}
84
85static sk_sp<SkShader> MakeLinear4f(const SkPoint pts[2], const GradData& data,
86 SkTileMode tm, const SkMatrix& localMatrix) {
87 auto srgb = SkColorSpace::MakeSRGB();
88 return SkGradientShader::MakeLinear(pts, data.fColors4f, srgb, data.fPos, data.fCount, tm, 0,
89 &localMatrix);
90}
91
92static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data,
93 SkTileMode tm, const SkMatrix& localMatrix) {
95 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
96 SkScalarAve(pts[0].fY, pts[1].fY));
97 return SkGradientShader::MakeRadial(center, center.fX, data.fColors, data.fPos, data.fCount,
98 tm, 0, &localMatrix);
99}
100
101static sk_sp<SkShader> MakeRadial4f(const SkPoint pts[2], const GradData& data,
102 SkTileMode tm, const SkMatrix& localMatrix) {
104 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
105 SkScalarAve(pts[0].fY, pts[1].fY));
106 auto srgb = SkColorSpace::MakeSRGB();
107 return SkGradientShader::MakeRadial(center, center.fX, data.fColors4f, srgb, data.fPos,
108 data.fCount, tm, 0, &localMatrix);
109}
110
111static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data,
112 SkTileMode, const SkMatrix& localMatrix) {
114 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
115 SkScalarAve(pts[0].fY, pts[1].fY));
116 return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount,
117 0, &localMatrix);
118}
119
120static sk_sp<SkShader> MakeSweep4f(const SkPoint pts[2], const GradData& data,
121 SkTileMode, const SkMatrix& localMatrix) {
123 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
124 SkScalarAve(pts[0].fY, pts[1].fY));
125 auto srgb = SkColorSpace::MakeSRGB();
126 return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors4f, srgb, data.fPos,
127 data.fCount, 0, &localMatrix);
128}
129
130static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data,
131 SkTileMode tm, const SkMatrix& localMatrix) {
132 SkPoint center0, center1;
133 center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
134 SkScalarAve(pts[0].fY, pts[1].fY));
135 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
136 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
137 return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
138 center0, (pts[1].fX - pts[0].fX) / 2,
139 data.fColors, data.fPos, data.fCount, tm,
140 0, &localMatrix);
141}
142
143static sk_sp<SkShader> Make2Radial4f(const SkPoint pts[2], const GradData& data,
144 SkTileMode tm, const SkMatrix& localMatrix) {
145 SkPoint center0, center1;
146 center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
147 SkScalarAve(pts[0].fY, pts[1].fY));
148 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3) / 5),
149 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1) / 4));
150 auto srgb = SkColorSpace::MakeSRGB();
151 return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
152 center0, (pts[1].fX - pts[0].fX) / 2,
153 data.fColors4f, srgb, data.fPos, data.fCount, tm,
154 0, &localMatrix);
155}
156
157static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data,
158 SkTileMode tm, const SkMatrix& localMatrix) {
159 SkPoint center0, center1;
160 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
161 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
162 center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
163 center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
164 return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
165 data.fColors, data.fPos,
166 data.fCount, tm, 0, &localMatrix);
167}
168
169static sk_sp<SkShader> Make2Conical4f(const SkPoint pts[2], const GradData& data,
170 SkTileMode tm, const SkMatrix& localMatrix) {
171 SkPoint center0, center1;
172 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
173 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
174 center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
175 center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
176 auto srgb = SkColorSpace::MakeSRGB();
177 return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
178 data.fColors4f, srgb, data.fPos,
179 data.fCount, tm, 0, &localMatrix);
180}
181
182typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data,
183 SkTileMode tm, const SkMatrix& localMatrix);
184constexpr GradMaker gGradMakers[] = {
186};
187constexpr GradMaker gGradMakers4f[] ={
188 MakeLinear4f, MakeRadial4f, MakeSweep4f, Make2Radial4f, Make2Conical4f
189};
190
191///////////////////////////////////////////////////////////////////////////////
192
193class GradientsGM : public skiagm::GM {
194public:
195 GradientsGM(bool dither) : fDither(dither) {}
196
197protected:
198 const bool fDither;
199
200 void onDraw(SkCanvas* canvas) override {
201 SkPoint pts[2] = {
202 { 0, 0 },
203 { SkIntToScalar(100), SkIntToScalar(100) }
204 };
206 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
208 paint.setAntiAlias(true);
209 paint.setDither(fDither);
210
211 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
212 for (size_t i = 0; i < std::size(gGradData); i++) {
213 canvas->save();
214 for (size_t j = 0; j < std::size(gGradMakers); j++) {
216
217 if (i == 5) { // if the clamp case
218 scale.setScale(0.5f, 0.5f);
219 scale.postTranslate(25.f, 25.f);
220 }
221
222 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, scale));
223 canvas->drawRect(r, paint);
224 canvas->translate(0, SkIntToScalar(120));
225 }
226 canvas->restore();
227 canvas->translate(SkIntToScalar(120), 0);
228 }
229 }
230
231private:
232 void onOnceBeforeDraw() override { this->setBGColor(0xFFDDDDDD); }
233
234 SkString getName() const override {
235 return SkString(fDither ? "gradients" : "gradients_nodither");
236 }
237
238 SkISize getISize() override { return {840, 815}; }
239};
240DEF_GM( return new GradientsGM(true); )
241DEF_GM( return new GradientsGM(false); )
242
243// Like the original gradients GM, but using the SkColor4f shader factories. Should be identical.
244class Gradients4fGM : public skiagm::GM {
245public:
246 Gradients4fGM(bool dither) : fDither(dither) {}
247
248private:
249 void onOnceBeforeDraw() override { this->setBGColor(0xFFDDDDDD); }
250
251 SkString getName() const override {
252 return SkString(fDither ? "gradients4f" : "gradients4f_nodither");
253 }
254
255 SkISize getISize() override { return {840, 815}; }
256
257 void onDraw(SkCanvas* canvas) override {
258 SkPoint pts[2] ={
259 { 0, 0 },
260 { SkIntToScalar(100), SkIntToScalar(100) }
261 };
263 SkRect r ={ 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
265 paint.setAntiAlias(true);
266 paint.setDither(fDither);
267
268 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
269 for (size_t i = 0; i < std::size(gGradData); i++) {
270 canvas->save();
271 for (size_t j = 0; j < std::size(gGradMakers4f); j++) {
273
274 if (i == 5) { // if the clamp case
275 scale.setScale(0.5f, 0.5f);
276 scale.postTranslate(25.f, 25.f);
277 }
278
279 paint.setShader(gGradMakers4f[j](pts, gGradData[i], tm, scale));
280 canvas->drawRect(r, paint);
281 canvas->translate(0, SkIntToScalar(120));
282 }
283 canvas->restore();
284 canvas->translate(SkIntToScalar(120), 0);
285 }
286 }
287
288 bool fDither;
289};
290DEF_GM(return new Gradients4fGM(true); )
291DEF_GM(return new Gradients4fGM(false); )
292
293// Based on the original gradient slide, but with perspective applied to the
294// gradient shaders' local matrices
295class GradientsLocalPerspectiveGM : public skiagm::GM {
296public:
297 GradientsLocalPerspectiveGM(bool dither) : fDither(dither) {
298 this->setBGColor(0xFFDDDDDD);
299 }
300
301private:
302 SkString getName() const override {
303 return SkString(fDither ? "gradients_local_perspective" :
304 "gradients_local_perspective_nodither");
305 }
306
307 SkISize getISize() override { return {840, 815}; }
308
309 void onDraw(SkCanvas* canvas) override {
310 SkPoint pts[2] = {
311 { 0, 0 },
312 { SkIntToScalar(100), SkIntToScalar(100) }
313 };
315 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
317 paint.setAntiAlias(true);
318 paint.setDither(fDither);
319
320 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
321 for (size_t i = 0; i < std::size(gGradData); i++) {
322 canvas->save();
323 for (size_t j = 0; j < std::size(gGradMakers); j++) {
324 // apply an increasing y perspective as we move to the right
325 SkMatrix perspective;
326 perspective.setIdentity();
327 perspective.setPerspY(SkIntToScalar(i+1) / 500);
328 perspective.setSkewX(SkIntToScalar(i+1) / 10);
329
330 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, perspective));
331 canvas->drawRect(r, paint);
332 canvas->translate(0, SkIntToScalar(120));
333 }
334 canvas->restore();
335 canvas->translate(SkIntToScalar(120), 0);
336 }
337 }
338
339 bool fDither;
340};
341DEF_GM( return new GradientsLocalPerspectiveGM(true); )
342DEF_GM( return new GradientsLocalPerspectiveGM(false); )
343
344// Based on the original gradient slide, but with perspective applied to
345// the view matrix
346class GradientsViewPerspectiveGM : public GradientsGM {
347public:
348 GradientsViewPerspectiveGM(bool dither) : INHERITED(dither) { }
349
350private:
351 SkString getName() const override {
352 return SkString(fDither ? "gradients_view_perspective" :
353 "gradients_view_perspective_nodither");
354 }
355
356 SkISize getISize() override { return {840, 500}; }
357
358 void onDraw(SkCanvas* canvas) override {
359 SkMatrix perspective;
360 perspective.setIdentity();
361 perspective.setPerspY(0.001f);
362 perspective.setSkewX(SkIntToScalar(8) / 25);
363 canvas->concat(perspective);
364 this->INHERITED::onDraw(canvas);
365 }
366
367private:
368 using INHERITED = GradientsGM;
369};
370DEF_GM( return new GradientsViewPerspectiveGM(true); )
371DEF_GM( return new GradientsViewPerspectiveGM(false); )
372
373/*
374 Inspired by this <canvas> javascript, where we need to detect that we are not
375 solving a quadratic equation, but must instead solve a linear (since our X^2
376 coefficient is 0)
377
378 ctx.fillStyle = '#f00';
379 ctx.fillRect(0, 0, 100, 50);
380
381 var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
382 g.addColorStop(0, '#f00');
383 g.addColorStop(0.01, '#0f0');
384 g.addColorStop(0.99, '#0f0');
385 g.addColorStop(1, '#f00');
386 ctx.fillStyle = g;
387 ctx.fillRect(0, 0, 100, 50);
388 */
389class GradientsDegenrate2PointGM : public skiagm::GM {
390public:
391 GradientsDegenrate2PointGM(bool dither) : fDither(dither) {}
392
393private:
394 SkString getName() const override {
395 return SkString(fDither ? "gradients_degenerate_2pt" : "gradients_degenerate_2pt_nodither");
396 }
397
398 SkISize getISize() override { return {320, 320}; }
399
400 void onDraw(SkCanvas* canvas) override {
401 canvas->drawColor(SK_ColorBLUE);
402
404 SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 };
405 SkPoint c0;
406 c0.iset(-80, 25);
407 SkScalar r0 = SkIntToScalar(70);
408 SkPoint c1;
409 c1.iset(0, 25);
410 SkScalar r1 = SkIntToScalar(150);
412 paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors,
413 pos, std::size(pos),
415 paint.setDither(fDither);
416 canvas->drawPaint(paint);
417 }
418
419 bool fDither;
420};
421DEF_GM( return new GradientsDegenrate2PointGM(true); )
422DEF_GM( return new GradientsDegenrate2PointGM(false); )
423
424/* bug.skia.org/517
425<canvas id="canvas"></canvas>
426<script>
427var c = document.getElementById("canvas");
428var ctx = c.getContext("2d");
429ctx.fillStyle = '#ff0';
430ctx.fillRect(0, 0, 100, 50);
431
432var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
433g.addColorStop(0, '#0f0');
434g.addColorStop(0.003, '#f00'); // 0.004 makes this work
435g.addColorStop(1, '#ff0');
436ctx.fillStyle = g;
437ctx.fillRect(0, 0, 100, 50);
438</script>
439*/
440
441// should draw only green
442DEF_SIMPLE_GM(small_color_stop, canvas, 100, 150) {
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
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 std::size(pos),
456 canvas->drawRect(SkRect::MakeWH(100, 150), paint);
457}
458
459
460/// Tests correctness of *optimized* codepaths in gradients.
461
462class ClampedGradientsGM : public skiagm::GM {
463public:
464 ClampedGradientsGM(bool dither) : fDither(dither) {}
465
466private:
467 SkString getName() const override {
468 return SkString(fDither ? "clamped_gradients" : "clamped_gradients_nodither");
469 }
470
471 SkISize getISize() override { return {640, 510}; }
472
473 void onDraw(SkCanvas* canvas) override {
474 canvas->drawColor(0xFFDDDDDD);
475
476 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
478 paint.setDither(fDither);
479 paint.setAntiAlias(true);
480
482 center.iset(0, 300);
483 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
486 SkIntToScalar(200), gColors, nullptr, 5,
488 canvas->drawRect(r, paint);
489 }
490
491 bool fDither;
492};
493DEF_GM( return new ClampedGradientsGM(true); )
494DEF_GM( return new ClampedGradientsGM(false); )
495
496/// Checks quality of large radial gradients, which may display
497/// some banding.
498
499class RadialGradientGM : public skiagm::GM {
500 SkString getName() const override { return SkString("radial_gradient"); }
501
502 SkISize getISize() override { return {1280, 1280}; }
503
504 void onDraw(SkCanvas* canvas) override {
505 const SkISize dim = this->getISize();
506
507 canvas->drawColor(0xFF000000);
508
510 paint.setDither(true);
512 center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
513 SkScalar radius = SkIntToScalar(dim.width())/2;
514 const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
515 const SkScalar pos[] = { 0.0f,
516 0.35f,
517 1.0f };
519 std::size(pos),
521 SkRect r = {
522 0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
523 };
524 canvas->drawRect(r, paint);
525 }
526};
527DEF_GM( return new RadialGradientGM; )
528
529class RadialGradient2GM : public skiagm::GM {
530public:
531 RadialGradient2GM(bool dither) : fDither(dither) {}
532
533private:
534 SkString getName() const override {
535 return SkString(fDither ? "radial_gradient2" : "radial_gradient2_nodither");
536 }
537
538 SkISize getISize() override { return {800, 400}; }
539
540 // Reproduces the example given in bug 7671058.
541 void onDraw(SkCanvas* canvas) override {
542 SkPaint paint1, paint2, paint3;
546
547 const SkColor sweep_colors[] =
548 { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 };
549 const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 };
550 const SkColor colors2[] = { 0xFF000000, 0x00000000 };
551
552 const SkScalar cx = 200, cy = 200, radius = 150;
554 center.set(cx, cy);
555
556 // We can either interpolate endpoints and premultiply each point (default, more precision),
557 // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap).
559
560 for (size_t i = 0; i < std::size(flags); i++) {
561 paint1.setShader(SkGradientShader::MakeSweep(cx, cy, sweep_colors,
562 nullptr, std::size(sweep_colors),
563 flags[i], nullptr));
564 paint2.setShader(SkGradientShader::MakeRadial(center, radius, colors1,
565 nullptr, std::size(colors1),
567 flags[i], nullptr));
568 paint3.setShader(SkGradientShader::MakeRadial(center, radius, colors2,
569 nullptr, std::size(colors2),
571 flags[i], nullptr));
572 paint1.setDither(fDither);
573 paint2.setDither(fDither);
574 paint3.setDither(fDither);
575
576 canvas->drawCircle(cx, cy, radius, paint1);
577 canvas->drawCircle(cx, cy, radius, paint3);
578 canvas->drawCircle(cx, cy, radius, paint2);
579
580 canvas->translate(400, 0);
581 }
582 }
583
584private:
585 bool fDither;
586
587 using INHERITED = GM;
588};
589DEF_GM( return new RadialGradient2GM(true); )
590DEF_GM( return new RadialGradient2GM(false); )
591
592// Shallow radial (shows banding on raster)
593class RadialGradient3GM : public skiagm::GM {
594public:
595 RadialGradient3GM(bool dither) : fDither(dither) { }
596
597private:
598 SkString getName() const override {
599 return SkString(fDither ? "radial_gradient3" : "radial_gradient3_nodither");
600 }
601
602 SkISize getISize() override { return {500, 500}; }
603
604 bool runAsBench() const override { return true; }
605
606 void onOnceBeforeDraw() override {
607 const SkPoint center = { 0, 0 };
608 const SkScalar kRadius = 3000;
609 const SkColor kColors[] = { 0xFFFFFFFF, 0xFF000000 };
610 fShader = SkGradientShader::MakeRadial(center, kRadius, kColors, nullptr, 2,
612 }
613
614 void onDraw(SkCanvas* canvas) override {
616 paint.setShader(fShader);
617 paint.setDither(fDither);
618 canvas->drawRect(SkRect::MakeWH(500, 500), paint);
619 }
620
621private:
622 sk_sp<SkShader> fShader;
623 bool fDither;
624
625 using INHERITED = GM;
626};
627DEF_GM( return new RadialGradient3GM(true); )
628DEF_GM( return new RadialGradient3GM(false); )
629
630class RadialGradient4GM : public skiagm::GM {
631public:
632 RadialGradient4GM(bool dither) : fDither(dither) { }
633
634private:
635 SkString getName() const override {
636 return SkString(fDither ? "radial_gradient4" : "radial_gradient4_nodither");
637 }
638
639 SkISize getISize() override { return {500, 500}; }
640
641 void onOnceBeforeDraw() override {
642 const SkPoint center = { 250, 250 };
643 const SkScalar kRadius = 250;
645 SK_ColorRED };
646 const SkScalar pos[] = { 0, .4f, .4f, .8f, .8f, 1 };
649 }
650
651 void onDraw(SkCanvas* canvas) override {
653 paint.setAntiAlias(true);
654 paint.setDither(fDither);
655 paint.setShader(fShader);
656 canvas->drawRect(SkRect::MakeWH(500, 500), paint);
657 }
658
659private:
660 sk_sp<SkShader> fShader;
661 bool fDither;
662
663 using INHERITED = GM;
664};
665DEF_GM( return new RadialGradient4GM(true); )
666DEF_GM( return new RadialGradient4GM(false); )
667
668class LinearGradientGM : public skiagm::GM {
669public:
670 LinearGradientGM(bool dither) : fDither(dither) { }
671
672private:
673 SkString getName() const override {
674 return SkString(fDither ? "linear_gradient" : "linear_gradient_nodither");
675 }
676
677 const SkScalar kWidthBump = 30.f;
678 const SkScalar kHeight = 5.f;
679 const SkScalar kMinWidth = 540.f;
680
681 SkISize getISize() override { return {500, 500}; }
682
683 void onOnceBeforeDraw() override {
684 SkPoint pts[2] = { {0, 0}, {0, 0} };
685 const SkColor colors[] = { SK_ColorWHITE, SK_ColorWHITE, 0xFF008200, 0xFF008200,
687 const SkScalar unitPos[] = { 0, 50, 70, 500, 540 };
688 SkScalar pos[6];
689 pos[5] = 1;
690 for (int index = 0; index < (int) std::size(fShader); ++index) {
691 pts[1].fX = 500.f + index * kWidthBump;
692 for (int inner = 0; inner < (int) std::size(unitPos); ++inner) {
693 pos[inner] = unitPos[inner] / (kMinWidth + index * kWidthBump);
694 }
695 fShader[index] = SkGradientShader::MakeLinear(pts, colors, pos,
697 }
698 }
699
700 void onDraw(SkCanvas* canvas) override {
702 paint.setAntiAlias(true);
703 paint.setDither(fDither);
704 for (int index = 0; index < (int) std::size(fShader); ++index) {
705 paint.setShader(fShader[index]);
706 canvas->drawRect(SkRect::MakeLTRB(0, index * kHeight, kMinWidth + index * kWidthBump,
707 (index + 1) * kHeight), paint);
708 }
709 }
710
711private:
712 sk_sp<SkShader> fShader[100];
713 bool fDither;
714
715 using INHERITED = GM;
716};
717DEF_GM( return new LinearGradientGM(true); )
718DEF_GM( return new LinearGradientGM(false); )
719
720class LinearGradientTinyGM : public skiagm::GM {
721 inline static constexpr uint32_t kFlags = 0;
722
723 SkString getName() const override { return SkString("linear_gradient_tiny"); }
724
725 SkISize getISize() override { return {600, 500}; }
726
727 void onDraw(SkCanvas* canvas) override {
728 const SkScalar kRectSize = 100;
729 const unsigned kStopCount = 3;
731 const struct {
732 SkPoint pts[2];
734 } configs[] = {
735 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.999999f, 1 }},
736 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.000001f, 1 }},
737 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.999999999f, 1 }},
738 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.000000001f, 1 }},
739
740 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.999999f, 1 }},
741 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.000001f, 1 }},
742 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.999999999f, 1 }},
743 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.000000001f, 1 }},
744
745 { { SkPoint::Make(0, 0), SkPoint::Make(0.00001f, 0) }, { 0, 0.5f, 1 }},
746 { { SkPoint::Make(9.99999f, 0), SkPoint::Make(10, 0) }, { 0, 0.5f, 1 }},
747 { { SkPoint::Make(0, 0), SkPoint::Make(0, 0.00001f) }, { 0, 0.5f, 1 }},
748 { { SkPoint::Make(0, 9.99999f), SkPoint::Make(0, 10) }, { 0, 0.5f, 1 }},
749 };
750
752 for (unsigned i = 0; i < std::size(configs); ++i) {
753 SkAutoCanvasRestore acr(canvas, true);
756 kFlags, nullptr));
757 canvas->translate(kRectSize * ((i % 4) * 1.5f + 0.25f),
758 kRectSize * ((i / 4) * 1.5f + 0.25f));
759
760 canvas->drawRect(SkRect::MakeWH(kRectSize, kRectSize), paint);
761 }
762 }
763};
764
765DEF_GM( return new LinearGradientTinyGM; )
766} // namespace
767
768///////////////////////////////////////////////////////////////////////////////////////////////////
769
770struct GradRun {
774};
775
776#define SIZE 121
777
779 const SkPoint pts[] { { 30, 30 }, { SIZE - 30, SIZE - 30 } };
780 return SkGradientShader::MakeLinear(pts, run.fColors, run.fPos, run.fCount, mode);
781}
782
784 const SkScalar half = SIZE * 0.5f;
785 return SkGradientShader::MakeRadial({half,half}, half - 10, run.fColors, run.fPos,
786 run.fCount, mode);
787}
788
790 const SkScalar half = SIZE * 0.5f;
791 const SkPoint center { half, half };
793 run.fColors, run.fPos, run.fCount, mode);
794}
795
797 const SkScalar half = SIZE * 0.5f;
798 return SkGradientShader::MakeSweep(half, half, run.fColors, run.fPos, run.fCount);
799}
800
801/*
802 * Exercise duplicate color-stops, at the ends, and in the middle
803 *
804 * At the time of this writing, only Linear correctly deals with duplicates at the ends,
805 * and then only correctly on CPU backend.
806 */
807DEF_SIMPLE_GM(gradients_dup_color_stops, canvas, 704, 564) {
808 const SkColor preColor = 0xFFFF0000; // clamp color before start
809 const SkColor postColor = 0xFF0000FF; // clamp color after end
810 const SkColor color0 = 0xFF000000;
811 const SkColor color1 = 0xFF00FF00;
812 const SkColor badColor = 0xFF3388BB; // should never be seen, fills out fixed-size array
813
814 const GradRun runs[] = {
815 { { color0, color1, badColor, badColor },
816 { 0, 1, -1, -1 },
817 2,
818 },
819 { { preColor, color0, color1, badColor },
820 { 0, 0, 1, -1 },
821 3,
822 },
823 { { color0, color1, postColor, badColor },
824 { 0, 1, 1, -1 },
825 3,
826 },
827 { { preColor, color0, color1, postColor },
828 { 0, 0, 1, 1 },
829 4,
830 },
831 { { color0, color0, color1, color1 },
832 { 0, 0.5f, 0.5f, 1 },
833 4,
834 },
835 };
836 sk_sp<SkShader> (*factories[])(const GradRun&, SkTileMode) {
838 };
839
841 const SkScalar dx = SIZE + 20;
842 const SkScalar dy = SIZE + 20;
844
846 canvas->translate(10, 10 - dy);
847 for (auto factory : factories) {
848 canvas->translate(0, dy);
849 SkAutoCanvasRestore acr(canvas, true);
850 for (const auto& run : runs) {
851 paint.setShader(factory(run, mode));
852 canvas->drawRect(rect, paint);
853 canvas->translate(dx, 0);
854 }
855 }
856}
857
858static void draw_many_stops(SkCanvas* canvas) {
859 const unsigned kStopCount = 200;
860 const SkPoint pts[] = { {50, 50}, {450, 450}};
861
863 for (unsigned i = 0; i < kStopCount; i++) {
864 switch (i % 5) {
865 case 0: colors[i] = SK_ColorRED; break;
866 case 1: colors[i] = SK_ColorGREEN; break;
867 case 2: colors[i] = SK_ColorGREEN; break;
868 case 3: colors[i] = SK_ColorBLUE; break;
869 case 4: colors[i] = SK_ColorRED; break;
870 }
871 }
872
873 SkPaint p;
874 p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, std::size(colors),
876
877 canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p);
878}
879
880DEF_SIMPLE_GM(gradient_many_stops, canvas, 500, 500) {
881 draw_many_stops(canvas);
882}
883
884static void draw_many_hard_stops(SkCanvas* canvas) {
885 const unsigned kStopCount = 300;
886 const SkPoint pts[] = {{50, 50}, {450, 450}};
887
890 for (unsigned i = 0; i < kStopCount; i++) {
891 switch (i % 6) {
892 case 0: colors[i] = SK_ColorRED; break;
893 case 1: colors[i] = SK_ColorGREEN; break;
894 case 2: colors[i] = SK_ColorGREEN; break;
895 case 3: colors[i] = SK_ColorBLUE; break;
896 case 4: colors[i] = SK_ColorBLUE; break;
897 case 5: colors[i] = SK_ColorRED; break;
898 }
899 pos[i] = (2.0f * (i / 2)) / kStopCount;
900 }
901
902 SkPaint p;
905
906 canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p);
907}
908
909DEF_SIMPLE_GM(gradient_many_hard_stops, canvas, 500, 500) {
910 draw_many_hard_stops(canvas);
911}
912
913static void draw_circle_shader(SkCanvas* canvas, SkScalar cx, SkScalar cy, SkScalar r,
914 sk_sp<SkShader> (*shaderFunc)()) {
915 SkPaint p;
916 p.setAntiAlias(true);
917 p.setShader(shaderFunc());
918 canvas->drawCircle(cx, cy, r, p);
919
920 p.setShader(nullptr);
921 p.setColor(SK_ColorGRAY);
922 p.setStyle(SkPaint::kStroke_Style);
923 p.setStrokeWidth(2);
924 canvas->drawCircle(cx, cy, r, p);
925}
926
927DEF_SIMPLE_GM(fancy_gradients, canvas, 800, 300) {
928 draw_circle_shader(canvas, 150, 150, 100, []() -> sk_sp<SkShader> {
929 // Checkerboard using two linear gradients + picture shader.
930 SkScalar kTileSize = 80 / sqrtf(2);
931 SkColor colors1[] = { 0xff000000, 0xff000000,
932 0xffffffff, 0xffffffff,
933 0xff000000, 0xff000000 };
934 SkColor colors2[] = { 0xff000000, 0xff000000,
935 0x00000000, 0x00000000,
936 0xff000000, 0xff000000 };
937 SkScalar pos[] = { 0, .25f, .25f, .75f, .75f, 1 };
938 static_assert(std::size(colors1) == std::size(pos), "color/pos size mismatch");
939 static_assert(std::size(colors2) == std::size(pos), "color/pos size mismatch");
940
941 SkPictureRecorder recorder;
942 recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize));
943
944 SkPaint p;
945
946 SkPoint pts1[] = { { 0, 0 }, { kTileSize, kTileSize }};
947 p.setShader(SkGradientShader::MakeLinear(pts1, colors1, pos, std::size(colors1),
948 SkTileMode::kClamp, 0, nullptr));
949 recorder.getRecordingCanvas()->drawPaint(p);
950
951 SkPoint pts2[] = { { 0, kTileSize }, { kTileSize, 0 }};
952 p.setShader(SkGradientShader::MakeLinear(pts2, colors2, pos, std::size(colors2),
953 SkTileMode::kClamp, 0, nullptr));
954 recorder.getRecordingCanvas()->drawPaint(p);
955
957 m.preRotate(45);
958 return recorder.finishRecordingAsPicture()->makeShader(
960 SkFilterMode::kNearest, &m, nullptr);
961 });
962
963 draw_circle_shader(canvas, 400, 150, 100, []() -> sk_sp<SkShader> {
964 // Checkerboard using a sweep gradient + picture shader.
965 SkScalar kTileSize = 80;
966 SkColor colors[] = { 0xff000000, 0xff000000,
967 0xffffffff, 0xffffffff,
968 0xff000000, 0xff000000,
969 0xffffffff, 0xffffffff };
970 SkScalar pos[] = { 0, .25f, .25f, .5f, .5f, .75f, .75f, 1 };
971 static_assert(std::size(colors) == std::size(pos), "color/pos size mismatch");
972
973 SkPaint p;
974 p.setShader(SkGradientShader::MakeSweep(kTileSize / 2, kTileSize / 2,
975 colors, pos, std::size(colors), 0, nullptr));
976 SkPictureRecorder recorder;
977 recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize))->drawPaint(p);
978
979 return recorder.finishRecordingAsPicture()->makeShader(
982 });
983
984 draw_circle_shader(canvas, 650, 150, 100, []() -> sk_sp<SkShader> {
985 // Dartboard using sweep + radial.
986 const SkColor a = 0xffffffff;
987 const SkColor b = 0xff000000;
988 SkColor colors[] = { a, a, b, b, a, a, b, b, a, a, b, b, a, a, b, b};
989 SkScalar pos[] = { 0, .125f, .125f, .25f, .25f, .375f, .375f, .5f, .5f,
990 .625f, .625f, .75f, .75f, .875f, .875f, 1};
991 static_assert(std::size(colors) == std::size(pos), "color/pos size mismatch");
992
993 SkPoint center = { 650, 150 };
995 std::size(colors), 0, nullptr);
997 m.preRotate(22.5f, center.x(), center.y());
999 std::size(colors), 0, &m);
1000
1002
1003 SkScalar radialPos[] = { 0, .02f, .02f, .04f, .04f, .08f, .08f, .16f, .16f, .31f, .31f,
1004 .62f, .62f, 1, 1, 1 };
1005 static_assert(std::size(colors) == std::size(radialPos),
1006 "color/pos size mismatch");
1007
1010 radialPos,
1011 std::size(radialPos),
1013 });
1014}
1015
1016DEF_SIMPLE_GM(sweep_tiling, canvas, 690, 512) {
1017 static constexpr SkScalar size = 160;
1018 static constexpr SkColor colors[] = { SK_ColorBLUE, SK_ColorYELLOW, SK_ColorGREEN };
1019 static constexpr SkScalar pos[] = { 0, .25f, .50f };
1020 static_assert(std::size(colors) == std::size(pos), "size mismatch");
1021
1022 static constexpr SkTileMode modes[] = { SkTileMode::kClamp,
1025
1026 static const struct {
1028 } angles[] = {
1029 { -330, -270 },
1030 { 30, 90 },
1031 { 390, 450 },
1032 { -30, 800 },
1033 };
1034
1035 SkPaint p;
1036 const SkRect r = SkRect::MakeWH(size, size);
1037
1038 for (auto mode : modes) {
1039 {
1040 SkAutoCanvasRestore acr(canvas, true);
1041
1042 for (auto angle : angles) {
1043 p.setShader(SkGradientShader::MakeSweep(size / 2, size / 2, colors, pos,
1045 angle.start, angle.end, 0, nullptr));
1046
1047 canvas->drawRect(r, p);
1048 canvas->translate(size * 1.1f, 0);
1049 }
1050 }
1051 canvas->translate(0, size * 1.1f);
1052 }
1053}
1054
1055DEF_SIMPLE_GM(rgbw_sweep_gradient, canvas, 100, 100) {
1056 static constexpr SkScalar size = 100;
1057 static constexpr SkColor colors[] = {SK_ColorWHITE, SK_ColorWHITE,
1061 static constexpr SkScalar pos[] = { 0, .25f, .25f, .50f, .50f, .75, .75, 1 };
1062 static_assert(std::size(colors) == std::size(pos), "size mismatch");
1063
1064 SkPaint p;
1066 canvas->drawRect(SkRect::MakeWH(size, size), p);
1067}
1068
1069// Exercises the special-case Ganesh gradient effects.
1070DEF_SIMPLE_GM(gradients_interesting, canvas, 640, 1300) {
1071 static const SkColor colors2[] = { SK_ColorRED, SK_ColorBLUE };
1072 static const SkColor colors3[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorBLUE };
1073 static const SkColor colors4[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorYELLOW, SK_ColorBLUE };
1074
1075 static const SkScalar softRight[] = { 0, .999f, 1 }; // Based on Android launcher "clipping"
1076 static const SkScalar hardLeft[] = { 0, 0, 1 };
1077 static const SkScalar hardRight[] = { 0, 1, 1 };
1078 static const SkScalar hardCenter[] = { 0, .5f, .5f, 1 };
1079
1080 static const struct {
1081 const SkColor* colors;
1082 const SkScalar* pos;
1083 int count;
1084 } configs[] = {
1085 { colors2, nullptr, 2 }, // kTwo_ColorType
1086 { colors3, nullptr, 3 }, // kThree_ColorType (simple)
1087 { colors3, softRight, 3 }, // kThree_ColorType (tricky)
1088 { colors3, hardLeft, 3 }, // kHardStopLeftEdged_ColorType
1089 { colors3, hardRight, 3 }, // kHardStopRightEdged_ColorType
1090 { colors4, hardCenter, 4 }, // kSingleHardStop_ColorType
1091 };
1092
1093 static const SkTileMode modes[] = {
1097 };
1098
1099 static constexpr SkScalar size = 200;
1100 static const SkPoint pts[] = { { size / 3, size / 3 }, { size * 2 / 3, size * 2 / 3} };
1101
1102 SkPaint p;
1103 for (const auto& cfg : configs) {
1104 {
1105 SkAutoCanvasRestore acr(canvas, true);
1106 for (auto mode : modes) {
1107 p.setShader(SkGradientShader::MakeLinear(pts, cfg.colors, cfg.pos, cfg.count,
1108 mode));
1109 canvas->drawRect(SkRect::MakeWH(size, size), p);
1110 canvas->translate(size * 1.1f, 0);
1111 }
1112 }
1113 canvas->translate(0, size * 1.1f);
1114 }
1115}
1116
1117// TODO(skia:13774): Still need to test degenerate gradients in strange color spaces
1118DEF_SIMPLE_GM_BG(gradients_color_space, canvas, 265, 255, SK_ColorGRAY) {
1120
1121 struct Config {
1122 CS fColorSpace;
1123 const char* fLabel;
1124 };
1125 static const Config kConfigs[] = {
1126 { CS::kSRGB, "sRGB" },
1127 { CS::kSRGBLinear, "Linear" },
1128 { CS::kLab, "Lab" },
1129 { CS::kOKLab, "OKLab" },
1130 { CS::kOKLabGamutMap, "OKLabGamutMap" },
1131 { CS::kLCH, "LCH" },
1132 { CS::kOKLCH, "OKLCH" },
1133 { CS::kOKLCHGamutMap, "OKLCHGamutMap" },
1134 { CS::kHSL, "HSL" },
1135 { CS::kHWB, "HWB" },
1136 };
1137
1138 SkPoint pts[] = {{0, 0}, {200, 0}};
1140
1141 SkPaint labelPaint;
1142 SkPaint p;
1143 SkGradientShader::Interpolation interpolation;
1144 canvas->translate(5, 5);
1146
1147 for (const Config& config : kConfigs) {
1148 interpolation.fColorSpace = config.fColorSpace;
1149 p.setShader(SkGradientShader::MakeLinear(pts, colors, SkColorSpace::MakeSRGB(), nullptr, 2,
1150 SkTileMode::kClamp, interpolation, nullptr));
1151 canvas->drawRect({0, 0, 200, 20}, p);
1152 canvas->drawSimpleText(config.fLabel, strlen(config.fLabel), SkTextEncoding::kUTF8, 210, 15,
1153 font, labelPaint);
1154 canvas->translate(0, 25);
1155 }
1156}
1157
1158DEF_SIMPLE_GM_BG(gradients_hue_method, canvas, 285, 155, SK_ColorGRAY) {
1160
1161 struct Config {
1162 HM fHueMethod;
1163 const char* fLabel;
1164 };
1165 static const Config kConfigs[] = {
1166 { HM::kShorter, "Shorter" },
1167 { HM::kLonger, "Longer" },
1168 { HM::kIncreasing, "Increasing" },
1169 { HM::kDecreasing, "Decreasing" },
1170 };
1171
1172 SkPoint pts[] = {{0, 0}, {200, 0}};
1174
1175 SkPaint labelPaint;
1176 SkPaint p;
1177 SkGradientShader::Interpolation interpolation;
1179 canvas->translate(5, 5);
1181
1182 for (const Config& config : kConfigs) {
1183 interpolation.fHueMethod = config.fHueMethod;
1184 p.setShader(SkGradientShader::MakeLinear(pts, colors, SkColorSpace::MakeSRGB(), nullptr, 4,
1185 SkTileMode::kClamp, interpolation, nullptr));
1186 canvas->drawRect({0, 0, 200, 20}, p);
1187 canvas->drawSimpleText(config.fLabel, strlen(config.fLabel), SkTextEncoding::kUTF8, 210, 15,
1188 font, labelPaint);
1189 canvas->translate(0, 25);
1190 }
1191
1192 // Test a bug (skia:13941) with how gradient shaders handle explicit positions.
1193 // If there are no explicit positions at 0 or 1, those are automatically added, with copies of
1194 // the first/last color. When using kLonger, this can produce extra gradient that should
1195 // actually be solid. This gradient *should* be:
1196 // |- solid red -|- red to green, the long way -|- solid green -|
1197 interpolation.fHueMethod = HM::kLonger;
1198 SkScalar middlePos[] = { 0.3f, 0.7f };
1199 p.setShader(SkGradientShader::MakeLinear(pts, colors, SkColorSpace::MakeSRGB(), middlePos, 2,
1200 SkTileMode::kClamp, interpolation, nullptr));
1201 canvas->drawRect({0, 0, 200, 20}, p);
1202 canvas->translate(0, 25);
1203
1204 // However... if the user explicitly includes those duplicate color stops in kLonger mode,
1205 // we expect the gradient to do a full rotation in those regions:
1206 // |- full circle, red to red -|- red to green -|- full circle, green to green -|
1207 colors[0] = colors[1] = SkColors::kRed;
1208 colors[2] = colors[3] = SkColors::kGreen;
1209 SkScalar allPos[] = { 0.0f, 0.3f, 0.7f, 1.0f };
1210 p.setShader(SkGradientShader::MakeLinear(pts, colors, SkColorSpace::MakeSRGB(), allPos, 4,
1211 SkTileMode::kClamp, interpolation, nullptr));
1212 canvas->drawRect({0, 0, 200, 20}, p);
1213 canvas->translate(0, 25);
1214}
1215
1216DEF_SIMPLE_GM_BG(gradients_color_space_tilemode, canvas, 360, 105, SK_ColorGRAY) {
1217 // Test exotic (CSS) gradient color spaces in conjunction with tile modes. Rather than test
1218 // every combination, we pick one color space that has a sufficiently strange interpolated
1219 // representation (OKLCH) and just use that. We're mostly interested in making sure that things
1220 // like decal mode are implemented at the correct time in the pipeline, relative to hue
1221 // conversion, re-premultiplication, etc.
1222 SkPoint pts[] = {{20, 0}, {120, 0}};
1224
1225 SkPaint p;
1226 SkGradientShader::Interpolation interpolation;
1228
1229 canvas->translate(5, 5);
1230
1231 for (int tm = 0; tm < kSkTileModeCount; ++tm) {
1232 p.setShader(SkGradientShader::MakeLinear(pts, colors, SkColorSpace::MakeSRGB(), nullptr, 2,
1233 static_cast<SkTileMode>(tm), interpolation,
1234 nullptr));
1235 canvas->drawRect({0, 0, 350, 20}, p);
1236 canvas->translate(0, 25);
1237 }
1238}
1239
1240DEF_SIMPLE_GM_BG(gradients_color_space_many_stops, canvas, 500, 500, SK_ColorGRAY) {
1241 // Test exotic (CSS) gradient color spaces with many stops. Rather than test every combination,
1242 // we pick one color space that has a sufficiently strange interpolated representation (OKLCH)
1243 // and just use that. We're mostly interested in making sure that the texture fallback on GPU
1244 // works correctly.
1245 const SkPoint pts[] = { {50, 50}, {450, 465}};
1246
1247 const unsigned kStopCount = 200;
1249 for (unsigned i = 0; i < kStopCount; i++) {
1250 switch (i % 5) {
1251 case 0: colors[i] = SkColors::kRed; break;
1252 case 1: colors[i] = SkColors::kGreen; break;
1253 case 2: colors[i] = SkColors::kGreen; break;
1254 case 3: colors[i] = SkColors::kBlue; break;
1255 case 4: colors[i] = SkColors::kRed; break;
1256 }
1257 }
1258
1259 SkPaint p;
1260
1261 SkGradientShader::Interpolation interpolation;
1264 std::size(colors), SkTileMode::kClamp, interpolation,
1265 nullptr));
1266
1267 canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p);
1268}
1269
1273
1274 auto nextRow = [=]() {
1275 canvas->restore();
1276 canvas->translate(0, 25);
1277 canvas->save();
1278 };
1279
1280 auto gradient = [&](std::initializer_list<SkColor4f> colors,
1281 std::initializer_list<float> pos,
1282 bool inPremul = false) {
1283 using Interpolation = SkGradientShader::Interpolation;
1284 SkASSERT(pos.size() == 0 || pos.size() == colors.size());
1285 SkPaint paint;
1286 SkPoint pts[] = {{0, 0}, {200, 0}};
1287 Interpolation interpolation;
1288 interpolation.fColorSpace = colorSpace;
1289 interpolation.fInPremul = static_cast<Interpolation::InPremul>(inPremul);
1290 paint.setShader(SkGradientShader::MakeLinear(pts,
1291 colors.begin(),
1293 pos.size() == 0 ? nullptr : pos.begin(),
1294 colors.size(),
1296 interpolation,
1297 nullptr));
1298 canvas->drawRect({0, 0, 200, 20}, paint);
1299 canvas->translate(205, 0); // next column
1300 };
1301
1302 canvas->translate(5, 5);
1303 canvas->save();
1304
1305 // For each test case, the first gradient (first column) has an under-specified result due to a
1306 // powerless component after conversion to LCH. The second gradient (second column) "hints" the
1307 // correct result, by slightly tinting the otherwise powerless color.
1308
1309 gradient({SkColors::kWhite, SkColors::kBlue}, {});
1310 gradient({{0.99f, 0.99f, 1.00f, 1.0f}, SkColors::kBlue}, {}); // white, with blue hue
1311 nextRow();
1312
1313 gradient({SkColors::kBlack, SkColors::kBlue}, {});
1314 gradient({{0.00f, 0.00f, 0.01f, 1.0f}, SkColors::kBlue}, {}); // black, with blue hue
1315 nextRow();
1316
1317 // Transparent cases are done in both premul and unpremul interpolation:
1318
1319 gradient({SkColors::kTransparent, SkColors::kBlue}, {}, /*inPremul=*/false);
1320 gradient({{0.00f, 0.00f, 0.01f, 0.0f}, SkColors::kBlue}, {}, /*inPremul=*/false);
1321 nextRow();
1322
1323 gradient({SkColors::kTransparent, SkColors::kBlue}, {}, /*inPremul=*/true);
1324 gradient({{0.00f, 0.00f, 0.01f, 0.0f}, SkColors::kBlue}, {}, /*inPremul=*/true);
1325 nextRow();
1326
1327 gradient({{1.00f, 1.00f, 1.00f, 0.0f}, SkColors::kBlue}, {}, /*inPremul=*/false);
1328 gradient({{0.99f, 0.99f, 1.00f, 0.0f}, SkColors::kBlue}, {}, /*inPremul=*/false);
1329 nextRow();
1330
1331 gradient({{1.00f, 1.00f, 1.00f, 0.0f}, SkColors::kBlue}, {}, /*inPremul=*/true);
1332 gradient({{0.99f, 0.99f, 1.00f, 0.0f}, SkColors::kBlue}, {}, /*inPremul=*/true);
1333 nextRow();
1334
1335 // Now we test three-stop gradients, where the middle stop needs to be "split" to handle the
1336 // different hues on either side. Again, the second column explicitly injects those to produce
1337 // a reference result. See: https://github.com/w3c/csswg-drafts/issues/9295
1338
1340 gradient({SkColors::kRed,
1341 {1.00f, 0.99f, 0.99f, 1.0f},
1342 {0.99f, 0.99f, 1.00f, 1.0f},
1344 {0.0f, 0.5f, 0.5f, 1.0f});
1345 nextRow();
1346
1348 gradient({SkColors::kRed,
1349 {0.01f, 0.00f, 0.00f, 1.0f},
1350 {0.00f, 0.00f, 0.01f, 1.0f},
1352 {0.0f, 0.5f, 0.5f, 1.0f});
1353 nextRow();
1354
1356 gradient({SkColors::kRed,
1357 {0.01f, 0.00f, 0.00f, 0.0f},
1358 {0.00f, 0.00f, 0.01f, 0.0f},
1360 {0.0f, 0.5f, 0.5f, 1.0f});
1361 nextRow();
1362
1363 // Now do a few black-white tests, to ensure that the hue propagation works correctly, even
1364 // when there isn't any hue in the adjacent stops.
1366 auto blackWhiteGradient = [&](HueMethod hm) {
1367 using Interpolation = SkGradientShader::Interpolation;
1368 SkPaint paint;
1369 SkPoint pts[] = {{0, 0}, {405, 0}};
1370 Interpolation interpolation;
1371 interpolation.fColorSpace = colorSpace;
1372 interpolation.fHueMethod = hm;
1376 paint.setShader(SkGradientShader::MakeLinear(pts,
1377 colors,
1379 nullptr,
1382 interpolation,
1383 nullptr));
1384 canvas->drawRect({0, 0, 405, 20}, paint);
1385 nextRow();
1386 };
1387
1388 blackWhiteGradient(HueMethod::kShorter);
1389 blackWhiteGradient(HueMethod::kIncreasing);
1390 blackWhiteGradient(HueMethod::kDecreasing);
1391 blackWhiteGradient(HueMethod::kLonger);
1392}
1393
1394#define DEF_POWERLESS_HUE_GM(colorSpace) \
1395 DEF_SIMPLE_GM(gradients_powerless_hue_##colorSpace, canvas, 415, 330) { \
1396 draw_powerless_hue_gradients(canvas, \
1397 SkGradientShader::Interpolation::ColorSpace::k##colorSpace); \
1398 }
1399
static const int kStopCount
int count
Definition: FontMgrTest.cpp:50
static const GradData gGradData[]
static sk_sp< SkShader > MakeSweep(const SkPoint pts[2], const GradData &data, SkTileMode tm, float scale)
Ignores scale.
static sk_sp< SkShader > MakeLinear(const SkPoint pts[2], const GradData &data, SkTileMode tm, float scale)
Ignores scale.
sk_sp< SkShader >(* GradMaker)(const SkPoint pts[2], const GradData &data, SkTileMode tm, float scale)
static sk_sp< SkShader > MakeRadial(const SkPoint pts[2], const GradData &data, SkTileMode tm, float scale)
static const SkColor gColors[]
SkPoint pos
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kExclusion
rc = s + d - two(s*d), ra = kSrcOver
constexpr SkColor SK_ColorYELLOW
Definition: SkColor.h:139
uint32_t SkColor
Definition: SkColor.h:37
constexpr SkColor SK_ColorGRAY
Definition: SkColor.h:113
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition: SkColor.h:122
@ kUTF8
uses bytes to represent UTF-8 or ASCII
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
#define SK_Scalar1
Definition: SkScalar.h:18
#define SkScalarAve(a, b)
Definition: SkScalar.h:74
#define SkIntToScalar(x)
Definition: SkScalar.h:57
static SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t)
Definition: SkScalar.h:131
SkTileMode
Definition: SkTileMode.h:13
static constexpr int kSkTileModeCount
Definition: SkTileMode.h:39
static const SkScalar gPos2[]
static const SkScalar gPos1[]
static const SkScalar gPos0[]
static SkScalar center(float pos0, float pos1)
SkISize getISize() override
void onDraw(SkCanvas *canvas) override
SkString getName() const override
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void restore()
Definition: SkCanvas.cpp:461
void drawSimpleText(const void *text, size_t byteLength, SkTextEncoding encoding, SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition: SkCanvas.cpp:2413
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
Definition: SkCanvas.h:1182
void drawPaint(const SkPaint &paint)
Definition: SkCanvas.cpp:1668
int save()
Definition: SkCanvas.cpp:447
void concat(const SkMatrix &matrix)
Definition: SkCanvas.cpp:1318
void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint &paint)
Definition: SkCanvas.cpp:2707
static sk_sp< SkColorSpace > MakeSRGB()
Definition: SkFont.h:35
static sk_sp< SkShader > MakeTwoPointConical(const SkPoint &start, SkScalar startRadius, const SkPoint &end, SkScalar endRadius, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static sk_sp< SkShader > MakeSweep(SkScalar cx, SkScalar cy, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, SkScalar startAngle, SkScalar endAngle, uint32_t flags, const SkMatrix *localMatrix)
static sk_sp< SkShader > MakeRadial(const SkPoint &center, SkScalar radius, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static sk_sp< SkShader > MakeLinear(const SkPoint pts[2], const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
SkMatrix & setSkewX(SkScalar v)
Definition: SkMatrix.h:518
SkMatrix & setPerspY(SkScalar v)
Definition: SkMatrix.h:544
SkMatrix & setIdentity()
Definition: SkMatrix.h:626
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
void setStyle(Style style)
Definition: SkPaint.cpp:105
void setDither(bool dither)
Definition: SkPaint.h:182
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
@ kFill_Style
set to fill geometry
Definition: SkPaint.h:193
void setShader(sk_sp< SkShader > shader)
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
SkCanvas * getRecordingCanvas()
sk_sp< SkPicture > finishRecordingAsPicture()
sk_sp< SkShader > makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode mode, const SkMatrix *localMatrix, const SkRect *tileRect) const
Definition: gm.h:110
virtual SkISize getISize()=0
virtual void onOnceBeforeDraw()
Definition: gm.cpp:167
void setBGColor(SkColor)
Definition: gm.cpp:159
virtual SkString getName() const =0
virtual DrawResult onDraw(SkCanvas *, SkString *errorMsg)
Definition: gm.cpp:139
const Paint & paint
Definition: color_source.cc:38
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct a[10]
FlutterSemanticsFlag flags
glong glong end
#define DEF_GM(CODE)
Definition: gm.h:40
constexpr GradMaker gGradMakers[]
static void draw_powerless_hue_gradients(SkCanvas *canvas, SkGradientShader::Interpolation::ColorSpace colorSpace)
Definition: gradients.cpp:1270
static void draw_many_stops(SkCanvas *canvas)
Definition: gradients.cpp:858
DEF_SIMPLE_GM_BG(gradients_color_space, canvas, 265, 255, SK_ColorGRAY)
Definition: gradients.cpp:1118
static void draw_many_hard_stops(SkCanvas *canvas)
Definition: gradients.cpp:884
static sk_sp< SkShader > make_linear(const GradRun &run, SkTileMode mode)
Definition: gradients.cpp:778
#define SIZE
Definition: gradients.cpp:776
static sk_sp< SkShader > make_radial(const GradRun &run, SkTileMode mode)
Definition: gradients.cpp:783
static sk_sp< SkShader > make_conical(const GradRun &run, SkTileMode mode)
Definition: gradients.cpp:789
#define DEF_POWERLESS_HUE_GM(colorSpace)
Definition: gradients.cpp:1394
static sk_sp< SkShader > make_sweep(const GradRun &run, SkTileMode)
Definition: gradients.cpp:796
static void draw_circle_shader(SkCanvas *canvas, SkScalar cx, SkScalar cy, SkScalar r, sk_sp< SkShader >(*shaderFunc)())
Definition: gradients.cpp:913
DEF_SIMPLE_GM(gradients_dup_color_stops, canvas, 704, 564)
Definition: gradients.cpp:807
static sk_sp< SkShader > Make2Conical(const SkPoint pts[2], const GradData &data, SkTileMode tm)
static sk_sp< SkShader > Make2Radial(const SkPoint pts[2], const GradData &data, SkTileMode tm)
constexpr SkColor4f kGreen
Definition: SkColor.h:441
constexpr SkColor4f kRed
Definition: SkColor.h:440
constexpr SkColor4f kWhite
Definition: SkColor.h:439
constexpr SkColor4f kTransparent
Definition: SkColor.h:434
constexpr SkColor4f kBlack
Definition: SkColor.h:435
constexpr SkColor4f kGray
Definition: SkColor.h:437
constexpr SkColor4f kBlue
Definition: SkColor.h:442
constexpr SkColor4f kYellow
Definition: SkColor.h:443
constexpr SkColor4f kDkGray
Definition: SkColor.h:436
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
PODArray< SkColor > colors
Definition: SkRecords.h:276
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
SK_API sk_sp< SkShader > Blend(SkBlendMode mode, sk_sp< SkShader > dst, sk_sp< SkShader > src)
void draw_checkerboard(SkCanvas *canvas, SkColor c1, SkColor c2, int size)
Definition: ToolUtils.cpp:174
SkFont DefaultPortableFont()
const DlColor kColors[]
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
font
Font Metadata and Metrics.
Definition: run.py:1
constexpr SkColor gColorClamp[]
constexpr SkScalar gPosClamp[]
const Scalar scale
constexpr int kRadius
const SkColor * fColors
const SkScalar * fPos
SkScalar fPos[4]
Definition: gradients.cpp:772
int fCount
Definition: gradients.cpp:773
SkColor fColors[4]
Definition: gradients.cpp:771
Definition: SkSize.h:16
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
void iset(int32_t x, int32_t y)
Definition: SkPoint_impl.h:214
float fX
x-axis value
Definition: SkPoint_impl.h:164
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
void set(float x, float y)
Definition: SkPoint_impl.h:200
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
constexpr size_t kHeight
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63