• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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::CreateTwoPointRadial(
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 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
84     SkScalar radius1 = SkScalarDiv(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:
onGetFlags() const108     virtual uint32_t onGetFlags() const SK_OVERRIDE {
109         return kSkipTiled_Flag;
110     }
111 
onShortName()112     SkString onShortName() {
113         return SkString("gradients");
114     }
115 
onISize()116     virtual SkISize onISize() { return SkISize::Make(840, 815); }
117 
onDraw(SkCanvas * canvas)118     virtual void onDraw(SkCanvas* canvas) {
119 
120         SkPoint pts[2] = {
121             { 0, 0 },
122             { SkIntToScalar(100), SkIntToScalar(100) }
123         };
124         SkShader::TileMode tm = SkShader::kClamp_TileMode;
125         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
126         SkPaint paint;
127         paint.setAntiAlias(true);
128 
129         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
130         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
131             canvas->save();
132             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
133                 SkMatrix scale = SkMatrix::I();
134 
135                 if (i == 5) { // if the clamp case
136                     scale.setScale(0.5f, 0.5f);
137                     scale.postTranslate(25.f, 25.f);
138                 }
139 
140                 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, scale);
141 
142                 paint.setShader(shader);
143                 canvas->drawRect(r, paint);
144                 shader->unref();
145                 canvas->translate(0, SkIntToScalar(120));
146             }
147             canvas->restore();
148             canvas->translate(SkIntToScalar(120), 0);
149         }
150     }
151 
152 private:
153     typedef GM INHERITED;
154 };
155 
156 // Based on the original gradient slide, but with perspective applied to the
157 // gradient shaders' local matrices
158 class GradientsLocalPerspectiveGM : public GM {
159 public:
GradientsLocalPerspectiveGM()160     GradientsLocalPerspectiveGM() {
161         this->setBGColor(0xFFDDDDDD);
162     }
163 
164 protected:
onGetFlags() const165     virtual uint32_t onGetFlags() const SK_OVERRIDE {
166         return kSkipTiled_Flag;
167     }
168 
onShortName()169     SkString onShortName() {
170         return SkString("gradients_local_perspective");
171     }
172 
onISize()173     virtual SkISize onISize() { return SkISize::Make(840, 815); }
174 
onDraw(SkCanvas * canvas)175     virtual void onDraw(SkCanvas* canvas) {
176 
177         SkPoint pts[2] = {
178             { 0, 0 },
179             { SkIntToScalar(100), SkIntToScalar(100) }
180         };
181         SkShader::TileMode tm = SkShader::kClamp_TileMode;
182         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
183         SkPaint paint;
184         paint.setAntiAlias(true);
185 
186         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
187         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
188             canvas->save();
189             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
190                 // apply an increasing y perspective as we move to the right
191                 SkMatrix perspective;
192                 perspective.setIdentity();
193                 perspective.setPerspY(SkScalarDiv(SkIntToScalar((unsigned) i+1),
194                                       SkIntToScalar(500)));
195                 perspective.setSkewX(SkScalarDiv(SkIntToScalar((unsigned) i+1),
196                                      SkIntToScalar(10)));
197 
198                 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, perspective);
199 
200                 paint.setShader(shader);
201                 canvas->drawRect(r, paint);
202                 shader->unref();
203                 canvas->translate(0, SkIntToScalar(120));
204             }
205             canvas->restore();
206             canvas->translate(SkIntToScalar(120), 0);
207         }
208     }
209 
210 private:
211     typedef GM INHERITED;
212 };
213 
214 // Based on the original gradient slide, but with perspective applied to
215 // the view matrix
216 class GradientsViewPerspectiveGM : public GradientsGM {
217 protected:
onShortName()218     SkString onShortName() {
219         return SkString("gradients_view_perspective");
220     }
221 
onISize()222     virtual SkISize onISize() { return SkISize::Make(840, 500); }
223 
onDraw(SkCanvas * canvas)224     virtual void onDraw(SkCanvas* canvas) {
225         SkMatrix perspective;
226         perspective.setIdentity();
227         perspective.setPerspY(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000)));
228         perspective.setSkewX(SkScalarDiv(SkIntToScalar(8), SkIntToScalar(25)));
229         canvas->concat(perspective);
230         INHERITED::onDraw(canvas);
231     }
232 
233 private:
234     typedef GradientsGM INHERITED;
235 };
236 
237 /*
238  Inspired by this <canvas> javascript, where we need to detect that we are not
239  solving a quadratic equation, but must instead solve a linear (since our X^2
240  coefficient is 0)
241 
242  ctx.fillStyle = '#f00';
243  ctx.fillRect(0, 0, 100, 50);
244 
245  var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
246  g.addColorStop(0, '#f00');
247  g.addColorStop(0.01, '#0f0');
248  g.addColorStop(0.99, '#0f0');
249  g.addColorStop(1, '#f00');
250  ctx.fillStyle = g;
251  ctx.fillRect(0, 0, 100, 50);
252  */
253 class GradientsDegenrate2PointGM : public GM {
254 public:
GradientsDegenrate2PointGM()255     GradientsDegenrate2PointGM() {}
256 
257 protected:
onShortName()258     SkString onShortName() {
259         return SkString("gradients_degenerate_2pt");
260     }
261 
onISize()262     virtual SkISize onISize() { return SkISize::Make(320, 320); }
263 
drawBG(SkCanvas * canvas)264     void drawBG(SkCanvas* canvas) {
265         canvas->drawColor(SK_ColorBLUE);
266     }
267 
onDraw(SkCanvas * canvas)268     virtual void onDraw(SkCanvas* canvas) {
269         this->drawBG(canvas);
270 
271         SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
272         SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 };
273         SkPoint c0;
274         c0.iset(-80, 25);
275         SkScalar r0 = SkIntToScalar(70);
276         SkPoint c1;
277         c1.iset(0, 25);
278         SkScalar r1 = SkIntToScalar(150);
279         SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
280                                                              pos, SK_ARRAY_COUNT(pos),
281                                                              SkShader::kClamp_TileMode);
282         SkPaint paint;
283         paint.setShader(s)->unref();
284         canvas->drawPaint(paint);
285     }
286 
287 private:
288     typedef GM INHERITED;
289 };
290 
291 /// Tests correctness of *optimized* codepaths in gradients.
292 
293 class ClampedGradientsGM : public GM {
294 public:
ClampedGradientsGM()295     ClampedGradientsGM() {}
296 
297 protected:
onGetFlags() const298     virtual uint32_t onGetFlags() const SK_OVERRIDE {
299         return kSkipTiled_Flag;
300     }
301 
onShortName()302     SkString onShortName() { return SkString("clamped_gradients"); }
303 
onISize()304     virtual SkISize onISize() { return SkISize::Make(640, 510); }
305 
drawBG(SkCanvas * canvas)306     void drawBG(SkCanvas* canvas) {
307         canvas->drawColor(0xFFDDDDDD);
308     }
309 
onDraw(SkCanvas * canvas)310     virtual void onDraw(SkCanvas* canvas) {
311         this->drawBG(canvas);
312 
313         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
314         SkPaint paint;
315         paint.setAntiAlias(true);
316 
317         SkPoint center;
318         center.iset(0, 300);
319         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
320         SkShader* shader = SkGradientShader::CreateRadial(
321             SkPoint(center),
322             SkIntToScalar(200), gColors, NULL, 5,
323             SkShader::kClamp_TileMode);
324         paint.setShader(shader);
325         canvas->drawRect(r, paint);
326         shader->unref();
327     }
328 
329 private:
330     typedef GM INHERITED;
331 };
332 
333 /// Checks quality of large radial gradients, which may display
334 /// some banding.
335 
336 class RadialGradientGM : public GM {
337 public:
RadialGradientGM()338     RadialGradientGM() {}
339 
340 protected:
onGetFlags() const341     virtual uint32_t onGetFlags() const SK_OVERRIDE {
342         return kSkipTiled_Flag;
343     }
344 
onShortName()345     SkString onShortName() { return SkString("radial_gradient"); }
onISize()346     virtual SkISize onISize() { return SkISize::Make(1280, 1280); }
drawBG(SkCanvas * canvas)347     void drawBG(SkCanvas* canvas) {
348         canvas->drawColor(0xFF000000);
349     }
onDraw(SkCanvas * canvas)350     virtual void onDraw(SkCanvas* canvas) {
351         const SkISize dim = this->getISize();
352 
353         this->drawBG(canvas);
354 
355         SkPaint paint;
356         paint.setDither(true);
357         SkPoint center;
358         center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
359         SkScalar radius = SkIntToScalar(dim.width())/2;
360         const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
361         const SkScalar pos[] = { 0.0f,
362                              0.35f,
363                              1.0f };
364         SkShader* shader =
365             SkGradientShader::CreateRadial(center, radius, colors,
366                                            pos, SK_ARRAY_COUNT(pos),
367                                            SkShader::kClamp_TileMode);
368         paint.setShader(shader)->unref();
369         SkRect r = {
370             0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
371         };
372         canvas->drawRect(r, paint);
373     }
374 private:
375     typedef GM INHERITED;
376 };
377 
378 
379 class RadialGradient2GM : public GM {
380 public:
RadialGradient2GM()381     RadialGradient2GM() {}
382 
383 protected:
onGetFlags() const384     virtual uint32_t onGetFlags() const SK_OVERRIDE {
385         return kSkipTiled_Flag;
386     }
387 
onShortName()388     SkString onShortName() { return SkString("radial_gradient2"); }
onISize()389     virtual SkISize onISize() { return SkISize::Make(800, 400); }
drawBG(SkCanvas * canvas)390     void drawBG(SkCanvas* canvas) {
391         canvas->drawColor(0xFF000000);
392     }
393 
394     // Reproduces the example given in bug 7671058.
onDraw(SkCanvas * canvas)395     virtual void onDraw(SkCanvas* canvas) {
396         SkPaint paint1, paint2, paint3;
397         paint1.setStyle(SkPaint::kFill_Style);
398         paint2.setStyle(SkPaint::kFill_Style);
399         paint3.setStyle(SkPaint::kFill_Style);
400 
401         const SkColor sweep_colors[] =
402             { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 };
403         const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 };
404         const SkColor colors2[] = { 0xFF000000, 0x00000000 };
405 
406         const SkScalar cx = 200, cy = 200, radius = 150;
407         SkPoint center;
408         center.set(cx, cy);
409 
410         // We can either interpolate endpoints and premultiply each point (default, more precision),
411         // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap).
412         const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag };
413 
414         for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) {
415             SkAutoTUnref<SkShader> sweep(
416                     SkGradientShader::CreateSweep(cx, cy, sweep_colors,
417                                                   NULL, SK_ARRAY_COUNT(sweep_colors),
418                                                   flags[i], NULL));
419             SkAutoTUnref<SkShader> radial1(
420                     SkGradientShader::CreateRadial(center, radius, colors1,
421                                                    NULL, SK_ARRAY_COUNT(colors1),
422                                                    SkShader::kClamp_TileMode,
423                                                    flags[i], NULL));
424             SkAutoTUnref<SkShader> radial2(
425                     SkGradientShader::CreateRadial(center, radius, colors2,
426                                                    NULL, SK_ARRAY_COUNT(colors2),
427                                                    SkShader::kClamp_TileMode,
428                                                    flags[i], NULL));
429             paint1.setShader(sweep);
430             paint2.setShader(radial1);
431             paint3.setShader(radial2);
432 
433             canvas->drawCircle(cx, cy, radius, paint1);
434             canvas->drawCircle(cx, cy, radius, paint3);
435             canvas->drawCircle(cx, cy, radius, paint2);
436 
437             canvas->translate(400, 0);
438         }
439     }
440 
441 private:
442     typedef GM INHERITED;
443 };
444 
445 ///////////////////////////////////////////////////////////////////////////////
446 
MyFactory(void *)447 static GM* MyFactory(void*) { return new GradientsGM; }
448 static GMRegistry reg(MyFactory);
449 
MyFactory2(void *)450 static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; }
451 static GMRegistry reg2(MyFactory2);
452 
MyFactory3(void *)453 static GM* MyFactory3(void*) { return new ClampedGradientsGM; }
454 static GMRegistry reg3(MyFactory3);
455 
MyFactory4(void *)456 static GM* MyFactory4(void*) { return new RadialGradientGM; }
457 static GMRegistry reg4(MyFactory4);
458 
MyFactory5(void *)459 static GM* MyFactory5(void*) { return new GradientsLocalPerspectiveGM; }
460 static GMRegistry reg5(MyFactory5);
461 
MyFactory6(void *)462 static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; }
463 static GMRegistry reg6(MyFactory6);
464 
MyFactory7(void *)465 static GM* MyFactory7(void*) { return new RadialGradient2GM; }
466 static GMRegistry reg7(MyFactory7);
467 }
468