Home | History | Annotate | Download | only in gm
      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     virtual uint32_t onGetFlags() const SK_OVERRIDE {
    109         return kSkipTiled_Flag;
    110     }
    111 
    112     SkString onShortName() {
    113         return SkString("gradients");
    114     }
    115 
    116     virtual SkISize onISize() { return SkISize::Make(840, 815); }
    117 
    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:
    160     GradientsLocalPerspectiveGM() {
    161         this->setBGColor(0xFFDDDDDD);
    162     }
    163 
    164 protected:
    165     virtual uint32_t onGetFlags() const SK_OVERRIDE {
    166         return kSkipTiled_Flag;
    167     }
    168 
    169     SkString onShortName() {
    170         return SkString("gradients_local_perspective");
    171     }
    172 
    173     virtual SkISize onISize() { return SkISize::Make(840, 815); }
    174 
    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:
    218     SkString onShortName() {
    219         return SkString("gradients_view_perspective");
    220     }
    221 
    222     virtual SkISize onISize() { return SkISize::Make(840, 500); }
    223 
    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:
    255     GradientsDegenrate2PointGM() {}
    256 
    257 protected:
    258     SkString onShortName() {
    259         return SkString("gradients_degenerate_2pt");
    260     }
    261 
    262     virtual SkISize onISize() { return SkISize::Make(320, 320); }
    263 
    264     void drawBG(SkCanvas* canvas) {
    265         canvas->drawColor(SK_ColorBLUE);
    266     }
    267 
    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:
    295     ClampedGradientsGM() {}
    296 
    297 protected:
    298     virtual uint32_t onGetFlags() const SK_OVERRIDE {
    299         return kSkipTiled_Flag;
    300     }
    301 
    302     SkString onShortName() { return SkString("clamped_gradients"); }
    303 
    304     virtual SkISize onISize() { return SkISize::Make(640, 510); }
    305 
    306     void drawBG(SkCanvas* canvas) {
    307         canvas->drawColor(0xFFDDDDDD);
    308     }
    309 
    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:
    338     RadialGradientGM() {}
    339 
    340 protected:
    341     virtual uint32_t onGetFlags() const SK_OVERRIDE {
    342         return kSkipTiled_Flag;
    343     }
    344 
    345     SkString onShortName() { return SkString("radial_gradient"); }
    346     virtual SkISize onISize() { return SkISize::Make(1280, 1280); }
    347     void drawBG(SkCanvas* canvas) {
    348         canvas->drawColor(0xFF000000);
    349     }
    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:
    381     RadialGradient2GM() {}
    382 
    383 protected:
    384     virtual uint32_t onGetFlags() const SK_OVERRIDE {
    385         return kSkipTiled_Flag;
    386     }
    387 
    388     SkString onShortName() { return SkString("radial_gradient2"); }
    389     virtual SkISize onISize() { return SkISize::Make(800, 400); }
    390     void drawBG(SkCanvas* canvas) {
    391         canvas->drawColor(0xFF000000);
    392     }
    393 
    394     // Reproduces the example given in bug 7671058.
    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 
    447 static GM* MyFactory(void*) { return new GradientsGM; }
    448 static GMRegistry reg(MyFactory);
    449 
    450 static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; }
    451 static GMRegistry reg2(MyFactory2);
    452 
    453 static GM* MyFactory3(void*) { return new ClampedGradientsGM; }
    454 static GMRegistry reg3(MyFactory3);
    455 
    456 static GM* MyFactory4(void*) { return new RadialGradientGM; }
    457 static GMRegistry reg4(MyFactory4);
    458 
    459 static GM* MyFactory5(void*) { return new GradientsLocalPerspectiveGM; }
    460 static GMRegistry reg5(MyFactory5);
    461 
    462 static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; }
    463 static GMRegistry reg6(MyFactory6);
    464 
    465 static GM* MyFactory7(void*) { return new RadialGradient2GM; }
    466 static GMRegistry reg7(MyFactory7);
    467 }
    468