blob: 34cfec41fc00f6e4880c21f54295014347a20714 [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"
Ben Wagner7fde8e12019-05-01 17:28:53 -04009#include "include/core/SkBlendMode.h"
10#include "include/core/SkCanvas.h"
11#include "include/core/SkColor.h"
12#include "include/core/SkColorSpace.h"
13#include "include/core/SkMatrix.h"
14#include "include/core/SkPaint.h"
15#include "include/core/SkPicture.h"
16#include "include/core/SkPictureRecorder.h"
17#include "include/core/SkPoint.h"
18#include "include/core/SkRect.h"
19#include "include/core/SkRefCnt.h"
20#include "include/core/SkScalar.h"
21#include "include/core/SkShader.h"
22#include "include/core/SkSize.h"
23#include "include/core/SkString.h"
24#include "include/core/SkTileMode.h"
25#include "include/core/SkTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "include/effects/SkGradientShader.h"
reed@android.com42309d42009-06-22 02:06:35 +000027
Ben Wagner7fde8e12019-05-01 17:28:53 -040028#include <math.h>
29
Hal Canaryfa3305a2019-07-18 12:36:54 -040030namespace {
reed@android.com42309d42009-06-22 02:06:35 +000031
32struct GradData {
brianosmane25d71c2016-09-28 11:27:28 -070033 int fCount;
34 const SkColor* fColors;
35 const SkColor4f* fColors4f;
36 const SkScalar* fPos;
reed@android.com42309d42009-06-22 02:06:35 +000037};
38
mtkleindbfd7ab2016-09-01 11:24:54 -070039constexpr SkColor gColors[] = {
reed@android.com42309d42009-06-22 02:06:35 +000040 SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
41};
brianosmane25d71c2016-09-28 11:27:28 -070042constexpr SkColor4f gColors4f[] ={
43 { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
44 { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
45 { 0.0f, 0.0f, 1.0f, 1.0f }, // Blue
46 { 1.0f, 1.0f, 1.0f, 1.0f }, // White
47 { 0.0f, 0.0f, 0.0f, 1.0f } // Black
48};
mtkleindbfd7ab2016-09-01 11:24:54 -070049constexpr SkScalar gPos0[] = { 0, SK_Scalar1 };
50constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
51constexpr SkScalar gPos2[] = {
reed@android.com42309d42009-06-22 02:06:35 +000052 0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
53};
54
mtkleindbfd7ab2016-09-01 11:24:54 -070055constexpr SkScalar gPosClamp[] = {0.0f, 0.0f, 1.0f, 1.0f};
56constexpr SkColor gColorClamp[] = {
commit-bot@chromium.org8ba1ad32013-08-07 15:22:13 +000057 SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
58};
brianosmane25d71c2016-09-28 11:27:28 -070059constexpr SkColor4f gColor4fClamp[] ={
60 { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
61 { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
62 { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
63 { 0.0f, 0.0f, 1.0f, 1.0f } // Blue
64};
mtkleindbfd7ab2016-09-01 11:24:54 -070065constexpr GradData gGradData[] = {
brianosmane25d71c2016-09-28 11:27:28 -070066 { 2, gColors, gColors4f, nullptr },
67 { 2, gColors, gColors4f, gPos0 },
68 { 2, gColors, gColors4f, gPos1 },
69 { 5, gColors, gColors4f, nullptr },
70 { 5, gColors, gColors4f, gPos2 },
71 { 4, gColorClamp, gColor4fClamp, gPosClamp }
reed@android.com42309d42009-06-22 02:06:35 +000072};
73
reed1a9b9642016-03-13 14:13:58 -070074static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -040075 SkTileMode tm, const SkMatrix& localMatrix) {
reed1a9b9642016-03-13 14:13:58 -070076 return SkGradientShader::MakeLinear(pts, data.fColors, data.fPos, data.fCount, tm, 0,
77 &localMatrix);
reed@android.com42309d42009-06-22 02:06:35 +000078}
reed@google.comf3c1cc92010-12-23 16:45:33 +000079
brianosmane25d71c2016-09-28 11:27:28 -070080static sk_sp<SkShader> MakeLinear4f(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -040081 SkTileMode tm, const SkMatrix& localMatrix) {
Mike Reed2e56f3c2019-03-06 11:21:15 -050082 auto srgb = SkColorSpace::MakeSRGB();
brianosmane25d71c2016-09-28 11:27:28 -070083 return SkGradientShader::MakeLinear(pts, data.fColors4f, srgb, data.fPos, data.fCount, tm, 0,
84 &localMatrix);
85}
86
reed1a9b9642016-03-13 14:13:58 -070087static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -040088 SkTileMode tm, 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::MakeRadial(center, center.fX, data.fColors, data.fPos, data.fCount,
93 tm, 0, &localMatrix);
reed@android.com42309d42009-06-22 02:06:35 +000094}
95
brianosmane25d71c2016-09-28 11:27:28 -070096static sk_sp<SkShader> MakeRadial4f(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -040097 SkTileMode tm, 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::MakeRadial(center, center.fX, data.fColors4f, srgb, data.fPos,
103 data.fCount, tm, 0, &localMatrix);
104}
105
reed1a9b9642016-03-13 14:13:58 -0700106static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400107 SkTileMode, const SkMatrix& localMatrix) {
reed@android.com42309d42009-06-22 02:06:35 +0000108 SkPoint center;
109 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
110 SkScalarAve(pts[0].fY, pts[1].fY));
reed1a9b9642016-03-13 14:13:58 -0700111 return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount,
112 0, &localMatrix);
reed@android.com42309d42009-06-22 02:06:35 +0000113}
114
brianosmane25d71c2016-09-28 11:27:28 -0700115static sk_sp<SkShader> MakeSweep4f(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400116 SkTileMode, const SkMatrix& localMatrix) {
brianosmane25d71c2016-09-28 11:27:28 -0700117 SkPoint center;
118 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
119 SkScalarAve(pts[0].fY, pts[1].fY));
Mike Reed2e56f3c2019-03-06 11:21:15 -0500120 auto srgb = SkColorSpace::MakeSRGB();
brianosmane25d71c2016-09-28 11:27:28 -0700121 return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors4f, srgb, data.fPos,
122 data.fCount, 0, &localMatrix);
123}
124
reed1a9b9642016-03-13 14:13:58 -0700125static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400126 SkTileMode tm, const SkMatrix& localMatrix) {
reed@google.comf3c1cc92010-12-23 16:45:33 +0000127 SkPoint center0, center1;
128 center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
129 SkScalarAve(pts[0].fY, pts[1].fY));
130 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
131 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
reed1a9b9642016-03-13 14:13:58 -0700132 return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
133 center0, (pts[1].fX - pts[0].fX) / 2,
134 data.fColors, data.fPos, data.fCount, tm,
135 0, &localMatrix);
reed@google.comf3c1cc92010-12-23 16:45:33 +0000136}
137
brianosmane25d71c2016-09-28 11:27:28 -0700138static sk_sp<SkShader> Make2Radial4f(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400139 SkTileMode tm, const SkMatrix& localMatrix) {
brianosmane25d71c2016-09-28 11:27:28 -0700140 SkPoint center0, center1;
141 center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
142 SkScalarAve(pts[0].fY, pts[1].fY));
143 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3) / 5),
144 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1) / 4));
Mike Reed2e56f3c2019-03-06 11:21:15 -0500145 auto srgb = SkColorSpace::MakeSRGB();
brianosmane25d71c2016-09-28 11:27:28 -0700146 return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
147 center0, (pts[1].fX - pts[0].fX) / 2,
148 data.fColors4f, srgb, data.fPos, data.fCount, tm,
149 0, &localMatrix);
150}
151
reed1a9b9642016-03-13 14:13:58 -0700152static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400153 SkTileMode tm, const SkMatrix& localMatrix) {
rileya@google.com5cf2c282012-07-09 14:42:16 +0000154 SkPoint center0, center1;
reed80ea19c2015-05-12 10:37:34 -0700155 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
156 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
rileya@google.com5cf2c282012-07-09 14:42:16 +0000157 center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
158 center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
reed1a9b9642016-03-13 14:13:58 -0700159 return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
160 data.fColors, data.fPos,
161 data.fCount, tm, 0, &localMatrix);
rileya@google.com5cf2c282012-07-09 14:42:16 +0000162}
163
brianosmane25d71c2016-09-28 11:27:28 -0700164static sk_sp<SkShader> Make2Conical4f(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400165 SkTileMode tm, const SkMatrix& localMatrix) {
brianosmane25d71c2016-09-28 11:27:28 -0700166 SkPoint center0, center1;
167 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
168 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
169 center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
170 center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
Mike Reed2e56f3c2019-03-06 11:21:15 -0500171 auto srgb = SkColorSpace::MakeSRGB();
brianosmane25d71c2016-09-28 11:27:28 -0700172 return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
173 data.fColors4f, srgb, data.fPos,
174 data.fCount, tm, 0, &localMatrix);
175}
176
reed1a9b9642016-03-13 14:13:58 -0700177typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data,
Mike Reedfae8fce2019-04-03 10:27:45 -0400178 SkTileMode tm, const SkMatrix& localMatrix);
mtkleindbfd7ab2016-09-01 11:24:54 -0700179constexpr GradMaker gGradMakers[] = {
rileya@google.com5cf2c282012-07-09 14:42:16 +0000180 MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
reed@android.com42309d42009-06-22 02:06:35 +0000181};
brianosmane25d71c2016-09-28 11:27:28 -0700182constexpr GradMaker gGradMakers4f[] ={
183 MakeLinear4f, MakeRadial4f, MakeSweep4f, Make2Radial4f, Make2Conical4f
184};
reed@android.com42309d42009-06-22 02:06:35 +0000185
186///////////////////////////////////////////////////////////////////////////////
187
Hal Canaryfa3305a2019-07-18 12:36:54 -0400188class GradientsGM : public skiagm::GM {
reed@android.com42309d42009-06-22 02:06:35 +0000189public:
Hal Canaryfa3305a2019-07-18 12:36:54 -0400190 GradientsGM(bool dither) : fDither(dither) {}
rmistry@google.comd6176b02012-08-23 18:14:13 +0000191
reed@android.com42309d42009-06-22 02:06:35 +0000192protected:
Hal Canaryfa3305a2019-07-18 12:36:54 -0400193 const bool fDither;
commit-bot@chromium.orga90c6802014-04-30 13:20:45 +0000194
Hal Canaryfa3305a2019-07-18 12:36:54 -0400195 void onDraw(SkCanvas* canvas) override {
reed@android.com42309d42009-06-22 02:06:35 +0000196 SkPoint pts[2] = {
197 { 0, 0 },
Matt Saretteebe87f2017-01-26 15:14:56 -0500198 { SkIntToScalar(100), SkIntToScalar(100) }
reed@android.com42309d42009-06-22 02:06:35 +0000199 };
Mike Reedfae8fce2019-04-03 10:27:45 -0400200 SkTileMode tm = SkTileMode::kClamp;
Matt Saretteebe87f2017-01-26 15:14:56 -0500201 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
reed@android.com42309d42009-06-22 02:06:35 +0000202 SkPaint paint;
203 paint.setAntiAlias(true);
fmalita063675b2015-10-12 10:41:48 -0700204 paint.setDither(fDither);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000205
reed@android.com42309d42009-06-22 02:06:35 +0000206 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
207 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
208 canvas->save();
209 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +0000210 SkMatrix scale = SkMatrix::I();
commit-bot@chromium.org8ba1ad32013-08-07 15:22:13 +0000211
212 if (i == 5) { // if the clamp case
commit-bot@chromium.org8ba1ad32013-08-07 15:22:13 +0000213 scale.setScale(0.5f, 0.5f);
214 scale.postTranslate(25.f, 25.f);
commit-bot@chromium.org8ba1ad32013-08-07 15:22:13 +0000215 }
skia.committer@gmail.comd55e3572013-08-08 07:01:20 +0000216
reed1a9b9642016-03-13 14:13:58 -0700217 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, scale));
reed@android.com42309d42009-06-22 02:06:35 +0000218 canvas->drawRect(r, paint);
reed@android.com42309d42009-06-22 02:06:35 +0000219 canvas->translate(0, SkIntToScalar(120));
220 }
221 canvas->restore();
222 canvas->translate(SkIntToScalar(120), 0);
223 }
224 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000225
reed@android.com42309d42009-06-22 02:06:35 +0000226private:
Hal Canaryfa3305a2019-07-18 12:36:54 -0400227 void onOnceBeforeDraw() override { this->setBGColor(0xFFDDDDDD); }
228
229 SkString onShortName() override {
230 return SkString(fDither ? "gradients" : "gradients_nodither");
231 }
232
233 SkISize onISize() override { return {840, 815}; }
reed@android.com42309d42009-06-22 02:06:35 +0000234};
fmalita063675b2015-10-12 10:41:48 -0700235DEF_GM( return new GradientsGM(true); )
236DEF_GM( return new GradientsGM(false); )
reed@android.com42309d42009-06-22 02:06:35 +0000237
brianosmane25d71c2016-09-28 11:27:28 -0700238// Like the original gradients GM, but using the SkColor4f shader factories. Should be identical.
Hal Canaryfa3305a2019-07-18 12:36:54 -0400239class Gradients4fGM : public skiagm::GM {
brianosmane25d71c2016-09-28 11:27:28 -0700240public:
Hal Canaryfa3305a2019-07-18 12:36:54 -0400241 Gradients4fGM(bool dither) : fDither(dither) {}
brianosmane25d71c2016-09-28 11:27:28 -0700242
Hal Canaryfa3305a2019-07-18 12:36:54 -0400243private:
244 void onOnceBeforeDraw() override { this->setBGColor(0xFFDDDDDD); }
brianosmane25d71c2016-09-28 11:27:28 -0700245
Hal Canaryfa3305a2019-07-18 12:36:54 -0400246 SkString onShortName() override {
brianosmane25d71c2016-09-28 11:27:28 -0700247 return SkString(fDither ? "gradients4f" : "gradients4f_nodither");
248 }
249
Hal Canaryfa3305a2019-07-18 12:36:54 -0400250 SkISize onISize() override { return {840, 815}; }
brianosmane25d71c2016-09-28 11:27:28 -0700251
Hal Canaryfa3305a2019-07-18 12:36:54 -0400252 void onDraw(SkCanvas* canvas) override {
brianosmane25d71c2016-09-28 11:27:28 -0700253 SkPoint pts[2] ={
254 { 0, 0 },
255 { SkIntToScalar(100), SkIntToScalar(100) }
256 };
Mike Reedfae8fce2019-04-03 10:27:45 -0400257 SkTileMode tm = SkTileMode::kClamp;
brianosmane25d71c2016-09-28 11:27:28 -0700258 SkRect r ={ 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
259 SkPaint paint;
260 paint.setAntiAlias(true);
261 paint.setDither(fDither);
262
263 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
264 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
265 canvas->save();
266 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers4f); j++) {
267 SkMatrix scale = SkMatrix::I();
268
269 if (i == 5) { // if the clamp case
270 scale.setScale(0.5f, 0.5f);
271 scale.postTranslate(25.f, 25.f);
272 }
273
274 paint.setShader(gGradMakers4f[j](pts, gGradData[i], tm, scale));
275 canvas->drawRect(r, paint);
276 canvas->translate(0, SkIntToScalar(120));
277 }
278 canvas->restore();
279 canvas->translate(SkIntToScalar(120), 0);
280 }
281 }
282
brianosmane25d71c2016-09-28 11:27:28 -0700283 bool fDither;
brianosmane25d71c2016-09-28 11:27:28 -0700284};
285DEF_GM(return new Gradients4fGM(true); )
286DEF_GM(return new Gradients4fGM(false); )
287
rileya@google.com5cf2c282012-07-09 14:42:16 +0000288// Based on the original gradient slide, but with perspective applied to the
289// gradient shaders' local matrices
Hal Canaryfa3305a2019-07-18 12:36:54 -0400290class GradientsLocalPerspectiveGM : public skiagm::GM {
rileya@google.com5cf2c282012-07-09 14:42:16 +0000291public:
fmalita063675b2015-10-12 10:41:48 -0700292 GradientsLocalPerspectiveGM(bool dither) : fDither(dither) {
Mike Kleind46dce32018-08-16 10:17:03 -0400293 this->setBGColor(0xFFDDDDDD);
rileya@google.com5cf2c282012-07-09 14:42:16 +0000294 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000295
Hal Canaryfa3305a2019-07-18 12:36:54 -0400296private:
297 SkString onShortName() override {
fmalita063675b2015-10-12 10:41:48 -0700298 return SkString(fDither ? "gradients_local_perspective" :
299 "gradients_local_perspective_nodither");
rileya@google.com5cf2c282012-07-09 14:42:16 +0000300 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000301
Hal Canaryfa3305a2019-07-18 12:36:54 -0400302 SkISize onISize() override { return {840, 815}; }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000303
Hal Canaryfa3305a2019-07-18 12:36:54 -0400304 void onDraw(SkCanvas* canvas) override {
rileya@google.com5cf2c282012-07-09 14:42:16 +0000305 SkPoint pts[2] = {
306 { 0, 0 },
307 { SkIntToScalar(100), SkIntToScalar(100) }
308 };
Mike Reedfae8fce2019-04-03 10:27:45 -0400309 SkTileMode tm = SkTileMode::kClamp;
rileya@google.com5cf2c282012-07-09 14:42:16 +0000310 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
311 SkPaint paint;
312 paint.setAntiAlias(true);
fmalita063675b2015-10-12 10:41:48 -0700313 paint.setDither(fDither);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000314
rileya@google.com5cf2c282012-07-09 14:42:16 +0000315 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
316 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
317 canvas->save();
318 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
rileya@google.com5cf2c282012-07-09 14:42:16 +0000319 // apply an increasing y perspective as we move to the right
320 SkMatrix perspective;
321 perspective.setIdentity();
reed80ea19c2015-05-12 10:37:34 -0700322 perspective.setPerspY(SkIntToScalar(i+1) / 500);
323 perspective.setSkewX(SkIntToScalar(i+1) / 10);
commit-bot@chromium.org8ba1ad32013-08-07 15:22:13 +0000324
reed1a9b9642016-03-13 14:13:58 -0700325 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, perspective));
rileya@google.com5cf2c282012-07-09 14:42:16 +0000326 canvas->drawRect(r, paint);
rileya@google.com5cf2c282012-07-09 14:42:16 +0000327 canvas->translate(0, SkIntToScalar(120));
328 }
329 canvas->restore();
330 canvas->translate(SkIntToScalar(120), 0);
331 }
332 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000333
fmalita063675b2015-10-12 10:41:48 -0700334 bool fDither;
rileya@google.com5cf2c282012-07-09 14:42:16 +0000335};
fmalita063675b2015-10-12 10:41:48 -0700336DEF_GM( return new GradientsLocalPerspectiveGM(true); )
337DEF_GM( return new GradientsLocalPerspectiveGM(false); )
rileya@google.com5cf2c282012-07-09 14:42:16 +0000338
339// Based on the original gradient slide, but with perspective applied to
340// the view matrix
341class GradientsViewPerspectiveGM : public GradientsGM {
fmalita063675b2015-10-12 10:41:48 -0700342public:
343 GradientsViewPerspectiveGM(bool dither) : INHERITED(dither) { }
344
Hal Canaryfa3305a2019-07-18 12:36:54 -0400345private:
346 SkString onShortName() override {
fmalita063675b2015-10-12 10:41:48 -0700347 return SkString(fDither ? "gradients_view_perspective" :
348 "gradients_view_perspective_nodither");
rileya@google.com5cf2c282012-07-09 14:42:16 +0000349 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000350
Hal Canaryfa3305a2019-07-18 12:36:54 -0400351 SkISize onISize() override { return {840, 500}; }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000352
Hal Canaryfa3305a2019-07-18 12:36:54 -0400353 void onDraw(SkCanvas* canvas) override {
rileya@google.com5cf2c282012-07-09 14:42:16 +0000354 SkMatrix perspective;
355 perspective.setIdentity();
reed80ea19c2015-05-12 10:37:34 -0700356 perspective.setPerspY(0.001f);
357 perspective.setSkewX(SkIntToScalar(8) / 25);
scroggo@google.com837d31a2012-08-15 18:42:45 +0000358 canvas->concat(perspective);
Hal Canaryfa3305a2019-07-18 12:36:54 -0400359 this->INHERITED::onDraw(canvas);
rileya@google.com5cf2c282012-07-09 14:42:16 +0000360 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000361
rileya@google.com5cf2c282012-07-09 14:42:16 +0000362private:
363 typedef GradientsGM INHERITED;
364};
fmalita063675b2015-10-12 10:41:48 -0700365DEF_GM( return new GradientsViewPerspectiveGM(true); )
366DEF_GM( return new GradientsViewPerspectiveGM(false); )
rileya@google.com5cf2c282012-07-09 14:42:16 +0000367
reed@google.comac864a92011-06-27 18:11:17 +0000368/*
369 Inspired by this <canvas> javascript, where we need to detect that we are not
370 solving a quadratic equation, but must instead solve a linear (since our X^2
371 coefficient is 0)
372
373 ctx.fillStyle = '#f00';
374 ctx.fillRect(0, 0, 100, 50);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000375
reed@google.comac864a92011-06-27 18:11:17 +0000376 var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
377 g.addColorStop(0, '#f00');
378 g.addColorStop(0.01, '#0f0');
379 g.addColorStop(0.99, '#0f0');
380 g.addColorStop(1, '#f00');
381 ctx.fillStyle = g;
382 ctx.fillRect(0, 0, 100, 50);
383 */
Hal Canaryfa3305a2019-07-18 12:36:54 -0400384class GradientsDegenrate2PointGM : public skiagm::GM {
reed@google.comac864a92011-06-27 18:11:17 +0000385public:
fmalita063675b2015-10-12 10:41:48 -0700386 GradientsDegenrate2PointGM(bool dither) : fDither(dither) {}
rmistry@google.comd6176b02012-08-23 18:14:13 +0000387
Hal Canaryfa3305a2019-07-18 12:36:54 -0400388private:
389 SkString onShortName() override {
fmalita063675b2015-10-12 10:41:48 -0700390 return SkString(fDither ? "gradients_degenerate_2pt" : "gradients_degenerate_2pt_nodither");
reed@google.comac864a92011-06-27 18:11:17 +0000391 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000392
Hal Canaryfa3305a2019-07-18 12:36:54 -0400393 SkISize onISize() override { return {320, 320}; }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000394
Hal Canaryfa3305a2019-07-18 12:36:54 -0400395 void onDraw(SkCanvas* canvas) override {
reed@google.comac864a92011-06-27 18:11:17 +0000396 canvas->drawColor(SK_ColorBLUE);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000397
reed@google.comac864a92011-06-27 18:11:17 +0000398 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000399 SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 };
epoger@google.com59f3abf2011-07-21 15:50:33 +0000400 SkPoint c0;
401 c0.iset(-80, 25);
402 SkScalar r0 = SkIntToScalar(70);
403 SkPoint c1;
404 c1.iset(0, 25);
405 SkScalar r1 = SkIntToScalar(150);
reed9283d202016-03-13 13:01:57 -0700406 SkPaint paint;
reed1a9b9642016-03-13 14:13:58 -0700407 paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors,
408 pos, SK_ARRAY_COUNT(pos),
Mike Reedfae8fce2019-04-03 10:27:45 -0400409 SkTileMode::kClamp));
fmalita063675b2015-10-12 10:41:48 -0700410 paint.setDither(fDither);
reed@google.comac864a92011-06-27 18:11:17 +0000411 canvas->drawPaint(paint);
412 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000413
fmalita063675b2015-10-12 10:41:48 -0700414 bool fDither;
reed@google.comac864a92011-06-27 18:11:17 +0000415};
fmalita063675b2015-10-12 10:41:48 -0700416DEF_GM( return new GradientsDegenrate2PointGM(true); )
417DEF_GM( return new GradientsDegenrate2PointGM(false); )
reed@google.comac864a92011-06-27 18:11:17 +0000418
caryclarkcb100712016-02-26 05:59:40 -0800419/* bug.skia.org/517
420<canvas id="canvas"></canvas>
421<script>
422var c = document.getElementById("canvas");
423var ctx = c.getContext("2d");
424ctx.fillStyle = '#ff0';
425ctx.fillRect(0, 0, 100, 50);
426
427var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
428g.addColorStop(0, '#0f0');
429g.addColorStop(0.003, '#f00'); // 0.004 makes this work
430g.addColorStop(1, '#ff0');
431ctx.fillStyle = g;
432ctx.fillRect(0, 0, 100, 50);
433</script>
434*/
435
436// should draw only green
437DEF_SIMPLE_GM(small_color_stop, canvas, 100, 150) {
438 SkColor colors[] = { SK_ColorGREEN, SK_ColorRED, SK_ColorYELLOW };
439 SkScalar pos[] = { 0, 0.003f, SK_Scalar1 }; // 0.004f makes this work
440 SkPoint c0 = { 200, 25 };
441 SkScalar r0 = 20;
442 SkPoint c1 = { 200, 25 };
443 SkScalar r1 = 10;
reed1a9b9642016-03-13 14:13:58 -0700444
caryclarkcb100712016-02-26 05:59:40 -0800445 SkPaint paint;
446 paint.setColor(SK_ColorYELLOW);
447 canvas->drawRect(SkRect::MakeWH(100, 150), paint);
reed1a9b9642016-03-13 14:13:58 -0700448 paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors, pos,
449 SK_ARRAY_COUNT(pos),
Mike Reedfae8fce2019-04-03 10:27:45 -0400450 SkTileMode::kClamp));
caryclarkcb100712016-02-26 05:59:40 -0800451 canvas->drawRect(SkRect::MakeWH(100, 150), paint);
452}
453
454
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000455/// Tests correctness of *optimized* codepaths in gradients.
456
Hal Canaryfa3305a2019-07-18 12:36:54 -0400457class ClampedGradientsGM : public skiagm::GM {
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000458public:
fmalita063675b2015-10-12 10:41:48 -0700459 ClampedGradientsGM(bool dither) : fDither(dither) {}
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000460
Hal Canaryfa3305a2019-07-18 12:36:54 -0400461private:
462 SkString onShortName() override {
fmalita063675b2015-10-12 10:41:48 -0700463 return SkString(fDither ? "clamped_gradients" : "clamped_gradients_nodither");
464 }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000465
Hal Canaryfa3305a2019-07-18 12:36:54 -0400466 SkISize onISize() override { return {640, 510}; }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000467
Hal Canaryfa3305a2019-07-18 12:36:54 -0400468 void onDraw(SkCanvas* canvas) override {
Mike Kleind46dce32018-08-16 10:17:03 -0400469 canvas->drawColor(0xFFDDDDDD);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000470
471 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
472 SkPaint paint;
fmalita063675b2015-10-12 10:41:48 -0700473 paint.setDither(fDither);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000474 paint.setAntiAlias(true);
475
476 SkPoint center;
477 center.iset(0, 300);
478 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
reed1a9b9642016-03-13 14:13:58 -0700479 paint.setShader(SkGradientShader::MakeRadial(
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000480 SkPoint(center),
halcanary96fcdcc2015-08-27 07:41:13 -0700481 SkIntToScalar(200), gColors, nullptr, 5,
Mike Reedfae8fce2019-04-03 10:27:45 -0400482 SkTileMode::kClamp));
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000483 canvas->drawRect(r, paint);
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000484 }
485
fmalita063675b2015-10-12 10:41:48 -0700486 bool fDither;
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000487};
fmalita063675b2015-10-12 10:41:48 -0700488DEF_GM( return new ClampedGradientsGM(true); )
489DEF_GM( return new ClampedGradientsGM(false); )
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000490
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000491/// Checks quality of large radial gradients, which may display
492/// some banding.
493
Hal Canaryfa3305a2019-07-18 12:36:54 -0400494class RadialGradientGM : public skiagm::GM {
mtklein36352bf2015-03-25 18:17:31 -0700495 SkString onShortName() override { return SkString("radial_gradient"); }
Hal Canaryfa3305a2019-07-18 12:36:54 -0400496
497 SkISize onISize() override { return {1280, 1280}; }
498
mtklein36352bf2015-03-25 18:17:31 -0700499 void onDraw(SkCanvas* canvas) override {
reed@google.combb0948f2012-01-31 14:44:13 +0000500 const SkISize dim = this->getISize();
501
Hal Canaryfa3305a2019-07-18 12:36:54 -0400502 canvas->drawColor(0xFF000000);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000503
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000504 SkPaint paint;
505 paint.setDither(true);
506 SkPoint center;
reed@google.combb0948f2012-01-31 14:44:13 +0000507 center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
508 SkScalar radius = SkIntToScalar(dim.width())/2;
509 const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000510 const SkScalar pos[] = { 0.0f,
511 0.35f,
512 1.0f };
reed1a9b9642016-03-13 14:13:58 -0700513 paint.setShader(SkGradientShader::MakeRadial(center, radius, colors, pos,
514 SK_ARRAY_COUNT(pos),
Mike Reedfae8fce2019-04-03 10:27:45 -0400515 SkTileMode::kClamp));
reed@google.combb0948f2012-01-31 14:44:13 +0000516 SkRect r = {
517 0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
518 };
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000519 canvas->drawRect(r, paint);
520 }
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000521};
reed3d9005c2015-04-23 10:30:27 -0700522DEF_GM( return new RadialGradientGM; )
tomhudson@google.comb18e58c2012-01-30 20:00:13 +0000523
Hal Canaryfa3305a2019-07-18 12:36:54 -0400524class RadialGradient2GM : public skiagm::GM {
mtklein@google.com361a72f2013-08-19 18:43:34 +0000525public:
fmalita063675b2015-10-12 10:41:48 -0700526 RadialGradient2GM(bool dither) : fDither(dither) {}
mtklein@google.com361a72f2013-08-19 18:43:34 +0000527
Hal Canaryfa3305a2019-07-18 12:36:54 -0400528private:
fmalita063675b2015-10-12 10:41:48 -0700529 SkString onShortName() override {
530 return SkString(fDither ? "radial_gradient2" : "radial_gradient2_nodither");
531 }
532
Hal Canaryfa3305a2019-07-18 12:36:54 -0400533 SkISize onISize() override { return {800, 400}; }
mtklein@google.com361a72f2013-08-19 18:43:34 +0000534
535 // Reproduces the example given in bug 7671058.
mtklein36352bf2015-03-25 18:17:31 -0700536 void onDraw(SkCanvas* canvas) override {
mtklein@google.com361a72f2013-08-19 18:43:34 +0000537 SkPaint paint1, paint2, paint3;
538 paint1.setStyle(SkPaint::kFill_Style);
539 paint2.setStyle(SkPaint::kFill_Style);
540 paint3.setStyle(SkPaint::kFill_Style);
541
542 const SkColor sweep_colors[] =
543 { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 };
544 const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 };
545 const SkColor colors2[] = { 0xFF000000, 0x00000000 };
546
547 const SkScalar cx = 200, cy = 200, radius = 150;
548 SkPoint center;
549 center.set(cx, cy);
550
mtklein@google.com3ef7eea2013-09-16 13:02:52 +0000551 // We can either interpolate endpoints and premultiply each point (default, more precision),
552 // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap).
553 const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag };
mtklein@google.com361a72f2013-08-19 18:43:34 +0000554
mtklein@google.com3ef7eea2013-09-16 13:02:52 +0000555 for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) {
reed1a9b9642016-03-13 14:13:58 -0700556 paint1.setShader(SkGradientShader::MakeSweep(cx, cy, sweep_colors,
557 nullptr, SK_ARRAY_COUNT(sweep_colors),
558 flags[i], nullptr));
559 paint2.setShader(SkGradientShader::MakeRadial(center, radius, colors1,
560 nullptr, SK_ARRAY_COUNT(colors1),
Mike Reedfae8fce2019-04-03 10:27:45 -0400561 SkTileMode::kClamp,
reed1a9b9642016-03-13 14:13:58 -0700562 flags[i], nullptr));
563 paint3.setShader(SkGradientShader::MakeRadial(center, radius, colors2,
564 nullptr, SK_ARRAY_COUNT(colors2),
Mike Reedfae8fce2019-04-03 10:27:45 -0400565 SkTileMode::kClamp,
reed1a9b9642016-03-13 14:13:58 -0700566 flags[i], nullptr));
fmalita063675b2015-10-12 10:41:48 -0700567 paint1.setDither(fDither);
fmalita063675b2015-10-12 10:41:48 -0700568 paint2.setDither(fDither);
fmalita063675b2015-10-12 10:41:48 -0700569 paint3.setDither(fDither);
mtklein@google.com3ef7eea2013-09-16 13:02:52 +0000570
571 canvas->drawCircle(cx, cy, radius, paint1);
572 canvas->drawCircle(cx, cy, radius, paint3);
573 canvas->drawCircle(cx, cy, radius, paint2);
574
575 canvas->translate(400, 0);
576 }
mtklein@google.com361a72f2013-08-19 18:43:34 +0000577 }
578
579private:
fmalita063675b2015-10-12 10:41:48 -0700580 bool fDither;
581
mtklein@google.com361a72f2013-08-19 18:43:34 +0000582 typedef GM INHERITED;
583};
fmalita063675b2015-10-12 10:41:48 -0700584DEF_GM( return new RadialGradient2GM(true); )
585DEF_GM( return new RadialGradient2GM(false); )
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000586
reed3d9005c2015-04-23 10:30:27 -0700587// Shallow radial (shows banding on raster)
Hal Canaryfa3305a2019-07-18 12:36:54 -0400588class RadialGradient3GM : public skiagm::GM {
fmalita063675b2015-10-12 10:41:48 -0700589public:
590 RadialGradient3GM(bool dither) : fDither(dither) { }
reed@android.com42309d42009-06-22 02:06:35 +0000591
Hal Canaryfa3305a2019-07-18 12:36:54 -0400592private:
fmalita063675b2015-10-12 10:41:48 -0700593 SkString onShortName() override {
594 return SkString(fDither ? "radial_gradient3" : "radial_gradient3_nodither");
595 }
reed@android.com42309d42009-06-22 02:06:35 +0000596
Hal Canaryfa3305a2019-07-18 12:36:54 -0400597 SkISize onISize() override { return {500, 500}; }
reed@google.comac864a92011-06-27 18:11:17 +0000598
reed3d9005c2015-04-23 10:30:27 -0700599 bool runAsBench() const override { return true; }
tomhudson@google.com5ea050f2011-09-26 15:03:55 +0000600
reed3d9005c2015-04-23 10:30:27 -0700601 void onOnceBeforeDraw() override {
602 const SkPoint center = { 0, 0 };
603 const SkScalar kRadius = 3000;
604 const SkColor gColors[] = { 0xFFFFFFFF, 0xFF000000 };
reed1a9b9642016-03-13 14:13:58 -0700605 fShader = SkGradientShader::MakeRadial(center, kRadius, gColors, nullptr, 2,
Mike Reedfae8fce2019-04-03 10:27:45 -0400606 SkTileMode::kClamp);
reed3d9005c2015-04-23 10:30:27 -0700607 }
rileya@google.com5cf2c282012-07-09 14:42:16 +0000608
reed3d9005c2015-04-23 10:30:27 -0700609 void onDraw(SkCanvas* canvas) override {
610 SkPaint paint;
611 paint.setShader(fShader);
fmalita063675b2015-10-12 10:41:48 -0700612 paint.setDither(fDither);
reed3d9005c2015-04-23 10:30:27 -0700613 canvas->drawRect(SkRect::MakeWH(500, 500), paint);
614 }
halcanary9d524f22016-03-29 09:03:52 -0700615
reed3d9005c2015-04-23 10:30:27 -0700616private:
reed1a9b9642016-03-13 14:13:58 -0700617 sk_sp<SkShader> fShader;
fmalita063675b2015-10-12 10:41:48 -0700618 bool fDither;
619
reed3d9005c2015-04-23 10:30:27 -0700620 typedef GM INHERITED;
621};
fmalita063675b2015-10-12 10:41:48 -0700622DEF_GM( return new RadialGradient3GM(true); )
623DEF_GM( return new RadialGradient3GM(false); )
rileya@google.com5cf2c282012-07-09 14:42:16 +0000624
Hal Canaryfa3305a2019-07-18 12:36:54 -0400625class RadialGradient4GM : public skiagm::GM {
fmalita063675b2015-10-12 10:41:48 -0700626public:
627 RadialGradient4GM(bool dither) : fDither(dither) { }
caryclark1864bfa2015-07-30 06:41:39 -0700628
Hal Canaryfa3305a2019-07-18 12:36:54 -0400629private:
fmalita063675b2015-10-12 10:41:48 -0700630 SkString onShortName() override {
631 return SkString(fDither ? "radial_gradient4" : "radial_gradient4_nodither");
632 }
caryclark1864bfa2015-07-30 06:41:39 -0700633
Hal Canaryfa3305a2019-07-18 12:36:54 -0400634 SkISize onISize() override { return {500, 500}; }
caryclark1864bfa2015-07-30 06:41:39 -0700635
636 void onOnceBeforeDraw() override {
637 const SkPoint center = { 250, 250 };
638 const SkScalar kRadius = 250;
639 const SkColor colors[] = { SK_ColorRED, SK_ColorRED, SK_ColorWHITE, SK_ColorWHITE,
640 SK_ColorRED };
641 const SkScalar pos[] = { 0, .4f, .4f, .8f, .8f, 1 };
reed1a9b9642016-03-13 14:13:58 -0700642 fShader = SkGradientShader::MakeRadial(center, kRadius, colors, pos,
Mike Reedfae8fce2019-04-03 10:27:45 -0400643 SK_ARRAY_COUNT(gColors), SkTileMode::kClamp);
caryclark1864bfa2015-07-30 06:41:39 -0700644 }
645
646 void onDraw(SkCanvas* canvas) override {
647 SkPaint paint;
648 paint.setAntiAlias(true);
fmalita063675b2015-10-12 10:41:48 -0700649 paint.setDither(fDither);
caryclark1864bfa2015-07-30 06:41:39 -0700650 paint.setShader(fShader);
651 canvas->drawRect(SkRect::MakeWH(500, 500), paint);
652 }
halcanary9d524f22016-03-29 09:03:52 -0700653
caryclark1864bfa2015-07-30 06:41:39 -0700654private:
reed1a9b9642016-03-13 14:13:58 -0700655 sk_sp<SkShader> fShader;
fmalita063675b2015-10-12 10:41:48 -0700656 bool fDither;
657
caryclark1864bfa2015-07-30 06:41:39 -0700658 typedef GM INHERITED;
659};
fmalita063675b2015-10-12 10:41:48 -0700660DEF_GM( return new RadialGradient4GM(true); )
661DEF_GM( return new RadialGradient4GM(false); )
caryclark1864bfa2015-07-30 06:41:39 -0700662
Hal Canaryfa3305a2019-07-18 12:36:54 -0400663class LinearGradientGM : public skiagm::GM {
fmalita063675b2015-10-12 10:41:48 -0700664public:
665 LinearGradientGM(bool dither) : fDither(dither) { }
caryclark159fa572015-07-30 12:35:48 -0700666
Hal Canaryfa3305a2019-07-18 12:36:54 -0400667private:
fmalita063675b2015-10-12 10:41:48 -0700668 SkString onShortName() override {
669 return SkString(fDither ? "linear_gradient" : "linear_gradient_nodither");
670 }
671
caryclark159fa572015-07-30 12:35:48 -0700672 const SkScalar kWidthBump = 30.f;
673 const SkScalar kHeight = 5.f;
674 const SkScalar kMinWidth = 540.f;
675
Hal Canaryfa3305a2019-07-18 12:36:54 -0400676 SkISize onISize() override { return {500, 500}; }
caryclark159fa572015-07-30 12:35:48 -0700677
678 void onOnceBeforeDraw() override {
679 SkPoint pts[2] = { {0, 0}, {0, 0} };
680 const SkColor colors[] = { SK_ColorWHITE, SK_ColorWHITE, 0xFF008200, 0xFF008200,
681 SK_ColorWHITE, SK_ColorWHITE };
682 const SkScalar unitPos[] = { 0, 50, 70, 500, 540 };
683 SkScalar pos[6];
684 pos[5] = 1;
685 for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) {
686 pts[1].fX = 500.f + index * kWidthBump;
687 for (int inner = 0; inner < (int) SK_ARRAY_COUNT(unitPos); ++inner) {
688 pos[inner] = unitPos[inner] / (kMinWidth + index * kWidthBump);
689 }
reed1a9b9642016-03-13 14:13:58 -0700690 fShader[index] = SkGradientShader::MakeLinear(pts, colors, pos,
Mike Reedfae8fce2019-04-03 10:27:45 -0400691 SK_ARRAY_COUNT(gColors), SkTileMode::kClamp);
caryclark159fa572015-07-30 12:35:48 -0700692 }
693 }
694
695 void onDraw(SkCanvas* canvas) override {
696 SkPaint paint;
697 paint.setAntiAlias(true);
fmalita063675b2015-10-12 10:41:48 -0700698 paint.setDither(fDither);
caryclark159fa572015-07-30 12:35:48 -0700699 for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) {
700 paint.setShader(fShader[index]);
701 canvas->drawRect(SkRect::MakeLTRB(0, index * kHeight, kMinWidth + index * kWidthBump,
702 (index + 1) * kHeight), paint);
703 }
704 }
halcanary9d524f22016-03-29 09:03:52 -0700705
caryclark159fa572015-07-30 12:35:48 -0700706private:
reed1a9b9642016-03-13 14:13:58 -0700707 sk_sp<SkShader> fShader[100];
fmalita063675b2015-10-12 10:41:48 -0700708 bool fDither;
709
caryclark159fa572015-07-30 12:35:48 -0700710 typedef GM INHERITED;
711};
fmalita063675b2015-10-12 10:41:48 -0700712DEF_GM( return new LinearGradientGM(true); )
713DEF_GM( return new LinearGradientGM(false); )
caryclark159fa572015-07-30 12:35:48 -0700714
Hal Canaryfa3305a2019-07-18 12:36:54 -0400715class LinearGradientTinyGM : public skiagm::GM {
716 static constexpr uint32_t kFlags = 0;
fmalitabc590c02016-02-22 09:12:33 -0800717
Hal Canaryfa3305a2019-07-18 12:36:54 -0400718 SkString onShortName() override { return SkString("linear_gradient_tiny"); }
fmalita8b78bd62015-11-20 13:58:24 -0800719
Hal Canaryfa3305a2019-07-18 12:36:54 -0400720 SkISize onISize() override { return {600, 500}; }
fmalita8b78bd62015-11-20 13:58:24 -0800721
722 void onDraw(SkCanvas* canvas) override {
723 const SkScalar kRectSize = 100;
724 const unsigned kStopCount = 3;
725 const SkColor colors[kStopCount] = { SK_ColorGREEN, SK_ColorRED, SK_ColorGREEN };
726 const struct {
727 SkPoint pts[2];
728 SkScalar pos[kStopCount];
729 } configs[] = {
reedde3aac82015-11-22 13:00:04 -0800730 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.999999f, 1 }},
731 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.000001f, 1 }},
732 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.999999999f, 1 }},
733 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.000000001f, 1 }},
fmalita8b78bd62015-11-20 13:58:24 -0800734
reedde3aac82015-11-22 13:00:04 -0800735 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.999999f, 1 }},
736 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.000001f, 1 }},
737 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.999999999f, 1 }},
738 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.000000001f, 1 }},
739
740 { { SkPoint::Make(0, 0), SkPoint::Make(0.00001f, 0) }, { 0, 0.5f, 1 }},
741 { { SkPoint::Make(9.99999f, 0), SkPoint::Make(10, 0) }, { 0, 0.5f, 1 }},
742 { { SkPoint::Make(0, 0), SkPoint::Make(0, 0.00001f) }, { 0, 0.5f, 1 }},
743 { { SkPoint::Make(0, 9.99999f), SkPoint::Make(0, 10) }, { 0, 0.5f, 1 }},
fmalita8b78bd62015-11-20 13:58:24 -0800744 };
745
746 SkPaint paint;
747 for (unsigned i = 0; i < SK_ARRAY_COUNT(configs); ++i) {
748 SkAutoCanvasRestore acr(canvas, true);
reed1a9b9642016-03-13 14:13:58 -0700749 paint.setShader(SkGradientShader::MakeLinear(configs[i].pts, colors, configs[i].pos,
Mike Reedfae8fce2019-04-03 10:27:45 -0400750 kStopCount, SkTileMode::kClamp,
Hal Canaryfa3305a2019-07-18 12:36:54 -0400751 kFlags, nullptr));
fmalita8b78bd62015-11-20 13:58:24 -0800752 canvas->translate(kRectSize * ((i % 4) * 1.5f + 0.25f),
753 kRectSize * ((i / 4) * 1.5f + 0.25f));
754
fmalita8b78bd62015-11-20 13:58:24 -0800755 canvas->drawRect(SkRect::MakeWH(kRectSize, kRectSize), paint);
756 }
757 }
fmalitabc590c02016-02-22 09:12:33 -0800758};
Hal Canaryfa3305a2019-07-18 12:36:54 -0400759
760DEF_GM( return new LinearGradientTinyGM; )
761} // namespace
reedd4eaa252016-01-22 10:35:26 -0800762
763///////////////////////////////////////////////////////////////////////////////////////////////////
764
765struct GradRun {
766 SkColor fColors[4];
767 SkScalar fPos[4];
768 int fCount;
769};
770
771#define SIZE 121
772
Mike Reedfae8fce2019-04-03 10:27:45 -0400773static sk_sp<SkShader> make_linear(const GradRun& run, SkTileMode mode) {
reedd4eaa252016-01-22 10:35:26 -0800774 const SkPoint pts[] { { 30, 30 }, { SIZE - 30, SIZE - 30 } };
reed1a9b9642016-03-13 14:13:58 -0700775 return SkGradientShader::MakeLinear(pts, run.fColors, run.fPos, run.fCount, mode);
reedd4eaa252016-01-22 10:35:26 -0800776}
777
Mike Reedfae8fce2019-04-03 10:27:45 -0400778static sk_sp<SkShader> make_radial(const GradRun& run, SkTileMode mode) {
reedd4eaa252016-01-22 10:35:26 -0800779 const SkScalar half = SIZE * 0.5f;
reed1a9b9642016-03-13 14:13:58 -0700780 return SkGradientShader::MakeRadial({half,half}, half - 10, run.fColors, run.fPos,
781 run.fCount, mode);
reedd4eaa252016-01-22 10:35:26 -0800782}
783
Mike Reedfae8fce2019-04-03 10:27:45 -0400784static sk_sp<SkShader> make_conical(const GradRun& run, SkTileMode mode) {
reedd4eaa252016-01-22 10:35:26 -0800785 const SkScalar half = SIZE * 0.5f;
786 const SkPoint center { half, half };
reed1a9b9642016-03-13 14:13:58 -0700787 return SkGradientShader::MakeTwoPointConical(center, 20, center, half - 10,
788 run.fColors, run.fPos, run.fCount, mode);
reedd4eaa252016-01-22 10:35:26 -0800789}
790
Mike Reedfae8fce2019-04-03 10:27:45 -0400791static sk_sp<SkShader> make_sweep(const GradRun& run, SkTileMode) {
reedd4eaa252016-01-22 10:35:26 -0800792 const SkScalar half = SIZE * 0.5f;
reed1a9b9642016-03-13 14:13:58 -0700793 return SkGradientShader::MakeSweep(half, half, run.fColors, run.fPos, run.fCount);
reedd4eaa252016-01-22 10:35:26 -0800794}
795
796/*
797 * Exercise duplicate color-stops, at the ends, and in the middle
798 *
799 * At the time of this writing, only Linear correctly deals with duplicates at the ends,
800 * and then only correctly on CPU backend.
801 */
802DEF_SIMPLE_GM(gradients_dup_color_stops, canvas, 704, 564) {
803 const SkColor preColor = 0xFFFF0000; // clamp color before start
804 const SkColor postColor = 0xFF0000FF; // clamp color after end
805 const SkColor color0 = 0xFF000000;
806 const SkColor color1 = 0xFF00FF00;
807 const SkColor badColor = 0xFF3388BB; // should never be seen, fills out fixed-size array
808
809 const GradRun runs[] = {
810 { { color0, color1, badColor, badColor },
811 { 0, 1, -1, -1 },
812 2,
813 },
814 { { preColor, color0, color1, badColor },
815 { 0, 0, 1, -1 },
816 3,
817 },
818 { { color0, color1, postColor, badColor },
819 { 0, 1, 1, -1 },
820 3,
821 },
822 { { preColor, color0, color1, postColor },
823 { 0, 0, 1, 1 },
824 4,
825 },
826 { { color0, color0, color1, color1 },
827 { 0, 0.5f, 0.5f, 1 },
828 4,
829 },
830 };
Mike Reedfae8fce2019-04-03 10:27:45 -0400831 sk_sp<SkShader> (*factories[])(const GradRun&, SkTileMode) {
reedd4eaa252016-01-22 10:35:26 -0800832 make_linear, make_radial, make_conical, make_sweep
833 };
834
835 const SkRect rect = SkRect::MakeWH(SIZE, SIZE);
836 const SkScalar dx = SIZE + 20;
837 const SkScalar dy = SIZE + 20;
Mike Reedfae8fce2019-04-03 10:27:45 -0400838 const SkTileMode mode = SkTileMode::kClamp;
reedd4eaa252016-01-22 10:35:26 -0800839
840 SkPaint paint;
841 canvas->translate(10, 10 - dy);
842 for (auto factory : factories) {
843 canvas->translate(0, dy);
844 SkAutoCanvasRestore acr(canvas, true);
845 for (const auto& run : runs) {
reed1a9b9642016-03-13 14:13:58 -0700846 paint.setShader(factory(run, mode));
reedd4eaa252016-01-22 10:35:26 -0800847 canvas->drawRect(rect, paint);
848 canvas->translate(dx, 0);
849 }
850 }
851}
fmalitabc590c02016-02-22 09:12:33 -0800852
Florin Malitad1aedde2017-06-07 15:03:38 -0400853static void draw_many_stops(SkCanvas* canvas) {
fmalitabc590c02016-02-22 09:12:33 -0800854 const unsigned kStopCount = 200;
855 const SkPoint pts[] = { {50, 50}, {450, 465}};
856
857 SkColor colors[kStopCount];
858 for (unsigned i = 0; i < kStopCount; i++) {
859 switch (i % 5) {
860 case 0: colors[i] = SK_ColorRED; break;
861 case 1: colors[i] = SK_ColorGREEN; break;
862 case 2: colors[i] = SK_ColorGREEN; break;
863 case 3: colors[i] = SK_ColorBLUE; break;
864 case 4: colors[i] = SK_ColorRED; break;
865 }
866 }
867
reed9283d202016-03-13 13:01:57 -0700868 SkPaint p;
reed1a9b9642016-03-13 14:13:58 -0700869 p.setShader(SkGradientShader::MakeLinear(
Mike Reedfae8fce2019-04-03 10:27:45 -0400870 pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkTileMode::kClamp));
reed9283d202016-03-13 13:01:57 -0700871
fmalitabc590c02016-02-22 09:12:33 -0800872 canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p);
873}
874
875DEF_SIMPLE_GM(gradient_many_stops, canvas, 500, 500) {
Florin Malitad1aedde2017-06-07 15:03:38 -0400876 draw_many_stops(canvas);
fmalitabc590c02016-02-22 09:12:33 -0800877}
878
Florin Malitaa0ac9632017-05-03 13:07:28 -0400879static void draw_circle_shader(SkCanvas* canvas, SkScalar cx, SkScalar cy, SkScalar r,
880 sk_sp<SkShader> (*shaderFunc)()) {
881 SkPaint p;
882 p.setAntiAlias(true);
883 p.setShader(shaderFunc());
884 canvas->drawCircle(cx, cy, r, p);
885
886 p.setShader(nullptr);
887 p.setColor(SK_ColorGRAY);
888 p.setStyle(SkPaint::kStroke_Style);
889 p.setStrokeWidth(2);
890 canvas->drawCircle(cx, cy, r, p);
891}
892
893DEF_SIMPLE_GM(fancy_gradients, canvas, 800, 300) {
894 draw_circle_shader(canvas, 150, 150, 100, []() -> sk_sp<SkShader> {
895 // Checkerboard using two linear gradients + picture shader.
896 SkScalar kTileSize = 80 / sqrtf(2);
897 SkColor colors1[] = { 0xff000000, 0xff000000,
898 0xffffffff, 0xffffffff,
899 0xff000000, 0xff000000 };
900 SkColor colors2[] = { 0xff000000, 0xff000000,
901 0x00000000, 0x00000000,
902 0xff000000, 0xff000000 };
903 SkScalar pos[] = { 0, .25f, .25f, .75f, .75f, 1 };
904 static_assert(SK_ARRAY_COUNT(colors1) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
905 static_assert(SK_ARRAY_COUNT(colors2) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
906
907 SkPictureRecorder recorder;
908 recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize));
909
910 SkPaint p;
911
912 SkPoint pts1[] = { { 0, 0 }, { kTileSize, kTileSize }};
913 p.setShader(SkGradientShader::MakeLinear(pts1, colors1, pos, SK_ARRAY_COUNT(colors1),
Mike Reedfae8fce2019-04-03 10:27:45 -0400914 SkTileMode::kClamp, 0, nullptr));
Florin Malitaa0ac9632017-05-03 13:07:28 -0400915 recorder.getRecordingCanvas()->drawPaint(p);
916
917 SkPoint pts2[] = { { 0, kTileSize }, { kTileSize, 0 }};
918 p.setShader(SkGradientShader::MakeLinear(pts2, colors2, pos, SK_ARRAY_COUNT(colors2),
Mike Reedfae8fce2019-04-03 10:27:45 -0400919 SkTileMode::kClamp, 0, nullptr));
Florin Malitaa0ac9632017-05-03 13:07:28 -0400920 recorder.getRecordingCanvas()->drawPaint(p);
921
922 SkMatrix m = SkMatrix::I();
923 m.preRotate(45);
Mike Reedfae8fce2019-04-03 10:27:45 -0400924 return recorder.finishRecordingAsPicture()->makeShader(
925 SkTileMode::kRepeat,
926 SkTileMode::kRepeat, &m, nullptr);
Florin Malitaa0ac9632017-05-03 13:07:28 -0400927 });
928
929 draw_circle_shader(canvas, 400, 150, 100, []() -> sk_sp<SkShader> {
930 // Checkerboard using a sweep gradient + picture shader.
931 SkScalar kTileSize = 80;
932 SkColor colors[] = { 0xff000000, 0xff000000,
933 0xffffffff, 0xffffffff,
934 0xff000000, 0xff000000,
935 0xffffffff, 0xffffffff };
936 SkScalar pos[] = { 0, .25f, .25f, .5f, .5f, .75f, .75f, 1 };
937 static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
938
939 SkPaint p;
940 p.setShader(SkGradientShader::MakeSweep(kTileSize / 2, kTileSize / 2,
941 colors, pos, SK_ARRAY_COUNT(colors), 0, nullptr));
942 SkPictureRecorder recorder;
943 recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize))->drawPaint(p);
944
Mike Reedfae8fce2019-04-03 10:27:45 -0400945 return recorder.finishRecordingAsPicture()->makeShader(
946 SkTileMode::kRepeat,
947 SkTileMode::kRepeat);
Florin Malitaa0ac9632017-05-03 13:07:28 -0400948 });
949
950 draw_circle_shader(canvas, 650, 150, 100, []() -> sk_sp<SkShader> {
951 // Dartboard using sweep + radial.
952 const SkColor a = 0xffffffff;
953 const SkColor b = 0xff000000;
954 SkColor colors[] = { a, a, b, b, a, a, b, b, a, a, b, b, a, a, b, b};
955 SkScalar pos[] = { 0, .125f, .125f, .25f, .25f, .375f, .375f, .5f, .5f,
956 .625f, .625f, .75f, .75f, .875f, .875f, 1};
957 static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
958
959 SkPoint center = { 650, 150 };
960 sk_sp<SkShader> sweep1 = SkGradientShader::MakeSweep(center.x(), center.y(), colors, pos,
961 SK_ARRAY_COUNT(colors), 0, nullptr);
962 SkMatrix m = SkMatrix::I();
963 m.preRotate(22.5f, center.x(), center.y());
964 sk_sp<SkShader> sweep2 = SkGradientShader::MakeSweep(center.x(), center.y(), colors, pos,
965 SK_ARRAY_COUNT(colors), 0, &m);
966
Mike Reedc8bea7d2019-04-09 13:55:36 -0400967 sk_sp<SkShader> sweep(SkShaders::Blend(SkBlendMode::kExclusion, sweep1, sweep2));
Florin Malitaa0ac9632017-05-03 13:07:28 -0400968
969 SkScalar radialPos[] = { 0, .02f, .02f, .04f, .04f, .08f, .08f, .16f, .16f, .31f, .31f,
970 .62f, .62f, 1, 1, 1 };
971 static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(radialPos),
972 "color/pos size mismatch");
973
Mike Reedc8bea7d2019-04-09 13:55:36 -0400974 return SkShaders::Blend(SkBlendMode::kExclusion, sweep,
975 SkGradientShader::MakeRadial(center, 100, colors,
976 radialPos,
977 SK_ARRAY_COUNT(radialPos),
978 SkTileMode::kClamp));
Florin Malitaa0ac9632017-05-03 13:07:28 -0400979 });
980}
Florin Malita5a9a9812017-08-01 16:38:08 -0400981
Florin Malita4d274292017-08-02 10:04:48 -0400982DEF_SIMPLE_GM(sweep_tiling, canvas, 690, 512) {
Florin Malita5a9a9812017-08-01 16:38:08 -0400983 static constexpr SkScalar size = 160;
984 static constexpr SkColor colors[] = { SK_ColorBLUE, SK_ColorYELLOW, SK_ColorGREEN };
985 static constexpr SkScalar pos[] = { 0, .25f, .50f };
986 static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "size mismatch");
987
Mike Reedfae8fce2019-04-03 10:27:45 -0400988 static constexpr SkTileMode modes[] = { SkTileMode::kClamp,
989 SkTileMode::kRepeat,
990 SkTileMode::kMirror };
Florin Malita5a9a9812017-08-01 16:38:08 -0400991
992 static const struct {
993 SkScalar start, end;
994 } angles[] = {
995 { -330, -270 },
996 { 30, 90 },
997 { 390, 450 },
Florin Malita50f7a1e2017-08-02 09:40:26 -0400998 { -30, 800 },
Florin Malita5a9a9812017-08-01 16:38:08 -0400999 };
1000
1001 SkPaint p;
1002 const SkRect r = SkRect::MakeWH(size, size);
1003
1004 for (auto mode : modes) {
1005 {
1006 SkAutoCanvasRestore acr(canvas, true);
1007
1008 for (auto angle : angles) {
1009 p.setShader(SkGradientShader::MakeSweep(size / 2, size / 2, colors, pos,
1010 SK_ARRAY_COUNT(colors), mode,
1011 angle.start, angle.end, 0, nullptr));
1012
1013 canvas->drawRect(r, p);
1014 canvas->translate(size * 1.1f, 0);
1015 }
1016 }
1017 canvas->translate(0, size * 1.1f);
1018 }
1019}
Florin Malita36f054a2017-08-03 12:55:41 -04001020
1021// Exercises the special-case Ganesh gradient effects.
Brian Osmana8e57442017-09-11 17:21:35 -04001022DEF_SIMPLE_GM(gradients_interesting, canvas, 640, 1300) {
Florin Malita36f054a2017-08-03 12:55:41 -04001023 static const SkColor colors2[] = { SK_ColorRED, SK_ColorBLUE };
1024 static const SkColor colors3[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorBLUE };
1025 static const SkColor colors4[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorYELLOW, SK_ColorBLUE };
1026
Brian Osmana8e57442017-09-11 17:21:35 -04001027 static const SkScalar softRight[] = { 0, .999f, 1 }; // Based on Android launcher "clipping"
1028 static const SkScalar hardLeft[] = { 0, 0, 1 };
1029 static const SkScalar hardRight[] = { 0, 1, 1 };
1030 static const SkScalar hardCenter[] = { 0, .5f, .5f, 1 };
Florin Malita36f054a2017-08-03 12:55:41 -04001031
1032 static const struct {
1033 const SkColor* colors;
1034 const SkScalar* pos;
1035 int count;
1036 } configs[] = {
1037 { colors2, nullptr, 2 }, // kTwo_ColorType
Brian Osman2ab4b2b2017-09-12 11:20:56 -04001038 { colors3, nullptr, 3 }, // kThree_ColorType (simple)
1039 { colors3, softRight, 3 }, // kThree_ColorType (tricky)
Florin Malita36f054a2017-08-03 12:55:41 -04001040 { colors3, hardLeft, 3 }, // kHardStopLeftEdged_ColorType
1041 { colors3, hardRight, 3 }, // kHardStopRightEdged_ColorType
1042 { colors4, hardCenter, 4 }, // kSingleHardStop_ColorType
1043 };
1044
Mike Reedfae8fce2019-04-03 10:27:45 -04001045 static const SkTileMode modes[] = {
1046 SkTileMode::kClamp,
1047 SkTileMode::kRepeat,
1048 SkTileMode::kMirror,
Florin Malita36f054a2017-08-03 12:55:41 -04001049 };
1050
1051 static constexpr SkScalar size = 200;
1052 static const SkPoint pts[] = { { size / 3, size / 3 }, { size * 2 / 3, size * 2 / 3} };
1053
1054 SkPaint p;
1055 for (const auto& cfg : configs) {
1056 {
1057 SkAutoCanvasRestore acr(canvas, true);
1058 for (auto mode : modes) {
1059 p.setShader(SkGradientShader::MakeLinear(pts, cfg.colors, cfg.pos, cfg.count,
1060 mode));
1061 canvas->drawRect(SkRect::MakeWH(size, size), p);
1062 canvas->translate(size * 1.1f, 0);
1063 }
1064 }
1065 canvas->translate(0, size * 1.1f);
1066 }
1067}