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, SkUnitMapper* mapper) {
     44     return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
     45                                           data.fCount, tm, mapper);
     46 }
     47 
     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 
     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 
     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 
     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:
    102     GradientsGM() {
    103         this->setBGColor(0xFFDDDDDD);
    104     }
    105 
    106 protected:
    107     SkString onShortName() {
    108         return SkString("gradients");
    109     }
    110 
    111     virtual SkISize onISize() { return make_isize(640, 615); }
    112 
    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:
    155     GradientsLocalPerspectiveGM() {
    156         this->setBGColor(0xFFDDDDDD);
    157     }
    158 
    159 protected:
    160     SkString onShortName() {
    161         return SkString("gradients_local_perspective");
    162     }
    163 
    164     virtual SkISize onISize() { return make_isize(640, 615); }
    165 
    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:
    211     SkString onShortName() {
    212         return SkString("gradients_view_perspective");
    213     }
    214 
    215     virtual SkISize onISize() { return make_isize(640, 400); }
    216 
    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:
    248     GradientsDegenrate2PointGM() {}
    249 
    250 protected:
    251     SkString onShortName() {
    252         return SkString("gradients_degenerate_2pt");
    253     }
    254 
    255     virtual SkISize onISize() { return make_isize(320, 320); }
    256 
    257     void drawBG(SkCanvas* canvas) {
    258         canvas->drawColor(SK_ColorBLUE);
    259     }
    260 
    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, SkFloatToScalar(0.01f), SkFloatToScalar(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:
    288     ClampedGradientsGM() {}
    289 
    290 protected:
    291     SkString onShortName() { return SkString("clamped_gradients"); }
    292 
    293     virtual SkISize onISize() { return make_isize(640, 510); }
    294 
    295     void drawBG(SkCanvas* canvas) {
    296         canvas->drawColor(0xFFDDDDDD);
    297     }
    298 
    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:
    327     RadialGradientGM() {}
    328 
    329 protected:
    330     SkString onShortName() { return SkString("radial_gradient"); }
    331     virtual SkISize onISize() { return make_isize(1280, 1280); }
    332     void drawBG(SkCanvas* canvas) {
    333         canvas->drawColor(0xFF000000);
    334     }
    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[] = { SkFloatToScalar(0.0f),
    347                              SkFloatToScalar(0.35f),
    348                              SkFloatToScalar(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 
    365 ///////////////////////////////////////////////////////////////////////////////
    366 
    367 static GM* MyFactory(void*) { return new GradientsGM; }
    368 static GMRegistry reg(MyFactory);
    369 
    370 static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; }
    371 static GMRegistry reg2(MyFactory2);
    372 
    373 static GM* MyFactory3(void*) { return new ClampedGradientsGM; }
    374 static GMRegistry reg3(MyFactory3);
    375 
    376 static GM* MyFactory4(void*) { return new RadialGradientGM; }
    377 static GMRegistry reg4(MyFactory4);
    378 
    379 static GM* MyFactory5(void*) { return new GradientsLocalPerspectiveGM; }
    380 static GMRegistry reg5(MyFactory5);
    381 
    382 static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; }
    383 static GMRegistry reg6(MyFactory6);
    384 }
    385