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