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