1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "gm.h"
9#include "SkGradientShader.h"
10
11namespace skiagm {
12
13struct GradData {
14    int             fCount;
15    const SkColor*  fColors;
16    const SkScalar* fPos;
17};
18
19static const SkColor gColors[] = {
20    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
21};
22static const SkScalar gPos0[] = { 0, SK_Scalar1 };
23static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
24static const SkScalar gPos2[] = {
25    0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
26};
27
28static const GradData gGradData[] = {
29    { 2, gColors, NULL },
30    { 2, gColors, gPos0 },
31    { 2, gColors, gPos1 },
32    { 5, gColors, NULL },
33    { 5, gColors, gPos2 }
34};
35
36static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
37                            SkShader::TileMode tm, SkUnitMapper* mapper) {
38    return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
39                                          data.fCount, tm, mapper);
40}
41
42static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
43                            SkShader::TileMode tm, SkUnitMapper* mapper) {
44    SkPoint center;
45    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
46               SkScalarAve(pts[0].fY, pts[1].fY));
47    return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
48                                          data.fPos, data.fCount, tm, mapper);
49}
50
51static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
52                           SkShader::TileMode tm, SkUnitMapper* mapper) {
53    SkPoint center;
54    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
55               SkScalarAve(pts[0].fY, pts[1].fY));
56    return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
57                                         data.fPos, data.fCount, mapper);
58}
59
60static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
61                             SkShader::TileMode tm, SkUnitMapper* mapper) {
62    SkPoint center0, center1;
63    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
64                SkScalarAve(pts[0].fY, pts[1].fY));
65    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
66                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
67    return SkGradientShader::CreateTwoPointRadial(
68                                                  center1, (pts[1].fX - pts[0].fX) / 7,
69                                                  center0, (pts[1].fX - pts[0].fX) / 2,
70                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
71}
72
73typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
74                               SkShader::TileMode tm, SkUnitMapper* mapper);
75static const GradMaker gGradMakers[] = {
76    MakeLinear, MakeRadial, MakeSweep, Make2Radial
77};
78
79///////////////////////////////////////////////////////////////////////////////
80
81class GradientsGM : public GM {
82public:
83	GradientsGM() {
84        this->setBGColor(0xFFDDDDDD);
85    }
86
87protected:
88    SkString onShortName() {
89        return SkString("gradients");
90    }
91
92    virtual SkISize onISize() { return make_isize(640, 510); }
93
94    virtual void onDraw(SkCanvas* canvas) {
95
96        SkPoint pts[2] = {
97            { 0, 0 },
98            { SkIntToScalar(100), SkIntToScalar(100) }
99        };
100        SkShader::TileMode tm = SkShader::kClamp_TileMode;
101        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
102        SkPaint paint;
103        paint.setAntiAlias(true);
104
105        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
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](pts, gGradData[i], tm, NULL);
110                paint.setShader(shader);
111                canvas->drawRect(r, paint);
112                shader->unref();
113                canvas->translate(0, SkIntToScalar(120));
114            }
115            canvas->restore();
116            canvas->translate(SkIntToScalar(120), 0);
117        }
118    }
119
120private:
121    typedef GM INHERITED;
122};
123
124/*
125 Inspired by this <canvas> javascript, where we need to detect that we are not
126 solving a quadratic equation, but must instead solve a linear (since our X^2
127 coefficient is 0)
128
129 ctx.fillStyle = '#f00';
130 ctx.fillRect(0, 0, 100, 50);
131
132 var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
133 g.addColorStop(0, '#f00');
134 g.addColorStop(0.01, '#0f0');
135 g.addColorStop(0.99, '#0f0');
136 g.addColorStop(1, '#f00');
137 ctx.fillStyle = g;
138 ctx.fillRect(0, 0, 100, 50);
139 */
140class GradientsDegenrate2PointGM : public GM {
141public:
142    GradientsDegenrate2PointGM() {}
143
144protected:
145    SkString onShortName() {
146        return SkString("gradients_degenerate_2pt");
147    }
148
149	virtual SkISize onISize() { return make_isize(320, 320); }
150
151    void drawBG(SkCanvas* canvas) {
152        canvas->drawColor(SK_ColorBLUE);
153    }
154
155    virtual void onDraw(SkCanvas* canvas) {
156        this->drawBG(canvas);
157
158        SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
159        SkScalar pos[] = { 0, SkFloatToScalar(0.01f), SkFloatToScalar(0.99f), SK_Scalar1 };
160        SkPoint c0;
161        c0.iset(-80, 25);
162        SkScalar r0 = SkIntToScalar(70);
163        SkPoint c1;
164        c1.iset(0, 25);
165        SkScalar r1 = SkIntToScalar(150);
166        SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
167                                                             pos, SK_ARRAY_COUNT(pos),
168                                                             SkShader::kClamp_TileMode);
169        SkPaint paint;
170        paint.setShader(s)->unref();
171        canvas->drawPaint(paint);
172    }
173
174private:
175    typedef GM INHERITED;
176};
177
178/// Tests correctness of *optimized* codepaths in gradients.
179
180class ClampedGradientsGM : public GM {
181public:
182    ClampedGradientsGM() {}
183
184protected:
185    SkString onShortName() { return SkString("clamped_gradients"); }
186
187    virtual SkISize onISize() { return make_isize(640, 510); }
188
189    void drawBG(SkCanvas* canvas) {
190        canvas->drawColor(0xFFDDDDDD);
191    }
192
193    virtual void onDraw(SkCanvas* canvas) {
194        this->drawBG(canvas);
195
196        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
197        SkPaint paint;
198        paint.setAntiAlias(true);
199
200        SkPoint center;
201        center.iset(0, 300);
202        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
203        SkShader* shader = SkGradientShader::CreateRadial(
204            SkPoint(center),
205            SkIntToScalar(200), gColors, NULL, 5,
206            SkShader::kClamp_TileMode, NULL);
207        paint.setShader(shader);
208        canvas->drawRect(r, paint);
209        shader->unref();
210    }
211
212private:
213    typedef GM INHERITED;
214};
215
216/// Checks quality of large radial gradients, which may display
217/// some banding.
218
219class RadialGradientGM : public GM {
220public:
221    RadialGradientGM() {}
222
223protected:
224    SkString onShortName() { return SkString("radial_gradient"); }
225    virtual SkISize onISize() { return make_isize(1280, 1280); }
226    void drawBG(SkCanvas* canvas) {
227        canvas->drawColor(0xFF000000);
228    }
229    virtual void onDraw(SkCanvas* canvas) {
230        const SkISize dim = this->getISize();
231
232        this->drawBG(canvas);
233
234        SkPaint paint;
235        paint.setDither(true);
236        SkPoint center;
237        center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
238        SkScalar radius = SkIntToScalar(dim.width())/2;
239        const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
240        const SkScalar pos[] = { SkFloatToScalar(0.0),
241                             SkFloatToScalar(0.35),
242                             SkFloatToScalar(1.0) };
243        SkShader* shader =
244            SkGradientShader::CreateRadial(center, radius, colors,
245                                           pos, SK_ARRAY_COUNT(pos),
246                                           SkShader::kClamp_TileMode);
247        paint.setShader(shader)->unref();
248        SkRect r = {
249            0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
250        };
251        canvas->drawRect(r, paint);
252    }
253private:
254    typedef GM INHERITED;
255};
256
257
258
259///////////////////////////////////////////////////////////////////////////////
260
261static GM* MyFactory(void*) { return new GradientsGM; }
262static GMRegistry reg(MyFactory);
263
264static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; }
265static GMRegistry reg2(MyFactory2);
266
267static GM* MyFactory3(void*) { return new ClampedGradientsGM; }
268static GMRegistry reg3(MyFactory3);
269
270static GM* MyFactory4(void*) { return new RadialGradientGM; }
271static GMRegistry reg4(MyFactory4);
272}
273
274