1 /*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "gm/gm.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkPaint.h"
12 #include "include/core/SkPoint.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkShader.h"
17 #include "include/core/SkSize.h"
18 #include "include/core/SkString.h"
19 #include "include/core/SkTileMode.h"
20 #include "include/core/SkTypes.h"
21 #include "include/effects/SkGradientShader.h"
22
23 #include <string.h>
24
25 using namespace skiagm;
26
27 struct GradData {
28 int fCount;
29 const SkColor* fColors;
30 const SkScalar* fPos;
31 };
32
33 constexpr SkColor gColors[] = {
34 SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE,
35 };
36
37 constexpr GradData gGradData[] = {
38 { 1, gColors, nullptr },
39 { 2, gColors, nullptr },
40 { 3, gColors, nullptr },
41 { 4, gColors, nullptr },
42 };
43
MakeLinear(const SkPoint pts[2],const GradData & data,SkTileMode tm)44 static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data, SkTileMode tm) {
45 return SkGradientShader::MakeLinear(pts, data.fColors, data.fPos, data.fCount, tm);
46 }
47
MakeRadial(const SkPoint pts[2],const GradData & data,SkTileMode tm)48 static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data, SkTileMode tm) {
49 SkPoint center;
50 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
51 SkScalarAve(pts[0].fY, pts[1].fY));
52 return SkGradientShader::MakeRadial(center, center.fX, data.fColors, data.fPos, data.fCount, tm);
53 }
54
MakeSweep(const SkPoint pts[2],const GradData & data,SkTileMode)55 static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data, SkTileMode) {
56 SkPoint center;
57 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
58 SkScalarAve(pts[0].fY, pts[1].fY));
59 return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount);
60 }
61
Make2Radial(const SkPoint pts[2],const GradData & data,SkTileMode tm)62 static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data, SkTileMode tm) {
63 SkPoint center0, center1;
64 center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
65 SkScalarAve(pts[0].fY, pts[1].fY));
66 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
67 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
68 return SkGradientShader::MakeTwoPointConical(
69 center1, (pts[1].fX - pts[0].fX) / 7,
70 center0, (pts[1].fX - pts[0].fX) / 2,
71 data.fColors, data.fPos, data.fCount, tm);
72 }
73
Make2Conical(const SkPoint pts[2],const GradData & data,SkTileMode tm)74 static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data, SkTileMode tm) {
75 SkPoint center0, center1;
76 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
77 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
78 center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
79 center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
80 return SkGradientShader::MakeTwoPointConical(center1, radius1,
81 center0, radius0,
82 data.fColors, data.fPos,
83 data.fCount, tm);
84 }
85
86
87 typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data, SkTileMode tm);
88
89 constexpr GradMaker gGradMakers[] = {
90 MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical,
91 };
92
93 ///////////////////////////////////////////////////////////////////////////////
94
95 class GradientsNoTextureGM : public GM {
96 public:
GradientsNoTextureGM(bool dither)97 GradientsNoTextureGM(bool dither) : fDither(dither) {
98 this->setBGColor(0xFFDDDDDD);
99 }
100
101 protected:
getName() const102 SkString getName() const override {
103 return SkString(fDither ? "gradients_no_texture" : "gradients_no_texture_nodither");
104 }
105
getISize()106 SkISize getISize() override { return SkISize::Make(640, 615); }
107
onDraw(SkCanvas * canvas)108 void onDraw(SkCanvas* canvas) override {
109 constexpr SkPoint kPts[2] = { { 0, 0 },
110 { SkIntToScalar(50), SkIntToScalar(50) } };
111 constexpr SkTileMode kTM = SkTileMode::kClamp;
112 SkRect kRect = { 0, 0, SkIntToScalar(50), SkIntToScalar(50) };
113 SkPaint paint;
114 paint.setAntiAlias(true);
115 paint.setDither(fDither);
116
117 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
118 constexpr uint8_t kAlphas[] = { 0xff, 0x40 };
119 for (size_t a = 0; a < std::size(kAlphas); ++a) {
120 for (size_t i = 0; i < std::size(gGradData); ++i) {
121 canvas->save();
122 for (size_t j = 0; j < std::size(gGradMakers); ++j) {
123 paint.setShader(gGradMakers[j](kPts, gGradData[i], kTM));
124 paint.setAlpha(kAlphas[a]);
125 canvas->drawRect(kRect, paint);
126 canvas->translate(0, SkIntToScalar(kRect.height() + 20));
127 }
128 canvas->restore();
129 canvas->translate(SkIntToScalar(kRect.width() + 20), 0);
130 }
131 }
132 }
133
134 private:
135 bool fDither;
136
137 using INHERITED = GM;
138 };
139
140 ///////////////////////////////////////////////////////////////////////////////
141
142 struct ColorPos {
143 SkColor* fColors;
144 SkScalar* fPos;
145 int fCount;
146
ColorPosColorPos147 ColorPos() : fColors(nullptr), fPos(nullptr), fCount(0) {}
~ColorPosColorPos148 ~ColorPos() {
149 delete[] fColors;
150 delete[] fPos;
151 }
152
constructColorPos153 void construct(const SkColor colors[], const SkScalar pos[], int count) {
154 fColors = new SkColor[count];
155 memcpy(fColors, colors, count * sizeof(SkColor));
156 if (pos) {
157 fPos = new SkScalar[count];
158 memcpy(fPos, pos, count * sizeof(SkScalar));
159 fPos[0] = 0;
160 fPos[count - 1] = 1;
161 }
162 fCount = count;
163 }
164 };
165
make0(ColorPos * rec)166 static void make0(ColorPos* rec) {
167 #if 0
168 From http://jsfiddle.net/3fe2a/
169
170 background-image: -webkit-linear-gradient(left, #22d1cd 1%, #22d1cd 0.9510157507590116%, #df4b37 2.9510157507590113%, #df4b37 23.695886056604927%, #22d1cd 25.695886056604927%, #22d1cd 25.39321881940624%, #e6de36 27.39321881940624%, #e6de36 31.849399922570655%, #3267ff 33.849399922570655%, #3267ff 44.57735802921938%, #9d47d1 46.57735802921938%, #9d47d1 53.27185850805876%, #3267ff 55.27185850805876%, #3267ff 61.95718972227316%, #5cdd9d 63.95718972227316%, #5cdd9d 69.89166004442%, #3267ff 71.89166004442%, #3267ff 74.45795382765857%, #9d47d1 76.45795382765857%, #9d47d1 82.78364610713776%, #3267ff 84.78364610713776%, #3267ff 94.52743647737229%, #e3d082 96.52743647737229%, #e3d082 96.03934633331295%);
171 height: 30px;
172 #endif
173
174 const SkColor colors[] = {
175 0xFF22d1cd, 0xFF22d1cd, 0xFFdf4b37, 0xFFdf4b37, 0xFF22d1cd, 0xFF22d1cd, 0xFFe6de36, 0xFFe6de36,
176 0xFF3267ff, 0xFF3267ff, 0xFF9d47d1, 0xFF9d47d1, 0xFF3267ff, 0xFF3267ff, 0xFF5cdd9d, 0xFF5cdd9d,
177 0xFF3267ff, 0xFF3267ff, 0xFF9d47d1, 0xFF9d47d1, 0xFF3267ff, 0xFF3267ff, 0xFFe3d082, 0xFFe3d082
178 };
179 const double percent[] = {
180 1, 0.9510157507590116, 2.9510157507590113, 23.695886056604927,
181 25.695886056604927, 25.39321881940624, 27.39321881940624, 31.849399922570655,
182 33.849399922570655, 44.57735802921938, 46.57735802921938, 53.27185850805876,
183 55.27185850805876, 61.95718972227316, 63.95718972227316, 69.89166004442,
184 71.89166004442, 74.45795382765857, 76.45795382765857, 82.78364610713776,
185 84.78364610713776, 94.52743647737229, 96.52743647737229, 96.03934633331295,
186 };
187 const int N = std::size(percent);
188 SkScalar pos[N];
189 for (int i = 0; i < N; ++i) {
190 pos[i] = SkDoubleToScalar(percent[i] / 100);
191 }
192 rec->construct(colors, pos, N);
193 }
194
make1(ColorPos * rec)195 static void make1(ColorPos* rec) {
196 const SkColor colors[] = {
197 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
198 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
199 SK_ColorBLACK,
200 };
201 rec->construct(colors, nullptr, std::size(colors));
202 }
203
make2(ColorPos * rec)204 static void make2(ColorPos* rec) {
205 const SkColor colors[] = {
206 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
207 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
208 SK_ColorBLACK,
209 };
210 const int N = std::size(colors);
211 SkScalar pos[N];
212 for (int i = 0; i < N; ++i) {
213 pos[i] = SK_Scalar1 * i / (N - 1);
214 }
215 rec->construct(colors, pos, N);
216 }
217
make3(ColorPos * rec)218 static void make3(ColorPos* rec) {
219 const SkColor colors[] = {
220 SK_ColorRED, SK_ColorBLUE, SK_ColorBLUE, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLACK,
221 };
222 const SkScalar pos[] = {
223 0, 0, 0.5f, 0.5, 1, 1,
224 };
225 rec->construct(colors, pos, std::size(colors));
226 }
227
228 class GradientsManyColorsGM : public GM {
229 enum {
230 W = 800,
231 };
232 sk_sp<SkShader> fShader;
233
234 typedef void (*Proc)(ColorPos*);
235 public:
GradientsManyColorsGM(bool dither)236 GradientsManyColorsGM(bool dither) : fDither(dither) {}
237
238 protected:
getName() const239 SkString getName() const override {
240 return SkString(fDither ? "gradients_many" : "gradients_many_nodither");
241 }
242
getISize()243 SkISize getISize() override { return SkISize::Make(880, 400); }
244
onDraw(SkCanvas * canvas)245 void onDraw(SkCanvas* canvas) override {
246 const Proc procs[] = {
247 make0, make1, make2, make3,
248 };
249 const SkPoint pts[] = {
250 { 0, 0 },
251 { SkIntToScalar(W), 0 },
252 };
253 const SkRect r = SkRect::MakeWH(SkIntToScalar(W), 30);
254
255 SkPaint paint;
256 paint.setDither(fDither);
257
258 canvas->translate(40, 20);
259
260 for (int i = 0; i <= 8; ++i) {
261 SkScalar x = r.width() * i / 8;
262 canvas->drawLine(x, 0, x, 10000, paint);
263 }
264
265 // expand the drawing rect so we exercise clampping in the gradients
266 const SkRect drawR = r.makeOutset(20, 0);
267 for (size_t i = 0; i < std::size(procs); ++i) {
268 ColorPos rec;
269 procs[i](&rec);
270 paint.setShader(SkGradientShader::MakeLinear(pts, rec.fColors, rec.fPos, rec.fCount,
271 SkTileMode::kClamp));
272 canvas->drawRect(drawR, paint);
273
274 canvas->save();
275 canvas->translate(r.centerX(), r.height() + 4);
276 canvas->scale(-1, 1);
277 canvas->translate(-r.centerX(), 0);
278 canvas->drawRect(drawR, paint);
279 canvas->restore();
280
281 canvas->translate(0, r.height() + 2*r.height() + 8);
282 }
283 }
284
285 private:
286 bool fDither;
287
288 using INHERITED = GM;
289 };
290
291 ///////////////////////////////////////////////////////////////////////////////
292
293 DEF_GM(return new GradientsNoTextureGM(true);)
294 DEF_GM(return new GradientsNoTextureGM(false);)
295 DEF_GM(return new GradientsManyColorsGM(true);)
296 DEF_GM(return new GradientsManyColorsGM(false);)
297