1/*
2 * Copyright 2014 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
8#include "gm.h"
9#include "sk_tool_utils.h"
10#include "SkGradientShader.h"
11
12namespace skiagm {
13
14struct GradData {
15    int             fCount;
16    const SkColor*  fColors;
17    const SkScalar* fPos;
18};
19
20constexpr SkColor gColors[] = {
21    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
22};
23constexpr SkScalar gPos0[] = { 0, SK_Scalar1 };
24constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
25constexpr SkScalar gPos2[] = {
26    0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
27};
28
29constexpr SkScalar gPosClamp[]   = {0.0f, 0.0f, 1.0f, 1.0f};
30constexpr SkColor  gColorClamp[] = {
31    SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
32};
33
34constexpr GradData gGradData[] = {
35    { 2, gColors, gPos0 },
36    { 2, gColors, gPos1 },
37    { 5, gColors, gPos2 },
38    { 4, gColorClamp, gPosClamp }
39};
40
41static sk_sp<SkShader> Make2ConicalOutside(const SkPoint pts[2], const GradData& data,
42                                           SkShader::TileMode tm, const SkMatrix& localMatrix) {
43    SkPoint center0, center1;
44    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
45    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
46    center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
47    center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
48    return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors,
49                                                 data.fPos, data.fCount, tm, 0, &localMatrix);
50}
51
52static sk_sp<SkShader> Make2ConicalOutsideFlip(const SkPoint pts[2], const GradData& data,
53                             SkShader::TileMode tm, const SkMatrix& localMatrix) {
54    SkPoint center0, center1;
55    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
56    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
57    center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
58    center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
59    return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0, data.fColors,
60                                                 data.fPos, data.fCount, tm, 0, &localMatrix);
61}
62
63static sk_sp<SkShader> Make2ConicalInside(const SkPoint pts[2], const GradData& data,
64                                          SkShader::TileMode tm, const SkMatrix& localMatrix) {
65    SkPoint center0, center1;
66    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
67                SkScalarAve(pts[0].fY, pts[1].fY));
68    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
69                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
70    return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
71                                                 center0, (pts[1].fX - pts[0].fX) / 2,
72                                                 data.fColors, data.fPos, data.fCount, tm,
73                                                 0, &localMatrix);
74}
75
76static sk_sp<SkShader> Make2ConicalInsideFlip(const SkPoint pts[2], const GradData& data,
77                                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
78    SkPoint center0, center1;
79    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
80                SkScalarAve(pts[0].fY, pts[1].fY));
81    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
82                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
83    return SkGradientShader::MakeTwoPointConical(center0, (pts[1].fX - pts[0].fX) / 2,
84                                                 center1, (pts[1].fX - pts[0].fX) / 7,
85                                                 data.fColors, data.fPos, data.fCount, tm,
86                                                 0, &localMatrix);
87}
88
89static sk_sp<SkShader> Make2ConicalInsideCenter(const SkPoint pts[2], const GradData& data,
90                             SkShader::TileMode tm, const SkMatrix& localMatrix) {
91    SkPoint center0, center1;
92    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
93                SkScalarAve(pts[0].fY, pts[1].fY));
94    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
95                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
96    return SkGradientShader::MakeTwoPointConical(center0, (pts[1].fX - pts[0].fX) / 7,
97                                                 center0, (pts[1].fX - pts[0].fX) / 2,
98                                                 data.fColors, data.fPos, data.fCount, tm,
99                                                 0, &localMatrix);
100}
101
102static sk_sp<SkShader> Make2ConicalZeroRad(const SkPoint pts[2], const GradData& data,
103                                           SkShader::TileMode tm, const SkMatrix& localMatrix) {
104    SkPoint center0, center1;
105    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
106                SkScalarAve(pts[0].fY, pts[1].fY));
107    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
108                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
109    return SkGradientShader::MakeTwoPointConical(center1, 0.f,
110                                                 center0, (pts[1].fX - pts[0].fX) / 2,
111                                                 data.fColors, data.fPos, data.fCount, tm,
112                                                 0, &localMatrix);
113}
114
115static sk_sp<SkShader> Make2ConicalZeroRadFlip(const SkPoint pts[2], const GradData& data,
116                                               SkShader::TileMode tm, const SkMatrix& localMatrix) {
117    SkPoint center0, center1;
118    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
119                SkScalarAve(pts[0].fY, pts[1].fY));
120    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
121                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
122    return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 2,
123                                                 center0, 0.f,
124                                                 data.fColors, data.fPos, data.fCount, tm,
125                                                 0, &localMatrix);
126}
127
128static sk_sp<SkShader> Make2ConicalZeroRadCenter(const SkPoint pts[2], const GradData& data,
129                             SkShader::TileMode tm, const SkMatrix& localMatrix) {
130    SkPoint center0, center1;
131    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
132                SkScalarAve(pts[0].fY, pts[1].fY));
133    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
134                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
135    return SkGradientShader::MakeTwoPointConical(center0, 0.f, center0, (pts[1].fX - pts[0].fX) / 2,
136                                                 data.fColors, data.fPos, data.fCount, tm,
137                                                 0, &localMatrix);
138}
139
140static sk_sp<SkShader> Make2ConicalZeroRadOutside(const SkPoint pts[2], const GradData& data,
141                                                  SkShader::TileMode tm,
142                                                  const SkMatrix& localMatrix) {
143    SkPoint center0, center1;
144    SkScalar radius0 = 0.f;
145    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
146    center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
147    center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
148    return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1,
149                                                 data.fColors, data.fPos,
150                                                 data.fCount, tm, 0, &localMatrix);
151}
152
153static sk_sp<SkShader> Make2ConicalZeroRadFlipOutside(const SkPoint pts[2], const GradData& data,
154                                                      SkShader::TileMode tm,
155                                                      const SkMatrix& localMatrix) {
156    SkPoint center0, center1;
157    SkScalar radius0 = 0.f;
158    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
159    center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
160    center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
161    return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0, data.fColors,
162                                                 data.fPos, data.fCount, tm, 0, &localMatrix);
163}
164
165static sk_sp<SkShader> Make2ConicalEdgeX(const SkPoint pts[2], const GradData& data,
166                                         SkShader::TileMode tm, const SkMatrix& localMatrix) {
167    SkPoint center0, center1;
168    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7;
169    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
170    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
171                SkScalarAve(pts[0].fY, pts[1].fY));
172    center0.set(center1.fX + radius1, center1.fY);
173    return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors,
174                                                 data.fPos, data.fCount, tm, 0, &localMatrix);
175}
176
177static sk_sp<SkShader> Make2ConicalEdgeY(const SkPoint pts[2], const GradData& data,
178                                         SkShader::TileMode tm, const SkMatrix& localMatrix) {
179    SkPoint center0, center1;
180    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7;
181    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
182    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
183                SkScalarAve(pts[0].fY, pts[1].fY));
184    center0.set(center1.fX, center1.fY + radius1);
185    return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors,
186                                                 data.fPos, data.fCount, tm, 0, &localMatrix);
187}
188
189static sk_sp<SkShader> Make2ConicalZeroRadEdgeX(const SkPoint pts[2], const GradData& data,
190                                                SkShader::TileMode tm,
191                                                const SkMatrix& localMatrix) {
192    SkPoint center0, center1;
193    SkScalar radius0 = 0.f;
194    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
195    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
196                SkScalarAve(pts[0].fY, pts[1].fY));
197    center0.set(center1.fX + radius1, center1.fY);
198    return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors,
199                                                 data.fPos, data.fCount, tm, 0, &localMatrix);
200}
201
202static sk_sp<SkShader> Make2ConicalZeroRadEdgeY(const SkPoint pts[2], const GradData& data,
203                                                SkShader::TileMode tm, const SkMatrix& localMatrix) {
204    SkPoint center0, center1;
205    SkScalar radius0 = 0.f;
206    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
207    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
208                SkScalarAve(pts[0].fY, pts[1].fY));
209    center0.set(center1.fX, center1.fY + radius1);
210    return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors,
211                                                 data.fPos, data.fCount, tm, 0, &localMatrix);
212}
213
214static sk_sp<SkShader> Make2ConicalTouchX(const SkPoint pts[2], const GradData& data,
215                                          SkShader::TileMode tm, const SkMatrix& localMatrix) {
216    SkPoint center0, center1;
217    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7;
218    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
219    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
220                SkScalarAve(pts[0].fY, pts[1].fY));
221    center0.set(center1.fX - radius1 + radius0, center1.fY);
222    return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors,
223                                                 data.fPos, data.fCount, tm, 0, &localMatrix);
224}
225
226static sk_sp<SkShader> Make2ConicalTouchY(const SkPoint pts[2], const GradData& data,
227                                          SkShader::TileMode tm, const SkMatrix& localMatrix) {
228    SkPoint center0, center1;
229    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7;
230    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
231    center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
232                SkScalarAve(pts[0].fY, pts[1].fY));
233    center0.set(center1.fX, center1.fY + radius1 - radius0);
234    return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors,
235                                                 data.fPos, data.fCount, tm, 0, &localMatrix);
236}
237
238static sk_sp<SkShader> Make2ConicalInsideSmallRad(const SkPoint pts[2], const GradData& data,
239                             SkShader::TileMode tm, const SkMatrix& localMatrix) {
240    SkPoint center0, center1;
241    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
242                SkScalarAve(pts[0].fY, pts[1].fY));
243    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
244                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
245    return SkGradientShader::MakeTwoPointConical(center0, 0.0000000000000000001f,
246                                                   center0, (pts[1].fX - pts[0].fX) / 2,
247                                                   data.fColors, data.fPos, data.fCount, tm,
248                                                   0, &localMatrix);
249}
250
251typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data,
252                                     SkShader::TileMode tm, const SkMatrix& localMatrix);
253
254constexpr GradMaker gGradMakersOutside[] = {
255    Make2ConicalOutside, Make2ConicalOutsideFlip,
256    Make2ConicalZeroRadOutside, Make2ConicalZeroRadFlipOutside
257};
258
259constexpr GradMaker gGradMakersInside[] = {
260    Make2ConicalInside, Make2ConicalInsideFlip, Make2ConicalInsideCenter,
261    Make2ConicalZeroRad, Make2ConicalZeroRadFlip, Make2ConicalZeroRadCenter,
262};
263
264constexpr GradMaker gGradMakersEdgeCases[] = {
265    Make2ConicalEdgeX, Make2ConicalEdgeY,
266    Make2ConicalZeroRadEdgeX, Make2ConicalZeroRadEdgeY,
267    Make2ConicalTouchX, Make2ConicalTouchY,
268    Make2ConicalInsideSmallRad
269};
270
271
272constexpr struct {
273    const GradMaker*   fMaker;
274    const int fCount;
275    const char* fName;
276} gGradCases[] = {
277    { gGradMakersOutside,   SK_ARRAY_COUNT(gGradMakersOutside),     "outside"  },
278    { gGradMakersInside,    SK_ARRAY_COUNT(gGradMakersInside),      "inside"  },
279    { gGradMakersEdgeCases, SK_ARRAY_COUNT(gGradMakersEdgeCases),   "edge"  },
280};
281
282enum GradCaseType { // these must match the order in gGradCases
283    kOutside_GradCaseType,
284    kInside_GradCaseType,
285    kEdge_GradCaseType,
286};
287
288///////////////////////////////////////////////////////////////////////////////
289
290class ConicalGradientsGM : public GM {
291public:
292    ConicalGradientsGM(GradCaseType gradCaseType, bool dither)
293        : fGradCaseType(gradCaseType)
294        , fDither(dither) {
295        this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD));
296        fName.printf("gradients_2pt_conical_%s%s", gGradCases[gradCaseType].fName,
297                     fDither ? "" : "_nodither");
298    }
299
300protected:
301    SkString onShortName() {
302        return fName;
303    }
304
305    virtual SkISize onISize() { return SkISize::Make(840, 815); }
306
307    virtual void onDraw(SkCanvas* canvas) {
308
309        SkPoint pts[2] = {
310            { 0, 0 },
311            { SkIntToScalar(100), SkIntToScalar(100) }
312        };
313        SkShader::TileMode tm = SkShader::kClamp_TileMode;
314        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
315        SkPaint paint;
316        paint.setAntiAlias(true);
317        paint.setDither(fDither);
318
319        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
320
321        const GradMaker* gradMaker = gGradCases[fGradCaseType].fMaker;
322        const int count = gGradCases[fGradCaseType].fCount;
323
324        for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
325            canvas->save();
326            for (int j = 0; j < count; j++) {
327                SkMatrix scale = SkMatrix::I();
328
329                if (i == 3) { // if the clamp case
330                    scale.setScale(0.5f, 0.5f);
331                    scale.postTranslate(25.f, 25.f);
332                }
333
334                paint.setShader(gradMaker[j](pts, gGradData[i], tm, scale));
335                canvas->drawRect(r, paint);
336                canvas->translate(0, SkIntToScalar(120));
337            }
338            canvas->restore();
339            canvas->translate(SkIntToScalar(120), 0);
340        }
341    }
342
343private:
344    typedef GM INHERITED;
345
346    GradCaseType fGradCaseType;
347    SkString fName;
348    bool fDither;
349};
350///////////////////////////////////////////////////////////////////////////////
351
352DEF_GM( return new ConicalGradientsGM(kInside_GradCaseType, true); )
353DEF_GM( return new ConicalGradientsGM(kOutside_GradCaseType, true); )
354DEF_GM( return new ConicalGradientsGM(kEdge_GradCaseType, true); )
355
356DEF_GM( return new ConicalGradientsGM(kInside_GradCaseType, false); )
357DEF_GM( return new ConicalGradientsGM(kOutside_GradCaseType, false); )
358DEF_GM( return new ConicalGradientsGM(kEdge_GradCaseType, false); )
359
360}
361