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