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     SkString onShortName() { return SkString("clamped_gradients"); }
    299 
    300     virtual SkISize onISize() { return SkISize::Make(640, 510); }
    301 
    302     void drawBG(SkCanvas* canvas) {
    303         canvas->drawColor(0xFFDDDDDD);
    304     }
    305 
    306     virtual void onDraw(SkCanvas* canvas) {
    307         this->drawBG(canvas);
    308 
    309         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
    310         SkPaint paint;
    311         paint.setAntiAlias(true);
    312 
    313         SkPoint center;
    314         center.iset(0, 300);
    315         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
    316         SkShader* shader = SkGradientShader::CreateRadial(
    317             SkPoint(center),
    318             SkIntToScalar(200), gColors, NULL, 5,
    319             SkShader::kClamp_TileMode);
    320         paint.setShader(shader);
    321         canvas->drawRect(r, paint);
    322         shader->unref();
    323     }
    324 
    325 private:
    326     typedef GM INHERITED;
    327 };
    328 
    329 /// Checks quality of large radial gradients, which may display
    330 /// some banding.
    331 
    332 class RadialGradientGM : public GM {
    333 public:
    334     RadialGradientGM() {}
    335 
    336 protected:
    337     virtual uint32_t onGetFlags() const SK_OVERRIDE {
    338         return kSkipTiled_Flag;
    339     }
    340 
    341     SkString onShortName() { return SkString("radial_gradient"); }
    342     virtual SkISize onISize() { return SkISize::Make(1280, 1280); }
    343     void drawBG(SkCanvas* canvas) {
    344         canvas->drawColor(0xFF000000);
    345     }
    346     virtual void onDraw(SkCanvas* canvas) {
    347         const SkISize dim = this->getISize();
    348 
    349         this->drawBG(canvas);
    350 
    351         SkPaint paint;
    352         paint.setDither(true);
    353         SkPoint center;
    354         center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
    355         SkScalar radius = SkIntToScalar(dim.width())/2;
    356         const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
    357         const SkScalar pos[] = { 0.0f,
    358                              0.35f,
    359                              1.0f };
    360         SkShader* shader =
    361             SkGradientShader::CreateRadial(center, radius, colors,
    362                                            pos, SK_ARRAY_COUNT(pos),
    363                                            SkShader::kClamp_TileMode);
    364         paint.setShader(shader)->unref();
    365         SkRect r = {
    366             0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
    367         };
    368         canvas->drawRect(r, paint);
    369     }
    370 private:
    371     typedef GM INHERITED;
    372 };
    373 
    374 
    375 class RadialGradient2GM : public GM {
    376 public:
    377     RadialGradient2GM() {}
    378 
    379 protected:
    380     virtual uint32_t onGetFlags() const SK_OVERRIDE {
    381         return kSkipTiled_Flag;
    382     }
    383 
    384     SkString onShortName() { return SkString("radial_gradient2"); }
    385     virtual SkISize onISize() { return SkISize::Make(800, 400); }
    386     void drawBG(SkCanvas* canvas) {
    387         canvas->drawColor(0xFF000000);
    388     }
    389 
    390     // Reproduces the example given in bug 7671058.
    391     virtual void onDraw(SkCanvas* canvas) {
    392         SkPaint paint1, paint2, paint3;
    393         paint1.setStyle(SkPaint::kFill_Style);
    394         paint2.setStyle(SkPaint::kFill_Style);
    395         paint3.setStyle(SkPaint::kFill_Style);
    396 
    397         const SkColor sweep_colors[] =
    398             { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 };
    399         const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 };
    400         const SkColor colors2[] = { 0xFF000000, 0x00000000 };
    401 
    402         const SkScalar cx = 200, cy = 200, radius = 150;
    403         SkPoint center;
    404         center.set(cx, cy);
    405 
    406         // We can either interpolate endpoints and premultiply each point (default, more precision),
    407         // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap).
    408         const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag };
    409 
    410         for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) {
    411             SkAutoTUnref<SkShader> sweep(
    412                     SkGradientShader::CreateSweep(cx, cy, sweep_colors,
    413                                                   NULL, SK_ARRAY_COUNT(sweep_colors),
    414                                                   flags[i], NULL));
    415             SkAutoTUnref<SkShader> radial1(
    416                     SkGradientShader::CreateRadial(center, radius, colors1,
    417                                                    NULL, SK_ARRAY_COUNT(colors1),
    418                                                    SkShader::kClamp_TileMode,
    419                                                    flags[i], NULL));
    420             SkAutoTUnref<SkShader> radial2(
    421                     SkGradientShader::CreateRadial(center, radius, colors2,
    422                                                    NULL, SK_ARRAY_COUNT(colors2),
    423                                                    SkShader::kClamp_TileMode,
    424                                                    flags[i], NULL));
    425             paint1.setShader(sweep);
    426             paint2.setShader(radial1);
    427             paint3.setShader(radial2);
    428 
    429             canvas->drawCircle(cx, cy, radius, paint1);
    430             canvas->drawCircle(cx, cy, radius, paint3);
    431             canvas->drawCircle(cx, cy, radius, paint2);
    432 
    433             canvas->translate(400, 0);
    434         }
    435     }
    436 
    437 private:
    438     typedef GM INHERITED;
    439 };
    440 
    441 ///////////////////////////////////////////////////////////////////////////////
    442 
    443 static GM* MyFactory(void*) { return new GradientsGM; }
    444 static GMRegistry reg(MyFactory);
    445 
    446 static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; }
    447 static GMRegistry reg2(MyFactory2);
    448 
    449 static GM* MyFactory3(void*) { return new ClampedGradientsGM; }
    450 static GMRegistry reg3(MyFactory3);
    451 
    452 static GM* MyFactory4(void*) { return new RadialGradientGM; }
    453 static GMRegistry reg4(MyFactory4);
    454 
    455 static GM* MyFactory5(void*) { return new GradientsLocalPerspectiveGM; }
    456 static GMRegistry reg5(MyFactory5);
    457 
    458 static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; }
    459 static GMRegistry reg6(MyFactory6);
    460 
    461 static GM* MyFactory7(void*) { return new RadialGradient2GM; }
    462 static GMRegistry reg7(MyFactory7);
    463 }
    464