• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 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 "SkGradientShader.h"
10 
11 namespace skiagm {
12 
13 struct GradData {
14     int             fCount;
15     const SkColor*  fColors;
16     const SkScalar* fPos;
17 };
18 
19 static const SkColor gColors[] = {
20     SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
21 };
22 static const SkScalar gPos0[] = { 0, SK_Scalar1 };
23 static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
24 static const SkScalar gPos2[] = {
25     0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
26 };
27 
28 static const SkScalar gPosClamp[]   = {0.0f, 0.0f, 1.0f, 1.0f};
29 static const SkColor  gColorClamp[] = {
30     SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
31 };
32 
33 static const GradData gGradData[] = {
34     { 2, gColors, NULL },
35     { 2, gColors, gPos0 },
36     { 2, gColors, gPos1 },
37     { 5, gColors, NULL },
38     { 5, gColors, gPos2 },
39     { 4, gColorClamp, gPosClamp }
40 };
41 
MakeLinear(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)42 static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
43                             SkShader::TileMode tm, const SkMatrix& localMatrix) {
44     return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
45                                           data.fCount, tm, 0, &localMatrix);
46 }
47 
MakeRadial(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)48 static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
49                             SkShader::TileMode tm, const SkMatrix& localMatrix) {
50     SkPoint center;
51     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
52                SkScalarAve(pts[0].fY, pts[1].fY));
53     return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
54                                           data.fPos, data.fCount, tm, 0, &localMatrix);
55 }
56 
MakeSweep(const SkPoint pts[2],const GradData & data,SkShader::TileMode,const SkMatrix & localMatrix)57 static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
58                            SkShader::TileMode, const SkMatrix& localMatrix) {
59     SkPoint center;
60     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
61                SkScalarAve(pts[0].fY, pts[1].fY));
62     return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
63                                          data.fPos, data.fCount, 0, &localMatrix);
64 }
65 
Make2Radial(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)66 static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
67                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
68     SkPoint center0, center1;
69     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
70                 SkScalarAve(pts[0].fY, pts[1].fY));
71     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
72                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
73     return SkGradientShader::CreateTwoPointConical(
74                                                    center1, (pts[1].fX - pts[0].fX) / 7,
75                                                    center0, (pts[1].fX - pts[0].fX) / 2,
76                                                    data.fColors, data.fPos, data.fCount, tm,
77                                                    0, &localMatrix);
78 }
79 
Make2Conical(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,const SkMatrix & localMatrix)80 static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data,
81                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
82     SkPoint center0, center1;
83     SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
84     SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
85     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
86     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
87     return SkGradientShader::CreateTwoPointConical(center1, radius1,
88                                                    center0, radius0,
89                                                    data.fColors, data.fPos,
90                                                    data.fCount, tm, 0, &localMatrix);
91 }
92 
93 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
94                                SkShader::TileMode tm, const SkMatrix& localMatrix);
95 static const GradMaker gGradMakers[] = {
96     MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
97 };
98 
99 ///////////////////////////////////////////////////////////////////////////////
100 
101 class GradientsGM : public GM {
102 public:
GradientsGM()103     GradientsGM() {
104         this->setBGColor(0xFFDDDDDD);
105     }
106 
107 protected:
108 
onShortName()109     SkString onShortName() {
110         return SkString("gradients");
111     }
112 
onISize()113     virtual SkISize onISize() { return SkISize::Make(840, 815); }
114 
onDraw(SkCanvas * canvas)115     virtual void onDraw(SkCanvas* canvas) {
116 
117         SkPoint pts[2] = {
118             { 0, 0 },
119             { SkIntToScalar(100), SkIntToScalar(100) }
120         };
121         SkShader::TileMode tm = SkShader::kClamp_TileMode;
122         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
123         SkPaint paint;
124         paint.setAntiAlias(true);
125 
126         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
127         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
128             canvas->save();
129             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
130                 SkMatrix scale = SkMatrix::I();
131 
132                 if (i == 5) { // if the clamp case
133                     scale.setScale(0.5f, 0.5f);
134                     scale.postTranslate(25.f, 25.f);
135                 }
136 
137                 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, scale);
138 
139                 paint.setShader(shader);
140                 canvas->drawRect(r, paint);
141                 shader->unref();
142                 canvas->translate(0, SkIntToScalar(120));
143             }
144             canvas->restore();
145             canvas->translate(SkIntToScalar(120), 0);
146         }
147     }
148 
149 private:
150     typedef GM INHERITED;
151 };
152 DEF_GM( return new GradientsGM; )
153 
154 // Based on the original gradient slide, but with perspective applied to the
155 // gradient shaders' local matrices
156 class GradientsLocalPerspectiveGM : public GM {
157 public:
GradientsLocalPerspectiveGM()158     GradientsLocalPerspectiveGM() {
159         this->setBGColor(0xFFDDDDDD);
160     }
161 
162 protected:
163 
onShortName()164     SkString onShortName() {
165         return SkString("gradients_local_perspective");
166     }
167 
onISize()168     virtual SkISize onISize() { return SkISize::Make(840, 815); }
169 
onDraw(SkCanvas * canvas)170     virtual void onDraw(SkCanvas* canvas) {
171 
172         SkPoint pts[2] = {
173             { 0, 0 },
174             { SkIntToScalar(100), SkIntToScalar(100) }
175         };
176         SkShader::TileMode tm = SkShader::kClamp_TileMode;
177         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
178         SkPaint paint;
179         paint.setAntiAlias(true);
180 
181         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
182         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
183             canvas->save();
184             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
185                 // apply an increasing y perspective as we move to the right
186                 SkMatrix perspective;
187                 perspective.setIdentity();
188                 perspective.setPerspY(SkIntToScalar(i+1) / 500);
189                 perspective.setSkewX(SkIntToScalar(i+1) / 10);
190 
191                 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, perspective);
192 
193                 paint.setShader(shader);
194                 canvas->drawRect(r, paint);
195                 shader->unref();
196                 canvas->translate(0, SkIntToScalar(120));
197             }
198             canvas->restore();
199             canvas->translate(SkIntToScalar(120), 0);
200         }
201     }
202 
203 private:
204     typedef GM INHERITED;
205 };
206 DEF_GM( return new GradientsLocalPerspectiveGM; )
207 
208 // Based on the original gradient slide, but with perspective applied to
209 // the view matrix
210 class GradientsViewPerspectiveGM : public GradientsGM {
211 protected:
onShortName()212     SkString onShortName() {
213         return SkString("gradients_view_perspective");
214     }
215 
onISize()216     virtual SkISize onISize() { return SkISize::Make(840, 500); }
217 
onDraw(SkCanvas * canvas)218     virtual void onDraw(SkCanvas* canvas) {
219         SkMatrix perspective;
220         perspective.setIdentity();
221         perspective.setPerspY(0.001f);
222         perspective.setSkewX(SkIntToScalar(8) / 25);
223         canvas->concat(perspective);
224         INHERITED::onDraw(canvas);
225     }
226 
227 private:
228     typedef GradientsGM INHERITED;
229 };
230 DEF_GM( return new GradientsViewPerspectiveGM; )
231 
232 /*
233  Inspired by this <canvas> javascript, where we need to detect that we are not
234  solving a quadratic equation, but must instead solve a linear (since our X^2
235  coefficient is 0)
236 
237  ctx.fillStyle = '#f00';
238  ctx.fillRect(0, 0, 100, 50);
239 
240  var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
241  g.addColorStop(0, '#f00');
242  g.addColorStop(0.01, '#0f0');
243  g.addColorStop(0.99, '#0f0');
244  g.addColorStop(1, '#f00');
245  ctx.fillStyle = g;
246  ctx.fillRect(0, 0, 100, 50);
247  */
248 class GradientsDegenrate2PointGM : public GM {
249 public:
GradientsDegenrate2PointGM()250     GradientsDegenrate2PointGM() {}
251 
252 protected:
onShortName()253     SkString onShortName() {
254         return SkString("gradients_degenerate_2pt");
255     }
256 
onISize()257     virtual SkISize onISize() { return SkISize::Make(320, 320); }
258 
drawBG(SkCanvas * canvas)259     void drawBG(SkCanvas* canvas) {
260         canvas->drawColor(SK_ColorBLUE);
261     }
262 
onDraw(SkCanvas * canvas)263     virtual void onDraw(SkCanvas* canvas) {
264         this->drawBG(canvas);
265 
266         SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
267         SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 };
268         SkPoint c0;
269         c0.iset(-80, 25);
270         SkScalar r0 = SkIntToScalar(70);
271         SkPoint c1;
272         c1.iset(0, 25);
273         SkScalar r1 = SkIntToScalar(150);
274         SkShader* s = SkGradientShader::CreateTwoPointConical(c0, r0, c1, r1, colors,
275                                                               pos, SK_ARRAY_COUNT(pos),
276                                                               SkShader::kClamp_TileMode);
277         SkPaint paint;
278         paint.setShader(s)->unref();
279         canvas->drawPaint(paint);
280     }
281 
282 private:
283     typedef GM INHERITED;
284 };
285 DEF_GM( return new GradientsDegenrate2PointGM; )
286 
287 /// Tests correctness of *optimized* codepaths in gradients.
288 
289 class ClampedGradientsGM : public GM {
290 public:
ClampedGradientsGM()291     ClampedGradientsGM() {}
292 
293 protected:
onShortName()294     SkString onShortName() { return SkString("clamped_gradients"); }
295 
onISize()296     virtual SkISize onISize() { return SkISize::Make(640, 510); }
297 
drawBG(SkCanvas * canvas)298     void drawBG(SkCanvas* canvas) {
299         canvas->drawColor(0xFFDDDDDD);
300     }
301 
onDraw(SkCanvas * canvas)302     virtual void onDraw(SkCanvas* canvas) {
303         this->drawBG(canvas);
304 
305         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
306         SkPaint paint;
307         paint.setAntiAlias(true);
308 
309         SkPoint center;
310         center.iset(0, 300);
311         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
312         SkShader* shader = SkGradientShader::CreateRadial(
313             SkPoint(center),
314             SkIntToScalar(200), gColors, NULL, 5,
315             SkShader::kClamp_TileMode);
316         paint.setShader(shader);
317         canvas->drawRect(r, paint);
318         shader->unref();
319     }
320 
321 private:
322     typedef GM INHERITED;
323 };
324 DEF_GM( return new ClampedGradientsGM; )
325 
326 /// Checks quality of large radial gradients, which may display
327 /// some banding.
328 
329 class RadialGradientGM : public GM {
330 public:
RadialGradientGM()331     RadialGradientGM() {}
332 
333 protected:
334 
onShortName()335     SkString onShortName() override { return SkString("radial_gradient"); }
onISize()336     SkISize onISize() override { return SkISize::Make(1280, 1280); }
drawBG(SkCanvas * canvas)337     void drawBG(SkCanvas* canvas) {
338         canvas->drawColor(0xFF000000);
339     }
onDraw(SkCanvas * canvas)340     void onDraw(SkCanvas* canvas) override {
341         const SkISize dim = this->getISize();
342 
343         this->drawBG(canvas);
344 
345         SkPaint paint;
346         paint.setDither(true);
347         SkPoint center;
348         center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
349         SkScalar radius = SkIntToScalar(dim.width())/2;
350         const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
351         const SkScalar pos[] = { 0.0f,
352                              0.35f,
353                              1.0f };
354         SkShader* shader =
355             SkGradientShader::CreateRadial(center, radius, colors,
356                                            pos, SK_ARRAY_COUNT(pos),
357                                            SkShader::kClamp_TileMode);
358         paint.setShader(shader)->unref();
359         SkRect r = {
360             0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
361         };
362         canvas->drawRect(r, paint);
363     }
364 private:
365     typedef GM INHERITED;
366 };
367 DEF_GM( return new RadialGradientGM; )
368 
369 class RadialGradient2GM : public GM {
370 public:
RadialGradient2GM()371     RadialGradient2GM() {}
372 
373 protected:
374 
onShortName()375     SkString onShortName() override { return SkString("radial_gradient2"); }
onISize()376     SkISize onISize() override { return SkISize::Make(800, 400); }
drawBG(SkCanvas * canvas)377     void drawBG(SkCanvas* canvas) {
378         canvas->drawColor(0xFF000000);
379     }
380 
381     // Reproduces the example given in bug 7671058.
onDraw(SkCanvas * canvas)382     void onDraw(SkCanvas* canvas) override {
383         SkPaint paint1, paint2, paint3;
384         paint1.setStyle(SkPaint::kFill_Style);
385         paint2.setStyle(SkPaint::kFill_Style);
386         paint3.setStyle(SkPaint::kFill_Style);
387 
388         const SkColor sweep_colors[] =
389             { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 };
390         const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 };
391         const SkColor colors2[] = { 0xFF000000, 0x00000000 };
392 
393         const SkScalar cx = 200, cy = 200, radius = 150;
394         SkPoint center;
395         center.set(cx, cy);
396 
397         // We can either interpolate endpoints and premultiply each point (default, more precision),
398         // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap).
399         const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag };
400 
401         for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) {
402             SkAutoTUnref<SkShader> sweep(
403                     SkGradientShader::CreateSweep(cx, cy, sweep_colors,
404                                                   NULL, SK_ARRAY_COUNT(sweep_colors),
405                                                   flags[i], NULL));
406             SkAutoTUnref<SkShader> radial1(
407                     SkGradientShader::CreateRadial(center, radius, colors1,
408                                                    NULL, SK_ARRAY_COUNT(colors1),
409                                                    SkShader::kClamp_TileMode,
410                                                    flags[i], NULL));
411             SkAutoTUnref<SkShader> radial2(
412                     SkGradientShader::CreateRadial(center, radius, colors2,
413                                                    NULL, SK_ARRAY_COUNT(colors2),
414                                                    SkShader::kClamp_TileMode,
415                                                    flags[i], NULL));
416             paint1.setShader(sweep);
417             paint2.setShader(radial1);
418             paint3.setShader(radial2);
419 
420             canvas->drawCircle(cx, cy, radius, paint1);
421             canvas->drawCircle(cx, cy, radius, paint3);
422             canvas->drawCircle(cx, cy, radius, paint2);
423 
424             canvas->translate(400, 0);
425         }
426     }
427 
428 private:
429     typedef GM INHERITED;
430 };
431 DEF_GM( return new RadialGradient2GM; )
432 
433 // Shallow radial (shows banding on raster)
434 class RadialGradient3GM : public GM {
435     SkAutoTUnref<SkShader> fShader;
436 
437 protected:
onShortName()438     SkString onShortName() override { return SkString("radial_gradient3"); }
439 
onISize()440     SkISize onISize() override { return SkISize::Make(500, 500); }
441 
runAsBench() const442     bool runAsBench() const override { return true; }
443 
onOnceBeforeDraw()444     void onOnceBeforeDraw() override {
445         const SkPoint center = { 0, 0 };
446         const SkScalar kRadius = 3000;
447         const SkColor gColors[] = { 0xFFFFFFFF, 0xFF000000 };
448         fShader.reset(SkGradientShader::CreateRadial(center, kRadius, gColors, NULL, 2,
449                                                      SkShader::kClamp_TileMode));
450     }
451 
onDraw(SkCanvas * canvas)452     void onDraw(SkCanvas* canvas) override {
453         SkPaint paint;
454         paint.setShader(fShader);
455         canvas->drawRect(SkRect::MakeWH(500, 500), paint);
456     }
457 
458 private:
459     typedef GM INHERITED;
460 };
461 DEF_GM( return new RadialGradient3GM; )
462 
463 }
464