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:
102
onShortName()103 SkString onShortName() override {
104 return SkString(fDither ? "gradients_no_texture" : "gradients_no_texture_nodither");
105 }
106
onISize()107 SkISize onISize() override { return SkISize::Make(640, 615); }
108
onDraw(SkCanvas * canvas)109 void onDraw(SkCanvas* canvas) override {
110 constexpr SkPoint kPts[2] = { { 0, 0 },
111 { SkIntToScalar(50), SkIntToScalar(50) } };
112 constexpr SkTileMode kTM = SkTileMode::kClamp;
113 SkRect kRect = { 0, 0, SkIntToScalar(50), SkIntToScalar(50) };
114 SkPaint paint;
115 paint.setAntiAlias(true);
116 paint.setDither(fDither);
117
118 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
119 constexpr uint8_t kAlphas[] = { 0xff, 0x40 };
120 for (size_t a = 0; a < SK_ARRAY_COUNT(kAlphas); ++a) {
121 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); ++i) {
122 canvas->save();
123 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); ++j) {
124 paint.setShader(gGradMakers[j](kPts, gGradData[i], kTM));
125 paint.setAlpha(kAlphas[a]);
126 canvas->drawRect(kRect, paint);
127 canvas->translate(0, SkIntToScalar(kRect.height() + 20));
128 }
129 canvas->restore();
130 canvas->translate(SkIntToScalar(kRect.width() + 20), 0);
131 }
132 }
133 }
134
135 private:
136 bool fDither;
137
138 typedef GM INHERITED;
139 };
140
141 ///////////////////////////////////////////////////////////////////////////////
142
143 struct ColorPos {
144 SkColor* fColors;
145 SkScalar* fPos;
146 int fCount;
147
ColorPosColorPos148 ColorPos() : fColors(nullptr), fPos(nullptr), fCount(0) {}
~ColorPosColorPos149 ~ColorPos() {
150 delete[] fColors;
151 delete[] fPos;
152 }
153
constructColorPos154 void construct(const SkColor colors[], const SkScalar pos[], int count) {
155 fColors = new SkColor[count];
156 memcpy(fColors, colors, count * sizeof(SkColor));
157 if (pos) {
158 fPos = new SkScalar[count];
159 memcpy(fPos, pos, count * sizeof(SkScalar));
160 fPos[0] = 0;
161 fPos[count - 1] = 1;
162 }
163 fCount = count;
164 }
165 };
166
make0(ColorPos * rec)167 static void make0(ColorPos* rec) {
168 #if 0
169 From http://jsfiddle.net/3fe2a/
170
171 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%);
172 height: 30px;
173 #endif
174
175 const SkColor colors[] = {
176 0xFF22d1cd, 0xFF22d1cd, 0xFFdf4b37, 0xFFdf4b37, 0xFF22d1cd, 0xFF22d1cd, 0xFFe6de36, 0xFFe6de36,
177 0xFF3267ff, 0xFF3267ff, 0xFF9d47d1, 0xFF9d47d1, 0xFF3267ff, 0xFF3267ff, 0xFF5cdd9d, 0xFF5cdd9d,
178 0xFF3267ff, 0xFF3267ff, 0xFF9d47d1, 0xFF9d47d1, 0xFF3267ff, 0xFF3267ff, 0xFFe3d082, 0xFFe3d082
179 };
180 const double percent[] = {
181 1, 0.9510157507590116, 2.9510157507590113, 23.695886056604927,
182 25.695886056604927, 25.39321881940624, 27.39321881940624, 31.849399922570655,
183 33.849399922570655, 44.57735802921938, 46.57735802921938, 53.27185850805876,
184 55.27185850805876, 61.95718972227316, 63.95718972227316, 69.89166004442,
185 71.89166004442, 74.45795382765857, 76.45795382765857, 82.78364610713776,
186 84.78364610713776, 94.52743647737229, 96.52743647737229, 96.03934633331295,
187 };
188 const int N = SK_ARRAY_COUNT(percent);
189 SkScalar pos[N];
190 for (int i = 0; i < N; ++i) {
191 pos[i] = SkDoubleToScalar(percent[i] / 100);
192 }
193 rec->construct(colors, pos, N);
194 }
195
make1(ColorPos * rec)196 static void make1(ColorPos* rec) {
197 const SkColor colors[] = {
198 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
199 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
200 SK_ColorBLACK,
201 };
202 rec->construct(colors, nullptr, SK_ARRAY_COUNT(colors));
203 }
204
make2(ColorPos * rec)205 static void make2(ColorPos* rec) {
206 const SkColor colors[] = {
207 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
208 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
209 SK_ColorBLACK,
210 };
211 const int N = SK_ARRAY_COUNT(colors);
212 SkScalar pos[N];
213 for (int i = 0; i < N; ++i) {
214 pos[i] = SK_Scalar1 * i / (N - 1);
215 }
216 rec->construct(colors, pos, N);
217 }
218
make3(ColorPos * rec)219 static void make3(ColorPos* rec) {
220 const SkColor colors[] = {
221 SK_ColorRED, SK_ColorBLUE, SK_ColorBLUE, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLACK,
222 };
223 const SkScalar pos[] = {
224 0, 0, 0.5f, 0.5, 1, 1,
225 };
226 rec->construct(colors, pos, SK_ARRAY_COUNT(colors));
227 }
228
229 class GradientsManyColorsGM : public GM {
230 enum {
231 W = 800,
232 };
233 sk_sp<SkShader> fShader;
234
235 typedef void (*Proc)(ColorPos*);
236 public:
GradientsManyColorsGM(bool dither)237 GradientsManyColorsGM(bool dither) : fDither(dither) {}
238
239 protected:
240
onShortName()241 SkString onShortName() override {
242 return SkString(fDither ? "gradients_many" : "gradients_many_nodither");
243 }
244
onISize()245 SkISize onISize() override { return SkISize::Make(880, 400); }
246
onDraw(SkCanvas * canvas)247 void onDraw(SkCanvas* canvas) override {
248 const Proc procs[] = {
249 make0, make1, make2, make3,
250 };
251 const SkPoint pts[] = {
252 { 0, 0 },
253 { SkIntToScalar(W), 0 },
254 };
255 const SkRect r = SkRect::MakeWH(SkIntToScalar(W), 30);
256
257 SkPaint paint;
258 paint.setDither(fDither);
259
260 canvas->translate(40, 20);
261
262 for (int i = 0; i <= 8; ++i) {
263 SkScalar x = r.width() * i / 8;
264 canvas->drawLine(x, 0, x, 10000, paint);
265 }
266
267 // expand the drawing rect so we exercise clampping in the gradients
268 const SkRect drawR = r.makeOutset(20, 0);
269 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
270 ColorPos rec;
271 procs[i](&rec);
272 paint.setShader(SkGradientShader::MakeLinear(pts, rec.fColors, rec.fPos, rec.fCount,
273 SkTileMode::kClamp));
274 canvas->drawRect(drawR, paint);
275
276 canvas->save();
277 canvas->translate(r.centerX(), r.height() + 4);
278 canvas->scale(-1, 1);
279 canvas->translate(-r.centerX(), 0);
280 canvas->drawRect(drawR, paint);
281 canvas->restore();
282
283 canvas->translate(0, r.height() + 2*r.height() + 8);
284 }
285 }
286
287 private:
288 bool fDither;
289
290 typedef GM INHERITED;
291 };
292
293 ///////////////////////////////////////////////////////////////////////////////
294
295 DEF_GM(return new GradientsNoTextureGM(true);)
296 DEF_GM(return new GradientsNoTextureGM(false);)
297 DEF_GM(return new GradientsManyColorsGM(true);)
298 DEF_GM(return new GradientsManyColorsGM(false);)
299