blob: 09de0e81c0226b6c9ae6e188cd29a3fb7f2662e9 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
reed3d9005c2015-04-23 10:30:27 -07007
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
9#include "include/effects/SkGradientShader.h"
reed@android.com42309d42009-06-22 02:06:35 +000010
11namespace skiagm {
12
13struct GradData {
brianosmane25d71c2016-09-28 11:27:28 -070014 int fCount;
15 const SkColor* fColors;
16 const SkColor4f* fColors4f;
17 const SkScalar* fPos;
reed@android.com42309d42009-06-22 02:06:35 +000018};
19
mtkleindbfd7ab2016-09-01 11:24:54 -070020constexpr SkColor gColors[] = {
reed@android.com42309d42009-06-22 02:06:35 +000021 SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
22};
brianosmane25d71c2016-09-28 11:27:28 -070023constexpr 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};
mtkleindbfd7ab2016-09-01 11:24:54 -070030constexpr SkScalar gPos0[] = { 0, SK_Scalar1 };
31constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
32constexpr SkScalar gPos2[] = {
reed@android.com42309d42009-06-22 02:06:35 +000033 0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
34};
35
mtkleindbfd7ab2016-09-01 11:24:54 -070036constexpr SkScalar gPosClamp[] = {0.0f, 0.0f, 1.0f, 1.0f};
37constexpr SkColor gColorClamp[] = {
commit-bot@chromium.org8ba1ad32013-08-07 15:22:13 +000038 SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
39};
brianosmane25d71c2016-09-28 11:27:28 -070040constexpr 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};
mtkleindbfd7ab2016-09-01 11:24:54 -070046constexpr GradData gGradData[] = {
brianosmane25d71c2016-09-28 11:27:28 -070047 { 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 }
reed@android.com42309d42009-06-22 02:06:35 +000053};
54
reed1a9b9642016-03-13 14:13:58 -070055static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -040056 SkTileMode tm, const SkMatrix& localMatrix) {
reed1a9b9642016-03-13 14:13:58 -070057 return SkGradientShader::MakeLinear(pts, data.fColors, data.fPos, data.fCount, tm, 0,
58 &localMatrix);
reed@android.com42309d42009-06-22 02:06:35 +000059}
reed@google.comf3c1cc92010-12-23 16:45:33 +000060
brianosmane25d71c2016-09-28 11:27:28 -070061static sk_sp<SkShader> MakeLinear4f(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -040062 SkTileMode tm, const SkMatrix& localMatrix) {
Mike Reed2e56f3c2019-03-06 11:21:15 -050063 auto srgb = SkColorSpace::MakeSRGB();
brianosmane25d71c2016-09-28 11:27:28 -070064 return SkGradientShader::MakeLinear(pts, data.fColors4f, srgb, data.fPos, data.fCount, tm, 0,
65 &localMatrix);
66}
67
reed1a9b9642016-03-13 14:13:58 -070068static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -040069 SkTileMode tm, const SkMatrix& localMatrix) {
reed@android.com42309d42009-06-22 02:06:35 +000070 SkPoint center;
71 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
72 SkScalarAve(pts[0].fY, pts[1].fY));
reed1a9b9642016-03-13 14:13:58 -070073 return SkGradientShader::MakeRadial(center, center.fX, data.fColors, data.fPos, data.fCount,
74 tm, 0, &localMatrix);
reed@android.com42309d42009-06-22 02:06:35 +000075}
76
brianosmane25d71c2016-09-28 11:27:28 -070077static sk_sp<SkShader> MakeRadial4f(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -040078 SkTileMode tm, const SkMatrix& localMatrix) {
brianosmane25d71c2016-09-28 11:27:28 -070079 SkPoint center;
80 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
81 SkScalarAve(pts[0].fY, pts[1].fY));
Mike Reed2e56f3c2019-03-06 11:21:15 -050082 auto srgb = SkColorSpace::MakeSRGB();
brianosmane25d71c2016-09-28 11:27:28 -070083 return SkGradientShader::MakeRadial(center, center.fX, data.fColors4f, srgb, data.fPos,
84 data.fCount, tm, 0, &localMatrix);
85}
86
reed1a9b9642016-03-13 14:13:58 -070087static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -040088 SkTileMode, const SkMatrix& localMatrix) {
reed@android.com42309d42009-06-22 02:06:35 +000089 SkPoint center;
90 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
91 SkScalarAve(pts[0].fY, pts[1].fY));
reed1a9b9642016-03-13 14:13:58 -070092 return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount,
93 0, &localMatrix);
reed@android.com42309d42009-06-22 02:06:35 +000094}
95
brianosmane25d71c2016-09-28 11:27:28 -070096static sk_sp<SkShader> MakeSweep4f(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -040097 SkTileMode, const SkMatrix& localMatrix) {
brianosmane25d71c2016-09-28 11:27:28 -070098 SkPoint center;
99 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
100 SkScalarAve(pts[0].fY, pts[1].fY));
Mike Reed2e56f3c2019-03-06 11:21:15 -0500101 auto srgb = SkColorSpace::MakeSRGB();
brianosmane25d71c2016-09-28 11:27:28 -0700102 return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors4f, srgb, data.fPos,
103 data.fCount, 0, &localMatrix);
104}
105
reed1a9b9642016-03-13 14:13:58 -0700106static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400107 SkTileMode tm, const SkMatrix& localMatrix) {
reed@google.comf3c1cc92010-12-23 16:45:33 +0000108 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));
reed1a9b9642016-03-13 14:13:58 -0700113 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);
reed@google.comf3c1cc92010-12-23 16:45:33 +0000117}
118
brianosmane25d71c2016-09-28 11:27:28 -0700119static sk_sp<SkShader> Make2Radial4f(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400120 SkTileMode tm, const SkMatrix& localMatrix) {
brianosmane25d71c2016-09-28 11:27:28 -0700121 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));
Mike Reed2e56f3c2019-03-06 11:21:15 -0500126 auto srgb = SkColorSpace::MakeSRGB();
brianosmane25d71c2016-09-28 11:27:28 -0700127 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
reed1a9b9642016-03-13 14:13:58 -0700133static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400134 SkTileMode tm, const SkMatrix& localMatrix) {
rileya@google.com5cf2c282012-07-09 14:42:16 +0000135 SkPoint center0, center1;
reed80ea19c2015-05-12 10:37:34 -0700136 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
137 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
rileya@google.com5cf2c282012-07-09 14:42:16 +0000138 center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
139 center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
reed1a9b9642016-03-13 14:13:58 -0700140 return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
141 data.fColors, data.fPos,
142 data.fCount, tm, 0, &localMatrix);
rileya@google.com5cf2c282012-07-09 14:42:16 +0000143}
144
brianosmane25d71c2016-09-28 11:27:28 -0700145static sk_sp<SkShader> Make2Conical4f(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400146 SkTileMode tm, const SkMatrix& localMatrix) {
brianosmane25d71c2016-09-28 11:27:28 -0700147 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);
Mike Reed2e56f3c2019-03-06 11:21:15 -0500152 auto srgb = SkColorSpace::MakeSRGB();
brianosmane25d71c2016-09-28 11:27:28 -0700153 return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
154 data.fColors4f, srgb, data.fPos,
155 data.fCount, tm, 0, &localMatrix);
156}
157
reed1a9b9642016-03-13 14:13:58 -0700158typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400159 SkTileMode tm, const SkMatrix& localMatrix);
mtkleindbfd7ab2016-09-01 11:24:54 -0700160constexpr GradMaker gGradMakers[] = {
rileya@google.com5cf2c282012-07-09 14:42:16 +0000161 MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
reed@android.com42309d42009-06-22 02:06:35 +0000162};
brianosmane25d71c2016-09-28 11:27:28 -0700163constexpr GradMaker gGradMakers4f[] ={
164 MakeLinear4f, MakeRadial4f, MakeSweep4f, Make2Radial4f, Make2Conical4f
165};
reed@android.com42309d42009-06-22 02:06:35 +0000166
167///////////////////////////////////////////////////////////////////////////////
168
169class GradientsGM : public GM {
170public:
fmalita063675b2015-10-12 10:41:48 -0700171 GradientsGM(bool dither) : fDither(dither) {
Mike Kleind46dce32018-08-16 10:17:03 -0400172 this->setBGColor(0xFFDDDDDD);
bsalomon@google.com48dd1a22011-10-31 14:18:20 +0000173 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000174
reed@android.com42309d42009-06-22 02:06:35 +0000175protected:
commit-bot@chromium.orga90c6802014-04-30 13:20:45 +0000176
reed@android.com42309d42009-06-22 02:06:35 +0000177 SkString onShortName() {
fmalita063675b2015-10-12 10:41:48 -0700178 return SkString(fDither ? "gradients" : "gradients_nodither");
reed@android.com42309d42009-06-22 02:06:35 +0000179 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000180
edisonn@google.com1da3a802013-09-19 17:55:49 +0000181 virtual SkISize onISize() { return SkISize::Make(840, 815); }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000182
reed@android.com42309d42009-06-22 02:06:35 +0000183 virtual void onDraw(SkCanvas* canvas) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000184
reed@android.com42309d42009-06-22 02:06:35 +0000185 SkPoint pts[2] = {
186 { 0, 0 },
Matt Saretteebe87f2017-01-26 15:14:56 -0500187 { SkIntToScalar(100), SkIntToScalar(100) }
reed@android.com42309d42009-06-22 02:06:35 +0000188 };
Mike Reedfae8fce2019-04-03 10:27:45 -0400189 SkTileMode tm = SkTileMode::kClamp;
Matt Saretteebe87f2017-01-26 15:14:56 -0500190 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
reed@android.com42309d42009-06-22 02:06:35 +0000191 SkPaint paint;
192 paint.setAntiAlias(true);
fmalita063675b2015-10-12 10:41:48 -0700193 paint.setDither(fDither);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000194
reed@android.com42309d42009-06-22 02:06:35 +0000195 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++) {
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +0000199 SkMatrix scale = SkMatrix::I();
commit-bot@chromium.org8ba1ad32013-08-07 15:22:13 +0000200
201 if (i == 5) { // if the clamp case
commit-bot@chromium.org8ba1ad32013-08-07 15:22:13 +0000202 scale.setScale(0.5f, 0.5f);
203 scale.postTranslate(25.f, 25.f);
commit-bot@chromium.org8ba1ad32013-08-07 15:22:13 +0000204 }
skia.committer@gmail.comd55e3572013-08-08 07:01:20 +0000205
reed1a9b9642016-03-13 14:13:58 -0700206 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, scale));
reed@android.com42309d42009-06-22 02:06:35 +0000207 canvas->drawRect(r, paint);
reed@android.com42309d42009-06-22 02:06:35 +0000208 canvas->translate(0, SkIntToScalar(120));
209 }
210 canvas->restore();
211 canvas->translate(SkIntToScalar(120), 0);
212 }
213 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000214
fmalita063675b2015-10-12 10:41:48 -0700215protected:
216 bool fDither;
217
reed@android.com42309d42009-06-22 02:06:35 +0000218private:
219 typedef GM INHERITED;
220};
fmalita063675b2015-10-12 10:41:48 -0700221DEF_GM( return new GradientsGM(true); )
222DEF_GM( return new GradientsGM(false); )
reed@android.com42309d42009-06-22 02:06:35 +0000223
brianosmane25d71c2016-09-28 11:27:28 -0700224// Like the original gradients GM, but using the SkColor4f shader factories. Should be identical.
225class Gradients4fGM : public GM {
226public:
227 Gradients4fGM(bool dither) : fDither(dither) {
Mike Kleind46dce32018-08-16 10:17:03 -0400228 this->setBGColor(0xFFDDDDDD);
brianosmane25d71c2016-09-28 11:27:28 -0700229 }
230
231protected:
232
233 SkString onShortName() {
234 return SkString(fDither ? "gradients4f" : "gradients4f_nodither");
235 }
236
237 virtual SkISize onISize() { return SkISize::Make(840, 815); }
238
239 virtual void onDraw(SkCanvas* canvas) {
240
241 SkPoint pts[2] ={
242 { 0, 0 },
243 { SkIntToScalar(100), SkIntToScalar(100) }
244 };
Mike Reedfae8fce2019-04-03 10:27:45 -0400245 SkTileMode tm = SkTileMode::kClamp;
brianosmane25d71c2016-09-28 11:27:28 -0700246 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
271protected:
272 bool fDither;
273
274private:
275 typedef GM INHERITED;
276};
277DEF_GM(return new Gradients4fGM(true); )
278DEF_GM(return new Gradients4fGM(false); )
279
rileya@google.com5cf2c282012-07-09 14:42:16 +0000280// Based on the original gradient slide, but with perspective applied to the
281// gradient shaders' local matrices
282class GradientsLocalPerspectiveGM : public GM {
283public:
fmalita063675b2015-10-12 10:41:48 -0700284 GradientsLocalPerspectiveGM(bool dither) : fDither(dither) {
Mike Kleind46dce32018-08-16 10:17:03 -0400285 this->setBGColor(0xFFDDDDDD);
rileya@google.com5cf2c282012-07-09 14:42:16 +0000286 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000287
rileya@google.com5cf2c282012-07-09 14:42:16 +0000288protected:
commit-bot@chromium.orga90c6802014-04-30 13:20:45 +0000289
rileya@google.com5cf2c282012-07-09 14:42:16 +0000290 SkString onShortName() {
fmalita063675b2015-10-12 10:41:48 -0700291 return SkString(fDither ? "gradients_local_perspective" :
292 "gradients_local_perspective_nodither");
rileya@google.com5cf2c282012-07-09 14:42:16 +0000293 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000294
edisonn@google.com1da3a802013-09-19 17:55:49 +0000295 virtual SkISize onISize() { return SkISize::Make(840, 815); }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000296
rileya@google.com5cf2c282012-07-09 14:42:16 +0000297 virtual void onDraw(SkCanvas* canvas) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000298
rileya@google.com5cf2c282012-07-09 14:42:16 +0000299 SkPoint pts[2] = {
300 { 0, 0 },
301 { SkIntToScalar(100), SkIntToScalar(100) }
302 };
Mike Reedfae8fce2019-04-03 10:27:45 -0400303 SkTileMode tm = SkTileMode::kClamp;
rileya@google.com5cf2c282012-07-09 14:42:16 +0000304 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
305 SkPaint paint;
306 paint.setAntiAlias(true);
fmalita063675b2015-10-12 10:41:48 -0700307 paint.setDither(fDither);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000308
rileya@google.com5cf2c282012-07-09 14:42:16 +0000309 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++) {
rileya@google.com5cf2c282012-07-09 14:42:16 +0000313 // apply an increasing y perspective as we move to the right
314 SkMatrix perspective;
315 perspective.setIdentity();
reed80ea19c2015-05-12 10:37:34 -0700316 perspective.setPerspY(SkIntToScalar(i+1) / 500);
317 perspective.setSkewX(SkIntToScalar(i+1) / 10);
commit-bot@chromium.org8ba1ad32013-08-07 15:22:13 +0000318
reed1a9b9642016-03-13 14:13:58 -0700319 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, perspective));
rileya@google.com5cf2c282012-07-09 14:42:16 +0000320 canvas->drawRect(r, paint);
rileya@google.com5cf2c282012-07-09 14:42:16 +0000321 canvas->translate(0, SkIntToScalar(120));
322 }
323 canvas->restore();
324 canvas->translate(SkIntToScalar(120), 0);
325 }
326 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000327
rileya@google.com5cf2c282012-07-09 14:42:16 +0000328private:
fmalita063675b2015-10-12 10:41:48 -0700329 bool fDither;
330
rileya@google.com5cf2c282012-07-09 14:42:16 +0000331 typedef GM INHERITED;
332};
fmalita063675b2015-10-12 10:41:48 -0700333DEF_GM( return new GradientsLocalPerspectiveGM(true); )
334DEF_GM( return new GradientsLocalPerspectiveGM(false); )
rileya@google.com5cf2c282012-07-09 14:42:16 +0000335
336// Based on the original gradient slide, but with perspective applied to
337// the view matrix
338class GradientsViewPerspectiveGM : public GradientsGM {
fmalita063675b2015-10-12 10:41:48 -0700339public:
340 GradientsViewPerspectiveGM(bool dither) : INHERITED(dither) { }
341
rileya@google.com5cf2c282012-07-09 14:42:16 +0000342protected:
343 SkString onShortName() {
fmalita063675b2015-10-12 10:41:48 -0700344 return SkString(fDither ? "gradients_view_perspective" :
345 "gradients_view_perspective_nodither");
rileya@google.com5cf2c282012-07-09 14:42:16 +0000346 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000347
edisonn@google.com1da3a802013-09-19 17:55:49 +0000348 virtual SkISize onISize() { return SkISize::Make(840, 500); }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000349
rileya@google.com5cf2c282012-07-09 14:42:16 +0000350 virtual void onDraw(SkCanvas* canvas) {
351 SkMatrix perspective;
352 perspective.setIdentity();
reed80ea19c2015-05-12 10:37:34 -0700353 perspective.setPerspY(0.001f);
354 perspective.setSkewX(SkIntToScalar(8) / 25);
scroggo@google.com837d31a2012-08-15 18:42:45 +0000355 canvas->concat(perspective);
rileya@google.com5cf2c282012-07-09 14:42:16 +0000356 INHERITED::onDraw(canvas);
357 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000358
rileya@google.com5cf2c282012-07-09 14:42:16 +0000359private:
360 typedef GradientsGM INHERITED;
361};
fmalita063675b2015-10-12 10:41:48 -0700362DEF_GM( return new GradientsViewPerspectiveGM(true); )
363DEF_GM( return new GradientsViewPerspectiveGM(false); )
rileya@google.com5cf2c282012-07-09 14:42:16 +0000364
reed@google.comac864a92011-06-27 18:11:17 +0000365/*
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);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000372
reed@google.comac864a92011-06-27 18:11:17 +0000373 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 */
381class GradientsDegenrate2PointGM : public GM {
382public:
fmalita063675b2015-10-12 10:41:48 -0700383 GradientsDegenrate2PointGM(bool dither) : fDither(dither) {}
rmistry@google.comd6176b02012-08-23 18:14:13 +0000384
reed@google.comac864a92011-06-27 18:11:17 +0000385protected:
386 SkString onShortName() {
fmalita063675b2015-10-12 10:41:48 -0700387 return SkString(fDither ? "gradients_degenerate_2pt" : "gradients_degenerate_2pt_nodither");
reed@google.comac864a92011-06-27 18:11:17 +0000388 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000389
edisonn@google.com1da3a802013-09-19 17:55:49 +0000390 virtual SkISize onISize() { return SkISize::Make(320, 320); }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000391
reed@google.comac864a92011-06-27 18:11:17 +0000392 void drawBG(SkCanvas* canvas) {
393 canvas->drawColor(SK_ColorBLUE);
394 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000395
reed@google.comac864a92011-06-27 18:11:17 +0000396 virtual void onDraw(SkCanvas* canvas) {
397 this->drawBG(canvas);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000398
reed@google.comac864a92011-06-27 18:11:17 +0000399 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000400 SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 };
epoger@google.com59f3abf2011-07-21 15:50:33 +0000401 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);
reed9283d202016-03-13 13:01:57 -0700407 SkPaint paint;
reed1a9b9642016-03-13 14:13:58 -0700408 paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors,
409 pos, SK_ARRAY_COUNT(pos),
Mike Reedfae8fce2019-04-03 10:27:45 -0400410 SkTileMode::kClamp));
fmalita063675b2015-10-12 10:41:48 -0700411 paint.setDither(fDither);
reed@google.comac864a92011-06-27 18:11:17 +0000412 canvas->drawPaint(paint);
413 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000414
reed@google.comac864a92011-06-27 18:11:17 +0000415private:
fmalita063675b2015-10-12 10:41:48 -0700416 bool fDither;
417
reed@google.comac864a92011-06-27 18:11:17 +0000418 typedef GM INHERITED;
419};
fmalita063675b2015-10-12 10:41:48 -0700420DEF_GM( return new GradientsDegenrate2PointGM(true); )
421DEF_GM( return new GradientsDegenrate2PointGM(false); )
reed@google.comac864a92011-06-27 18:11:17 +0000422
caryclarkcb100712016-02-26 05:59:40 -0800423/* bug.skia.org/517
424<canvas id="canvas"></canvas>
425<script>
426var c = document.getElementById("canvas");
427var ctx = c.getContext("2d");
428ctx.fillStyle = '#ff0';
429ctx.fillRect(0, 0, 100, 50);
430
431var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
432g.addColorStop(0, '#0f0');
433g.addColorStop(0.003, '#f00'); // 0.004 makes this work
434g.addColorStop(1, '#ff0');
435ctx.fillStyle = g;
436ctx.fillRect(0, 0, 100, 50);
437</script>
438*/
439
440// should draw only green
441DEF_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;
reed1a9b9642016-03-13 14:13:58 -0700448
caryclarkcb100712016-02-26 05:59:40 -0800449 SkPaint paint;
450 paint.setColor(SK_ColorYELLOW);
451 canvas->drawRect(SkRect::MakeWH(100, 150), paint);
reed1a9b9642016-03-13 14:13:58 -0700452 paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors, pos,
453 SK_ARRAY_COUNT(pos),
Mike Reedfae8fce2019-04-03 10:27:45 -0400454 SkTileMode::kClamp));
caryclarkcb100712016-02-26 05:59:40 -0800455 canvas->drawRect(SkRect::MakeWH(100, 150), paint);
456}
457
458
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000459/// Tests correctness of *optimized* codepaths in gradients.
460
461class ClampedGradientsGM : public GM {
462public:
fmalita063675b2015-10-12 10:41:48 -0700463 ClampedGradientsGM(bool dither) : fDither(dither) {}
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000464
465protected:
fmalita063675b2015-10-12 10:41:48 -0700466 SkString onShortName() {
467 return SkString(fDither ? "clamped_gradients" : "clamped_gradients_nodither");
468 }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000469
edisonn@google.com1da3a802013-09-19 17:55:49 +0000470 virtual SkISize onISize() { return SkISize::Make(640, 510); }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000471
472 void drawBG(SkCanvas* canvas) {
Mike Kleind46dce32018-08-16 10:17:03 -0400473 canvas->drawColor(0xFFDDDDDD);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000474 }
475
476 virtual void onDraw(SkCanvas* canvas) {
477 this->drawBG(canvas);
478
479 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
480 SkPaint paint;
fmalita063675b2015-10-12 10:41:48 -0700481 paint.setDither(fDither);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000482 paint.setAntiAlias(true);
483
484 SkPoint center;
485 center.iset(0, 300);
486 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
reed1a9b9642016-03-13 14:13:58 -0700487 paint.setShader(SkGradientShader::MakeRadial(
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000488 SkPoint(center),
halcanary96fcdcc2015-08-27 07:41:13 -0700489 SkIntToScalar(200), gColors, nullptr, 5,
Mike Reedfae8fce2019-04-03 10:27:45 -0400490 SkTileMode::kClamp));
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000491 canvas->drawRect(r, paint);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000492 }
493
494private:
fmalita063675b2015-10-12 10:41:48 -0700495 bool fDither;
496
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000497 typedef GM INHERITED;
498};
fmalita063675b2015-10-12 10:41:48 -0700499DEF_GM( return new ClampedGradientsGM(true); )
500DEF_GM( return new ClampedGradientsGM(false); )
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000501
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000502/// Checks quality of large radial gradients, which may display
503/// some banding.
504
505class RadialGradientGM : public GM {
506public:
507 RadialGradientGM() {}
508
509protected:
commit-bot@chromium.orga90c6802014-04-30 13:20:45 +0000510
mtklein36352bf2015-03-25 18:17:31 -0700511 SkString onShortName() override { return SkString("radial_gradient"); }
512 SkISize onISize() override { return SkISize::Make(1280, 1280); }
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000513 void drawBG(SkCanvas* canvas) {
514 canvas->drawColor(0xFF000000);
515 }
mtklein36352bf2015-03-25 18:17:31 -0700516 void onDraw(SkCanvas* canvas) override {
reed@google.combb0948f2012-01-31 14:44:13 +0000517 const SkISize dim = this->getISize();
518
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000519 this->drawBG(canvas);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000520
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000521 SkPaint paint;
522 paint.setDither(true);
523 SkPoint center;
reed@google.combb0948f2012-01-31 14:44:13 +0000524 center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
525 SkScalar radius = SkIntToScalar(dim.width())/2;
526 const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000527 const SkScalar pos[] = { 0.0f,
528 0.35f,
529 1.0f };
reed1a9b9642016-03-13 14:13:58 -0700530 paint.setShader(SkGradientShader::MakeRadial(center, radius, colors, pos,
531 SK_ARRAY_COUNT(pos),
Mike Reedfae8fce2019-04-03 10:27:45 -0400532 SkTileMode::kClamp));
reed@google.combb0948f2012-01-31 14:44:13 +0000533 SkRect r = {
534 0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
535 };
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000536 canvas->drawRect(r, paint);
537 }
538private:
539 typedef GM INHERITED;
540};
reed3d9005c2015-04-23 10:30:27 -0700541DEF_GM( return new RadialGradientGM; )
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000542
mtklein@google.com361a72f2013-08-19 18:43:34 +0000543class RadialGradient2GM : public GM {
544public:
fmalita063675b2015-10-12 10:41:48 -0700545 RadialGradient2GM(bool dither) : fDither(dither) {}
mtklein@google.com361a72f2013-08-19 18:43:34 +0000546
547protected:
commit-bot@chromium.orga90c6802014-04-30 13:20:45 +0000548
fmalita063675b2015-10-12 10:41:48 -0700549 SkString onShortName() override {
550 return SkString(fDither ? "radial_gradient2" : "radial_gradient2_nodither");
551 }
552
mtklein36352bf2015-03-25 18:17:31 -0700553 SkISize onISize() override { return SkISize::Make(800, 400); }
mtklein@google.com361a72f2013-08-19 18:43:34 +0000554 void drawBG(SkCanvas* canvas) {
555 canvas->drawColor(0xFF000000);
556 }
557
558 // Reproduces the example given in bug 7671058.
mtklein36352bf2015-03-25 18:17:31 -0700559 void onDraw(SkCanvas* canvas) override {
mtklein@google.com361a72f2013-08-19 18:43:34 +0000560 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
mtklein@google.com3ef7eea2013-09-16 13:02:52 +0000574 // 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 };
mtklein@google.com361a72f2013-08-19 18:43:34 +0000577
mtklein@google.com3ef7eea2013-09-16 13:02:52 +0000578 for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) {
reed1a9b9642016-03-13 14:13:58 -0700579 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),
Mike Reedfae8fce2019-04-03 10:27:45 -0400584 SkTileMode::kClamp,
reed1a9b9642016-03-13 14:13:58 -0700585 flags[i], nullptr));
586 paint3.setShader(SkGradientShader::MakeRadial(center, radius, colors2,
587 nullptr, SK_ARRAY_COUNT(colors2),
Mike Reedfae8fce2019-04-03 10:27:45 -0400588 SkTileMode::kClamp,
reed1a9b9642016-03-13 14:13:58 -0700589 flags[i], nullptr));
fmalita063675b2015-10-12 10:41:48 -0700590 paint1.setDither(fDither);
fmalita063675b2015-10-12 10:41:48 -0700591 paint2.setDither(fDither);
fmalita063675b2015-10-12 10:41:48 -0700592 paint3.setDither(fDither);
mtklein@google.com3ef7eea2013-09-16 13:02:52 +0000593
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 }
mtklein@google.com361a72f2013-08-19 18:43:34 +0000600 }
601
602private:
fmalita063675b2015-10-12 10:41:48 -0700603 bool fDither;
604
mtklein@google.com361a72f2013-08-19 18:43:34 +0000605 typedef GM INHERITED;
606};
fmalita063675b2015-10-12 10:41:48 -0700607DEF_GM( return new RadialGradient2GM(true); )
608DEF_GM( return new RadialGradient2GM(false); )
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000609
reed3d9005c2015-04-23 10:30:27 -0700610// Shallow radial (shows banding on raster)
611class RadialGradient3GM : public GM {
fmalita063675b2015-10-12 10:41:48 -0700612public:
613 RadialGradient3GM(bool dither) : fDither(dither) { }
reed@android.com42309d42009-06-22 02:06:35 +0000614
reed3d9005c2015-04-23 10:30:27 -0700615protected:
fmalita063675b2015-10-12 10:41:48 -0700616 SkString onShortName() override {
617 return SkString(fDither ? "radial_gradient3" : "radial_gradient3_nodither");
618 }
reed@android.com42309d42009-06-22 02:06:35 +0000619
reed3d9005c2015-04-23 10:30:27 -0700620 SkISize onISize() override { return SkISize::Make(500, 500); }
reed@google.comac864a92011-06-27 18:11:17 +0000621
reed3d9005c2015-04-23 10:30:27 -0700622 bool runAsBench() const override { return true; }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000623
reed3d9005c2015-04-23 10:30:27 -0700624 void onOnceBeforeDraw() override {
625 const SkPoint center = { 0, 0 };
626 const SkScalar kRadius = 3000;
627 const SkColor gColors[] = { 0xFFFFFFFF, 0xFF000000 };
reed1a9b9642016-03-13 14:13:58 -0700628 fShader = SkGradientShader::MakeRadial(center, kRadius, gColors, nullptr, 2,
Mike Reedfae8fce2019-04-03 10:27:45 -0400629 SkTileMode::kClamp);
reed3d9005c2015-04-23 10:30:27 -0700630 }
rileya@google.com5cf2c282012-07-09 14:42:16 +0000631
reed3d9005c2015-04-23 10:30:27 -0700632 void onDraw(SkCanvas* canvas) override {
633 SkPaint paint;
634 paint.setShader(fShader);
fmalita063675b2015-10-12 10:41:48 -0700635 paint.setDither(fDither);
reed3d9005c2015-04-23 10:30:27 -0700636 canvas->drawRect(SkRect::MakeWH(500, 500), paint);
637 }
halcanary9d524f22016-03-29 09:03:52 -0700638
reed3d9005c2015-04-23 10:30:27 -0700639private:
reed1a9b9642016-03-13 14:13:58 -0700640 sk_sp<SkShader> fShader;
fmalita063675b2015-10-12 10:41:48 -0700641 bool fDither;
642
reed3d9005c2015-04-23 10:30:27 -0700643 typedef GM INHERITED;
644};
fmalita063675b2015-10-12 10:41:48 -0700645DEF_GM( return new RadialGradient3GM(true); )
646DEF_GM( return new RadialGradient3GM(false); )
rileya@google.com5cf2c282012-07-09 14:42:16 +0000647
caryclark1864bfa2015-07-30 06:41:39 -0700648class RadialGradient4GM : public GM {
fmalita063675b2015-10-12 10:41:48 -0700649public:
650 RadialGradient4GM(bool dither) : fDither(dither) { }
caryclark1864bfa2015-07-30 06:41:39 -0700651
652protected:
fmalita063675b2015-10-12 10:41:48 -0700653 SkString onShortName() override {
654 return SkString(fDither ? "radial_gradient4" : "radial_gradient4_nodither");
655 }
caryclark1864bfa2015-07-30 06:41:39 -0700656
657 SkISize onISize() override { return SkISize::Make(500, 500); }
658
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 };
reed1a9b9642016-03-13 14:13:58 -0700665 fShader = SkGradientShader::MakeRadial(center, kRadius, colors, pos,
Mike Reedfae8fce2019-04-03 10:27:45 -0400666 SK_ARRAY_COUNT(gColors), SkTileMode::kClamp);
caryclark1864bfa2015-07-30 06:41:39 -0700667 }
668
669 void onDraw(SkCanvas* canvas) override {
670 SkPaint paint;
671 paint.setAntiAlias(true);
fmalita063675b2015-10-12 10:41:48 -0700672 paint.setDither(fDither);
caryclark1864bfa2015-07-30 06:41:39 -0700673 paint.setShader(fShader);
674 canvas->drawRect(SkRect::MakeWH(500, 500), paint);
675 }
halcanary9d524f22016-03-29 09:03:52 -0700676
caryclark1864bfa2015-07-30 06:41:39 -0700677private:
reed1a9b9642016-03-13 14:13:58 -0700678 sk_sp<SkShader> fShader;
fmalita063675b2015-10-12 10:41:48 -0700679 bool fDither;
680
caryclark1864bfa2015-07-30 06:41:39 -0700681 typedef GM INHERITED;
682};
fmalita063675b2015-10-12 10:41:48 -0700683DEF_GM( return new RadialGradient4GM(true); )
684DEF_GM( return new RadialGradient4GM(false); )
caryclark1864bfa2015-07-30 06:41:39 -0700685
caryclark159fa572015-07-30 12:35:48 -0700686class LinearGradientGM : public GM {
fmalita063675b2015-10-12 10:41:48 -0700687public:
688 LinearGradientGM(bool dither) : fDither(dither) { }
caryclark159fa572015-07-30 12:35:48 -0700689
690protected:
fmalita063675b2015-10-12 10:41:48 -0700691 SkString onShortName() override {
692 return SkString(fDither ? "linear_gradient" : "linear_gradient_nodither");
693 }
694
caryclark159fa572015-07-30 12:35:48 -0700695 const SkScalar kWidthBump = 30.f;
696 const SkScalar kHeight = 5.f;
697 const SkScalar kMinWidth = 540.f;
698
699 SkISize onISize() override { return SkISize::Make(500, 500); }
700
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 }
reed1a9b9642016-03-13 14:13:58 -0700713 fShader[index] = SkGradientShader::MakeLinear(pts, colors, pos,
Mike Reedfae8fce2019-04-03 10:27:45 -0400714 SK_ARRAY_COUNT(gColors), SkTileMode::kClamp);
caryclark159fa572015-07-30 12:35:48 -0700715 }
716 }
717
718 void onDraw(SkCanvas* canvas) override {
719 SkPaint paint;
720 paint.setAntiAlias(true);
fmalita063675b2015-10-12 10:41:48 -0700721 paint.setDither(fDither);
caryclark159fa572015-07-30 12:35:48 -0700722 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 }
halcanary9d524f22016-03-29 09:03:52 -0700728
caryclark159fa572015-07-30 12:35:48 -0700729private:
reed1a9b9642016-03-13 14:13:58 -0700730 sk_sp<SkShader> fShader[100];
fmalita063675b2015-10-12 10:41:48 -0700731 bool fDither;
732
caryclark159fa572015-07-30 12:35:48 -0700733 typedef GM INHERITED;
734};
fmalita063675b2015-10-12 10:41:48 -0700735DEF_GM( return new LinearGradientGM(true); )
736DEF_GM( return new LinearGradientGM(false); )
caryclark159fa572015-07-30 12:35:48 -0700737
fmalita8b78bd62015-11-20 13:58:24 -0800738class LinearGradientTinyGM : public GM {
fmalitabc590c02016-02-22 09:12:33 -0800739public:
740 LinearGradientTinyGM(uint32_t flags, const char* suffix = nullptr)
741 : fName("linear_gradient_tiny")
742 , fFlags(flags) {
743 fName.append(suffix);
744 }
745
fmalita8b78bd62015-11-20 13:58:24 -0800746protected:
747 SkString onShortName() override {
fmalitabc590c02016-02-22 09:12:33 -0800748 return fName;
fmalita8b78bd62015-11-20 13:58:24 -0800749 }
750
751 SkISize onISize() override {
752 return SkISize::Make(600, 500);
753 }
754
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[] = {
reedde3aac82015-11-22 13:00:04 -0800763 { { 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 }},
fmalita8b78bd62015-11-20 13:58:24 -0800767
reedde3aac82015-11-22 13:00:04 -0800768 { { 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 }},
fmalita8b78bd62015-11-20 13:58:24 -0800777 };
778
779 SkPaint paint;
780 for (unsigned i = 0; i < SK_ARRAY_COUNT(configs); ++i) {
781 SkAutoCanvasRestore acr(canvas, true);
reed1a9b9642016-03-13 14:13:58 -0700782 paint.setShader(SkGradientShader::MakeLinear(configs[i].pts, colors, configs[i].pos,
Mike Reedfae8fce2019-04-03 10:27:45 -0400783 kStopCount, SkTileMode::kClamp,
reed1a9b9642016-03-13 14:13:58 -0700784 fFlags, nullptr));
fmalita8b78bd62015-11-20 13:58:24 -0800785 canvas->translate(kRectSize * ((i % 4) * 1.5f + 0.25f),
786 kRectSize * ((i / 4) * 1.5f + 0.25f));
787
fmalita8b78bd62015-11-20 13:58:24 -0800788 canvas->drawRect(SkRect::MakeWH(kRectSize, kRectSize), paint);
789 }
790 }
791
792private:
793 typedef GM INHERITED;
fmalita8b78bd62015-11-20 13:58:24 -0800794
fmalitabc590c02016-02-22 09:12:33 -0800795 SkString fName;
796 uint32_t fFlags;
797};
798DEF_GM( return new LinearGradientTinyGM(0); )
reed@android.com42309d42009-06-22 02:06:35 +0000799}
reedd4eaa252016-01-22 10:35:26 -0800800
801///////////////////////////////////////////////////////////////////////////////////////////////////
802
803struct GradRun {
804 SkColor fColors[4];
805 SkScalar fPos[4];
806 int fCount;
807};
808
809#define SIZE 121
810
Mike Reedfae8fce2019-04-03 10:27:45 -0400811static sk_sp<SkShader> make_linear(const GradRun& run, SkTileMode mode) {
reedd4eaa252016-01-22 10:35:26 -0800812 const SkPoint pts[] { { 30, 30 }, { SIZE - 30, SIZE - 30 } };
reed1a9b9642016-03-13 14:13:58 -0700813 return SkGradientShader::MakeLinear(pts, run.fColors, run.fPos, run.fCount, mode);
reedd4eaa252016-01-22 10:35:26 -0800814}
815
Mike Reedfae8fce2019-04-03 10:27:45 -0400816static sk_sp<SkShader> make_radial(const GradRun& run, SkTileMode mode) {
reedd4eaa252016-01-22 10:35:26 -0800817 const SkScalar half = SIZE * 0.5f;
reed1a9b9642016-03-13 14:13:58 -0700818 return SkGradientShader::MakeRadial({half,half}, half - 10, run.fColors, run.fPos,
819 run.fCount, mode);
reedd4eaa252016-01-22 10:35:26 -0800820}
821
Mike Reedfae8fce2019-04-03 10:27:45 -0400822static sk_sp<SkShader> make_conical(const GradRun& run, SkTileMode mode) {
reedd4eaa252016-01-22 10:35:26 -0800823 const SkScalar half = SIZE * 0.5f;
824 const SkPoint center { half, half };
reed1a9b9642016-03-13 14:13:58 -0700825 return SkGradientShader::MakeTwoPointConical(center, 20, center, half - 10,
826 run.fColors, run.fPos, run.fCount, mode);
reedd4eaa252016-01-22 10:35:26 -0800827}
828
Mike Reedfae8fce2019-04-03 10:27:45 -0400829static sk_sp<SkShader> make_sweep(const GradRun& run, SkTileMode) {
reedd4eaa252016-01-22 10:35:26 -0800830 const SkScalar half = SIZE * 0.5f;
reed1a9b9642016-03-13 14:13:58 -0700831 return SkGradientShader::MakeSweep(half, half, run.fColors, run.fPos, run.fCount);
reedd4eaa252016-01-22 10:35:26 -0800832}
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 */
840DEF_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 };
Mike Reedfae8fce2019-04-03 10:27:45 -0400869 sk_sp<SkShader> (*factories[])(const GradRun&, SkTileMode) {
reedd4eaa252016-01-22 10:35:26 -0800870 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;
Mike Reedfae8fce2019-04-03 10:27:45 -0400876 const SkTileMode mode = SkTileMode::kClamp;
reedd4eaa252016-01-22 10:35:26 -0800877
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) {
reed1a9b9642016-03-13 14:13:58 -0700884 paint.setShader(factory(run, mode));
reedd4eaa252016-01-22 10:35:26 -0800885 canvas->drawRect(rect, paint);
886 canvas->translate(dx, 0);
887 }
888 }
889}
fmalitabc590c02016-02-22 09:12:33 -0800890
Florin Malitad1aedde2017-06-07 15:03:38 -0400891static void draw_many_stops(SkCanvas* canvas) {
fmalitabc590c02016-02-22 09:12:33 -0800892 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
reed9283d202016-03-13 13:01:57 -0700906 SkPaint p;
reed1a9b9642016-03-13 14:13:58 -0700907 p.setShader(SkGradientShader::MakeLinear(
Mike Reedfae8fce2019-04-03 10:27:45 -0400908 pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkTileMode::kClamp));
reed9283d202016-03-13 13:01:57 -0700909
fmalitabc590c02016-02-22 09:12:33 -0800910 canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p);
911}
912
913DEF_SIMPLE_GM(gradient_many_stops, canvas, 500, 500) {
Florin Malitad1aedde2017-06-07 15:03:38 -0400914 draw_many_stops(canvas);
fmalitabc590c02016-02-22 09:12:33 -0800915}
916
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500917#include "include/core/SkPictureRecorder.h"
Florin Malitaa0ac9632017-05-03 13:07:28 -0400918
919static 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
933DEF_SIMPLE_GM(fancy_gradients, canvas, 800, 300) {
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),
Mike Reedfae8fce2019-04-03 10:27:45 -0400954 SkTileMode::kClamp, 0, nullptr));
Florin Malitaa0ac9632017-05-03 13:07:28 -0400955 recorder.getRecordingCanvas()->drawPaint(p);
956
957 SkPoint pts2[] = { { 0, kTileSize }, { kTileSize, 0 }};
958 p.setShader(SkGradientShader::MakeLinear(pts2, colors2, pos, SK_ARRAY_COUNT(colors2),
Mike Reedfae8fce2019-04-03 10:27:45 -0400959 SkTileMode::kClamp, 0, nullptr));
Florin Malitaa0ac9632017-05-03 13:07:28 -0400960 recorder.getRecordingCanvas()->drawPaint(p);
961
962 SkMatrix m = SkMatrix::I();
963 m.preRotate(45);
Mike Reedfae8fce2019-04-03 10:27:45 -0400964 return recorder.finishRecordingAsPicture()->makeShader(
965 SkTileMode::kRepeat,
966 SkTileMode::kRepeat, &m, nullptr);
Florin Malitaa0ac9632017-05-03 13:07:28 -0400967 });
968
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
Mike Reedfae8fce2019-04-03 10:27:45 -0400985 return recorder.finishRecordingAsPicture()->makeShader(
986 SkTileMode::kRepeat,
987 SkTileMode::kRepeat);
Florin Malitaa0ac9632017-05-03 13:07:28 -0400988 });
989
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
Mike Reedc8bea7d2019-04-09 13:55:36 -04001007 sk_sp<SkShader> sweep(SkShaders::Blend(SkBlendMode::kExclusion, sweep1, sweep2));
Florin Malitaa0ac9632017-05-03 13:07:28 -04001008
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
Mike Reedc8bea7d2019-04-09 13:55:36 -04001014 return SkShaders::Blend(SkBlendMode::kExclusion, sweep,
1015 SkGradientShader::MakeRadial(center, 100, colors,
1016 radialPos,
1017 SK_ARRAY_COUNT(radialPos),
1018 SkTileMode::kClamp));
Florin Malitaa0ac9632017-05-03 13:07:28 -04001019 });
1020}
Florin Malita5a9a9812017-08-01 16:38:08 -04001021
Florin Malita4d274292017-08-02 10:04:48 -04001022DEF_SIMPLE_GM(sweep_tiling, canvas, 690, 512) {
Florin Malita5a9a9812017-08-01 16:38:08 -04001023 static constexpr SkScalar size = 160;
1024 static constexpr SkColor colors[] = { SK_ColorBLUE, SK_ColorYELLOW, SK_ColorGREEN };
1025 static constexpr SkScalar pos[] = { 0, .25f, .50f };
1026 static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "size mismatch");
1027
Mike Reedfae8fce2019-04-03 10:27:45 -04001028 static constexpr SkTileMode modes[] = { SkTileMode::kClamp,
1029 SkTileMode::kRepeat,
1030 SkTileMode::kMirror };
Florin Malita5a9a9812017-08-01 16:38:08 -04001031
1032 static const struct {
1033 SkScalar start, end;
1034 } angles[] = {
1035 { -330, -270 },
1036 { 30, 90 },
1037 { 390, 450 },
Florin Malita50f7a1e2017-08-02 09:40:26 -04001038 { -30, 800 },
Florin Malita5a9a9812017-08-01 16:38:08 -04001039 };
1040
1041 SkPaint p;
1042 const SkRect r = SkRect::MakeWH(size, size);
1043
1044 for (auto mode : modes) {
1045 {
1046 SkAutoCanvasRestore acr(canvas, true);
1047
1048 for (auto angle : angles) {
1049 p.setShader(SkGradientShader::MakeSweep(size / 2, size / 2, colors, pos,
1050 SK_ARRAY_COUNT(colors), mode,
1051 angle.start, angle.end, 0, nullptr));
1052
1053 canvas->drawRect(r, p);
1054 canvas->translate(size * 1.1f, 0);
1055 }
1056 }
1057 canvas->translate(0, size * 1.1f);
1058 }
1059}
Florin Malita36f054a2017-08-03 12:55:41 -04001060
1061// Exercises the special-case Ganesh gradient effects.
Brian Osmana8e57442017-09-11 17:21:35 -04001062DEF_SIMPLE_GM(gradients_interesting, canvas, 640, 1300) {
Florin Malita36f054a2017-08-03 12:55:41 -04001063 static const SkColor colors2[] = { SK_ColorRED, SK_ColorBLUE };
1064 static const SkColor colors3[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorBLUE };
1065 static const SkColor colors4[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorYELLOW, SK_ColorBLUE };
1066
Brian Osmana8e57442017-09-11 17:21:35 -04001067 static const SkScalar softRight[] = { 0, .999f, 1 }; // Based on Android launcher "clipping"
1068 static const SkScalar hardLeft[] = { 0, 0, 1 };
1069 static const SkScalar hardRight[] = { 0, 1, 1 };
1070 static const SkScalar hardCenter[] = { 0, .5f, .5f, 1 };
Florin Malita36f054a2017-08-03 12:55:41 -04001071
1072 static const struct {
1073 const SkColor* colors;
1074 const SkScalar* pos;
1075 int count;
1076 } configs[] = {
1077 { colors2, nullptr, 2 }, // kTwo_ColorType
Brian Osman2ab4b2b2017-09-12 11:20:56 -04001078 { colors3, nullptr, 3 }, // kThree_ColorType (simple)
1079 { colors3, softRight, 3 }, // kThree_ColorType (tricky)
Florin Malita36f054a2017-08-03 12:55:41 -04001080 { colors3, hardLeft, 3 }, // kHardStopLeftEdged_ColorType
1081 { colors3, hardRight, 3 }, // kHardStopRightEdged_ColorType
1082 { colors4, hardCenter, 4 }, // kSingleHardStop_ColorType
1083 };
1084
Mike Reedfae8fce2019-04-03 10:27:45 -04001085 static const SkTileMode modes[] = {
1086 SkTileMode::kClamp,
1087 SkTileMode::kRepeat,
1088 SkTileMode::kMirror,
Florin Malita36f054a2017-08-03 12:55:41 -04001089 };
1090
1091 static constexpr SkScalar size = 200;
1092 static const SkPoint pts[] = { { size / 3, size / 3 }, { size * 2 / 3, size * 2 / 3} };
1093
1094 SkPaint p;
1095 for (const auto& cfg : configs) {
1096 {
1097 SkAutoCanvasRestore acr(canvas, true);
1098 for (auto mode : modes) {
1099 p.setShader(SkGradientShader::MakeLinear(pts, cfg.colors, cfg.pos, cfg.count,
1100 mode));
1101 canvas->drawRect(SkRect::MakeWH(size, size), p);
1102 canvas->translate(size * 1.1f, 0);
1103 }
1104 }
1105 canvas->translate(0, size * 1.1f);
1106 }
1107}