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