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, NULL },
24    { 2, gColors, NULL },
25    { 3, gColors, NULL },
26    { 4, gColors, NULL },
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() {
84        this->setBGColor(0xFFDDDDDD);
85    }
86
87protected:
88
89    SkString onShortName() override { return SkString("gradients_no_texture"); }
90    SkISize onISize() override { return SkISize::Make(640, 615); }
91
92    void onDraw(SkCanvas* canvas) override {
93        static const SkPoint kPts[2] = { { 0, 0 },
94                                         { SkIntToScalar(50), SkIntToScalar(50) } };
95        static const SkShader::TileMode kTM = SkShader::kClamp_TileMode;
96        SkRect kRect = { 0, 0, SkIntToScalar(50), SkIntToScalar(50) };
97        SkPaint paint;
98        paint.setAntiAlias(true);
99
100        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
101        static const uint8_t kAlphas[] = { 0xff, 0x40 };
102        for (size_t a = 0; a < SK_ARRAY_COUNT(kAlphas); ++a) {
103            for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); ++i) {
104                canvas->save();
105                for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); ++j) {
106                    SkShader* shader = gGradMakers[j](kPts, gGradData[i], kTM);
107                    paint.setShader(shader)->unref();
108                    paint.setAlpha(kAlphas[a]);
109                    canvas->drawRect(kRect, paint);
110                    canvas->translate(0, SkIntToScalar(kRect.height() + 20));
111                }
112                canvas->restore();
113                canvas->translate(SkIntToScalar(kRect.width() + 20), 0);
114            }
115        }
116    }
117
118private:
119    typedef GM INHERITED;
120};
121
122///////////////////////////////////////////////////////////////////////////////
123
124struct ColorPos {
125    SkColor*    fColors;
126    SkScalar*   fPos;
127    int         fCount;
128
129    ColorPos() : fColors(NULL), fPos(NULL), fCount(0) {}
130    ~ColorPos() {
131        SkDELETE_ARRAY(fColors);
132        SkDELETE_ARRAY(fPos);
133    }
134
135    void construct(const SkColor colors[], const SkScalar pos[], int count) {
136        fColors = SkNEW_ARRAY(SkColor, count);
137        memcpy(fColors, colors, count * sizeof(SkColor));
138        if (pos) {
139            fPos = SkNEW_ARRAY(SkScalar, count);
140            memcpy(fPos, pos, count * sizeof(SkScalar));
141            fPos[0] = 0;
142            fPos[count - 1] = 1;
143        }
144        fCount = count;
145    }
146};
147
148static void make0(ColorPos* rec) {
149#if 0
150    From http://jsfiddle.net/3fe2a/
151
152background-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%);
153height: 30px;
154#endif
155
156    const SkColor colors[] = {
157        0xFF22d1cd, 0xFF22d1cd, 0xFFdf4b37, 0xFFdf4b37, 0xFF22d1cd, 0xFF22d1cd, 0xFFe6de36, 0xFFe6de36,
158        0xFF3267ff, 0xFF3267ff, 0xFF9d47d1, 0xFF9d47d1, 0xFF3267ff, 0xFF3267ff, 0xFF5cdd9d, 0xFF5cdd9d,
159        0xFF3267ff, 0xFF3267ff, 0xFF9d47d1, 0xFF9d47d1, 0xFF3267ff, 0xFF3267ff, 0xFFe3d082, 0xFFe3d082
160    };
161    const double percent[] = {
162        1, 0.9510157507590116, 2.9510157507590113, 23.695886056604927,
163        25.695886056604927, 25.39321881940624, 27.39321881940624, 31.849399922570655,
164        33.849399922570655, 44.57735802921938, 46.57735802921938, 53.27185850805876,
165        55.27185850805876, 61.95718972227316, 63.95718972227316, 69.89166004442,
166        71.89166004442, 74.45795382765857, 76.45795382765857, 82.78364610713776,
167        84.78364610713776, 94.52743647737229, 96.52743647737229, 96.03934633331295,
168    };
169    const int N = SK_ARRAY_COUNT(percent);
170    SkScalar pos[N];
171    for (int i = 0; i < N; ++i) {
172        pos[i] = SkDoubleToScalar(percent[i] / 100);
173    }
174    rec->construct(colors, pos, N);
175}
176
177static void make1(ColorPos* rec) {
178    const SkColor colors[] = {
179        SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
180        SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
181        SK_ColorBLACK,
182    };
183    rec->construct(colors, NULL, SK_ARRAY_COUNT(colors));
184}
185
186static void make2(ColorPos* rec) {
187    const SkColor colors[] = {
188        SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
189        SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE,
190        SK_ColorBLACK,
191    };
192    const int N = SK_ARRAY_COUNT(colors);
193    SkScalar pos[N];
194    for (int i = 0; i < N; ++i) {
195        pos[i] = SK_Scalar1 * i / (N - 1);
196    }
197    rec->construct(colors, pos, N);
198}
199
200class GradientsManyColorsGM : public GM {
201    enum {
202        W = 800,
203    };
204    SkAutoTUnref<SkShader> fShader;
205
206    typedef void (*Proc)(ColorPos*);
207public:
208    GradientsManyColorsGM() {}
209
210protected:
211
212    SkString onShortName() override { return SkString("gradients_many"); }
213    SkISize onISize() override { return SkISize::Make(850, 100); }
214
215    void onDraw(SkCanvas* canvas) override {
216        const Proc procs[] = {
217            make0, make1, make2,
218        };
219        const SkPoint pts[] = {
220            { 0, 0 },
221            { SkIntToScalar(W), 0 },
222        };
223        const SkRect r = SkRect::MakeWH(SkIntToScalar(W), 30);
224
225        SkPaint paint;
226
227        canvas->translate(20, 20);
228
229        for (int i = 0; i <= 8; ++i) {
230            SkScalar x = r.width() * i / 8;
231            canvas->drawLine(x, 0, x, 10000, paint);
232        }
233
234        for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
235            ColorPos rec;
236            procs[i](&rec);
237            SkShader* s = SkGradientShader::CreateLinear(pts, rec.fColors, rec.fPos, rec.fCount,
238                                                         SkShader::kClamp_TileMode);
239            paint.setShader(s)->unref();
240            canvas->drawRect(r, paint);
241            canvas->translate(0, r.height() + 20);
242        }
243    }
244
245private:
246    typedef GM INHERITED;
247};
248
249///////////////////////////////////////////////////////////////////////////////
250
251DEF_GM( return SkNEW(GradientsNoTextureGM));
252DEF_GM( return SkNEW(GradientsManyColorsGM));
253