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