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