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