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