Home | History | Annotate | Download | only in gm
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "gm.h"
      9 #include "SkGradientShader.h"
     10 #include "SkLinearGradient.h"
     11 
     12 namespace skiagm {
     13 
     14 struct GradData {
     15     int             fCount;
     16     const SkColor*  fColors;
     17     const SkScalar* fPos;
     18 };
     19 
     20 static const SkColor gColors[] = {
     21     SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
     22 };
     23 static const SkScalar gPos0[] = { 0, SK_Scalar1 };
     24 static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
     25 static const SkScalar gPos2[] = {
     26     0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
     27 };
     28 
     29 static const SkScalar gPosClamp[]   = {0.0f, 0.0f, 1.0f, 1.0f};
     30 static const SkColor  gColorClamp[] = {
     31     SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
     32 };
     33 
     34 static const GradData gGradData[] = {
     35     { 2, gColors, nullptr },
     36     { 2, gColors, gPos0 },
     37     { 2, gColors, gPos1 },
     38     { 5, gColors, nullptr },
     39     { 5, gColors, gPos2 },
     40     { 4, gColorClamp, gPosClamp }
     41 };
     42 
     43 static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
     44                             SkShader::TileMode tm, const SkMatrix& localMatrix) {
     45     return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
     46                                           data.fCount, tm, 0, &localMatrix);
     47 }
     48 
     49 static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
     50                             SkShader::TileMode tm, const SkMatrix& localMatrix) {
     51     SkPoint center;
     52     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
     53                SkScalarAve(pts[0].fY, pts[1].fY));
     54     return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
     55                                           data.fPos, data.fCount, tm, 0, &localMatrix);
     56 }
     57 
     58 static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
     59                            SkShader::TileMode, const SkMatrix& localMatrix) {
     60     SkPoint center;
     61     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
     62                SkScalarAve(pts[0].fY, pts[1].fY));
     63     return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
     64                                          data.fPos, data.fCount, 0, &localMatrix);
     65 }
     66 
     67 static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
     68                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     69     SkPoint center0, center1;
     70     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
     71                 SkScalarAve(pts[0].fY, pts[1].fY));
     72     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
     73                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
     74     return SkGradientShader::CreateTwoPointConical(
     75                                                    center1, (pts[1].fX - pts[0].fX) / 7,
     76                                                    center0, (pts[1].fX - pts[0].fX) / 2,
     77                                                    data.fColors, data.fPos, data.fCount, tm,
     78                                                    0, &localMatrix);
     79 }
     80 
     81 static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data,
     82                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     83     SkPoint center0, center1;
     84     SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
     85     SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     86     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
     87     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
     88     return SkGradientShader::CreateTwoPointConical(center1, radius1,
     89                                                    center0, radius0,
     90                                                    data.fColors, data.fPos,
     91                                                    data.fCount, tm, 0, &localMatrix);
     92 }
     93 
     94 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
     95                                SkShader::TileMode tm, const SkMatrix& localMatrix);
     96 static const GradMaker gGradMakers[] = {
     97     MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
     98 };
     99 
    100 ///////////////////////////////////////////////////////////////////////////////
    101 
    102 class GradientsGM : public GM {
    103 public:
    104     GradientsGM(bool dither) : fDither(dither) {
    105         this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD));
    106     }
    107 
    108 protected:
    109 
    110     SkString onShortName() {
    111         return SkString(fDither ? "gradients" : "gradients_nodither");
    112     }
    113 
    114     virtual SkISize onISize() { return SkISize::Make(840, 815); }
    115 
    116     virtual void onDraw(SkCanvas* canvas) {
    117 
    118         SkPoint pts[2] = {
    119             { 0, 0 },
    120             { SkIntToScalar(100), SkIntToScalar(100) }
    121         };
    122         SkShader::TileMode tm = SkShader::kClamp_TileMode;
    123         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
    124         SkPaint paint;
    125         paint.setAntiAlias(true);
    126         paint.setDither(fDither);
    127 
    128         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
    129         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
    130             canvas->save();
    131             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
    132                 SkMatrix scale = SkMatrix::I();
    133 
    134                 if (i == 5) { // if the clamp case
    135                     scale.setScale(0.5f, 0.5f);
    136                     scale.postTranslate(25.f, 25.f);
    137                 }
    138 
    139                 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, scale);
    140 
    141                 paint.setShader(shader);
    142                 canvas->drawRect(r, paint);
    143                 shader->unref();
    144                 canvas->translate(0, SkIntToScalar(120));
    145             }
    146             canvas->restore();
    147             canvas->translate(SkIntToScalar(120), 0);
    148         }
    149     }
    150 
    151 protected:
    152     bool fDither;
    153 
    154 private:
    155     typedef GM INHERITED;
    156 };
    157 DEF_GM( return new GradientsGM(true); )
    158 DEF_GM( return new GradientsGM(false); )
    159 
    160 // Based on the original gradient slide, but with perspective applied to the
    161 // gradient shaders' local matrices
    162 class GradientsLocalPerspectiveGM : public GM {
    163 public:
    164     GradientsLocalPerspectiveGM(bool dither) : fDither(dither) {
    165         this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD));
    166     }
    167 
    168 protected:
    169 
    170     SkString onShortName() {
    171         return SkString(fDither ? "gradients_local_perspective" :
    172                                   "gradients_local_perspective_nodither");
    173     }
    174 
    175     virtual SkISize onISize() { return SkISize::Make(840, 815); }
    176 
    177     virtual void onDraw(SkCanvas* canvas) {
    178 
    179         SkPoint pts[2] = {
    180             { 0, 0 },
    181             { SkIntToScalar(100), SkIntToScalar(100) }
    182         };
    183         SkShader::TileMode tm = SkShader::kClamp_TileMode;
    184         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
    185         SkPaint paint;
    186         paint.setAntiAlias(true);
    187         paint.setDither(fDither);
    188 
    189         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
    190         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
    191             canvas->save();
    192             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
    193                 // apply an increasing y perspective as we move to the right
    194                 SkMatrix perspective;
    195                 perspective.setIdentity();
    196                 perspective.setPerspY(SkIntToScalar(i+1) / 500);
    197                 perspective.setSkewX(SkIntToScalar(i+1) / 10);
    198 
    199                 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, perspective);
    200 
    201                 paint.setShader(shader);
    202                 canvas->drawRect(r, paint);
    203                 shader->unref();
    204                 canvas->translate(0, SkIntToScalar(120));
    205             }
    206             canvas->restore();
    207             canvas->translate(SkIntToScalar(120), 0);
    208         }
    209     }
    210 
    211 private:
    212     bool fDither;
    213 
    214     typedef GM INHERITED;
    215 };
    216 DEF_GM( return new GradientsLocalPerspectiveGM(true); )
    217 DEF_GM( return new GradientsLocalPerspectiveGM(false); )
    218 
    219 // Based on the original gradient slide, but with perspective applied to
    220 // the view matrix
    221 class GradientsViewPerspectiveGM : public GradientsGM {
    222 public:
    223     GradientsViewPerspectiveGM(bool dither) : INHERITED(dither) { }
    224 
    225 protected:
    226     SkString onShortName() {
    227         return SkString(fDither ? "gradients_view_perspective" :
    228                                   "gradients_view_perspective_nodither");
    229     }
    230 
    231     virtual SkISize onISize() { return SkISize::Make(840, 500); }
    232 
    233     virtual void onDraw(SkCanvas* canvas) {
    234         SkMatrix perspective;
    235         perspective.setIdentity();
    236         perspective.setPerspY(0.001f);
    237         perspective.setSkewX(SkIntToScalar(8) / 25);
    238         canvas->concat(perspective);
    239         INHERITED::onDraw(canvas);
    240     }
    241 
    242 private:
    243     typedef GradientsGM INHERITED;
    244 };
    245 DEF_GM( return new GradientsViewPerspectiveGM(true); )
    246 DEF_GM( return new GradientsViewPerspectiveGM(false); )
    247 
    248 /*
    249  Inspired by this <canvas> javascript, where we need to detect that we are not
    250  solving a quadratic equation, but must instead solve a linear (since our X^2
    251  coefficient is 0)
    252 
    253  ctx.fillStyle = '#f00';
    254  ctx.fillRect(0, 0, 100, 50);
    255 
    256  var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
    257  g.addColorStop(0, '#f00');
    258  g.addColorStop(0.01, '#0f0');
    259  g.addColorStop(0.99, '#0f0');
    260  g.addColorStop(1, '#f00');
    261  ctx.fillStyle = g;
    262  ctx.fillRect(0, 0, 100, 50);
    263  */
    264 class GradientsDegenrate2PointGM : public GM {
    265 public:
    266     GradientsDegenrate2PointGM(bool dither) : fDither(dither) {}
    267 
    268 protected:
    269     SkString onShortName() {
    270         return SkString(fDither ? "gradients_degenerate_2pt" : "gradients_degenerate_2pt_nodither");
    271     }
    272 
    273     virtual SkISize onISize() { return SkISize::Make(320, 320); }
    274 
    275     void drawBG(SkCanvas* canvas) {
    276         canvas->drawColor(SK_ColorBLUE);
    277     }
    278 
    279     virtual void onDraw(SkCanvas* canvas) {
    280         this->drawBG(canvas);
    281 
    282         SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
    283         SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 };
    284         SkPoint c0;
    285         c0.iset(-80, 25);
    286         SkScalar r0 = SkIntToScalar(70);
    287         SkPoint c1;
    288         c1.iset(0, 25);
    289         SkScalar r1 = SkIntToScalar(150);
    290         SkShader* s = SkGradientShader::CreateTwoPointConical(c0, r0, c1, r1, colors,
    291                                                               pos, SK_ARRAY_COUNT(pos),
    292                                                               SkShader::kClamp_TileMode);
    293         SkPaint paint;
    294         paint.setDither(fDither);
    295         paint.setShader(s)->unref();
    296         canvas->drawPaint(paint);
    297     }
    298 
    299 private:
    300     bool fDither;
    301 
    302     typedef GM INHERITED;
    303 };
    304 DEF_GM( return new GradientsDegenrate2PointGM(true); )
    305 DEF_GM( return new GradientsDegenrate2PointGM(false); )
    306 
    307 /// Tests correctness of *optimized* codepaths in gradients.
    308 
    309 class ClampedGradientsGM : public GM {
    310 public:
    311     ClampedGradientsGM(bool dither) : fDither(dither) {}
    312 
    313 protected:
    314     SkString onShortName() {
    315         return SkString(fDither ? "clamped_gradients" : "clamped_gradients_nodither");
    316     }
    317 
    318     virtual SkISize onISize() { return SkISize::Make(640, 510); }
    319 
    320     void drawBG(SkCanvas* canvas) {
    321         canvas->drawColor(sk_tool_utils::color_to_565(0xFFDDDDDD));
    322     }
    323 
    324     virtual void onDraw(SkCanvas* canvas) {
    325         this->drawBG(canvas);
    326 
    327         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
    328         SkPaint paint;
    329         paint.setDither(fDither);
    330         paint.setAntiAlias(true);
    331 
    332         SkPoint center;
    333         center.iset(0, 300);
    334         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
    335         SkShader* shader = SkGradientShader::CreateRadial(
    336             SkPoint(center),
    337             SkIntToScalar(200), gColors, nullptr, 5,
    338             SkShader::kClamp_TileMode);
    339         paint.setShader(shader);
    340         canvas->drawRect(r, paint);
    341         shader->unref();
    342     }
    343 
    344 private:
    345     bool fDither;
    346 
    347     typedef GM INHERITED;
    348 };
    349 DEF_GM( return new ClampedGradientsGM(true); )
    350 DEF_GM( return new ClampedGradientsGM(false); )
    351 
    352 /// Checks quality of large radial gradients, which may display
    353 /// some banding.
    354 
    355 class RadialGradientGM : public GM {
    356 public:
    357     RadialGradientGM() {}
    358 
    359 protected:
    360 
    361     SkString onShortName() override { return SkString("radial_gradient"); }
    362     SkISize onISize() override { return SkISize::Make(1280, 1280); }
    363     void drawBG(SkCanvas* canvas) {
    364         canvas->drawColor(0xFF000000);
    365     }
    366     void onDraw(SkCanvas* canvas) override {
    367         const SkISize dim = this->getISize();
    368 
    369         this->drawBG(canvas);
    370 
    371         SkPaint paint;
    372         paint.setDither(true);
    373         SkPoint center;
    374         center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
    375         SkScalar radius = SkIntToScalar(dim.width())/2;
    376         const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
    377         const SkScalar pos[] = { 0.0f,
    378                              0.35f,
    379                              1.0f };
    380         SkShader* shader =
    381             SkGradientShader::CreateRadial(center, radius, colors,
    382                                            pos, SK_ARRAY_COUNT(pos),
    383                                            SkShader::kClamp_TileMode);
    384         paint.setShader(shader)->unref();
    385         SkRect r = {
    386             0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
    387         };
    388         canvas->drawRect(r, paint);
    389     }
    390 private:
    391     typedef GM INHERITED;
    392 };
    393 DEF_GM( return new RadialGradientGM; )
    394 
    395 class RadialGradient2GM : public GM {
    396 public:
    397     RadialGradient2GM(bool dither) : fDither(dither) {}
    398 
    399 protected:
    400 
    401     SkString onShortName() override {
    402         return SkString(fDither ? "radial_gradient2" : "radial_gradient2_nodither");
    403     }
    404 
    405     SkISize onISize() override { return SkISize::Make(800, 400); }
    406     void drawBG(SkCanvas* canvas) {
    407         canvas->drawColor(0xFF000000);
    408     }
    409 
    410     // Reproduces the example given in bug 7671058.
    411     void onDraw(SkCanvas* canvas) override {
    412         SkPaint paint1, paint2, paint3;
    413         paint1.setStyle(SkPaint::kFill_Style);
    414         paint2.setStyle(SkPaint::kFill_Style);
    415         paint3.setStyle(SkPaint::kFill_Style);
    416 
    417         const SkColor sweep_colors[] =
    418             { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 };
    419         const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 };
    420         const SkColor colors2[] = { 0xFF000000, 0x00000000 };
    421 
    422         const SkScalar cx = 200, cy = 200, radius = 150;
    423         SkPoint center;
    424         center.set(cx, cy);
    425 
    426         // We can either interpolate endpoints and premultiply each point (default, more precision),
    427         // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap).
    428         const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag };
    429 
    430         for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) {
    431             SkAutoTUnref<SkShader> sweep(
    432                     SkGradientShader::CreateSweep(cx, cy, sweep_colors,
    433                                                   nullptr, SK_ARRAY_COUNT(sweep_colors),
    434                                                   flags[i], nullptr));
    435             SkAutoTUnref<SkShader> radial1(
    436                     SkGradientShader::CreateRadial(center, radius, colors1,
    437                                                    nullptr, SK_ARRAY_COUNT(colors1),
    438                                                    SkShader::kClamp_TileMode,
    439                                                    flags[i], nullptr));
    440             SkAutoTUnref<SkShader> radial2(
    441                     SkGradientShader::CreateRadial(center, radius, colors2,
    442                                                    nullptr, SK_ARRAY_COUNT(colors2),
    443                                                    SkShader::kClamp_TileMode,
    444                                                    flags[i], nullptr));
    445             paint1.setShader(sweep);
    446             paint1.setDither(fDither);
    447             paint2.setShader(radial1);
    448             paint2.setDither(fDither);
    449             paint3.setShader(radial2);
    450             paint3.setDither(fDither);
    451 
    452             canvas->drawCircle(cx, cy, radius, paint1);
    453             canvas->drawCircle(cx, cy, radius, paint3);
    454             canvas->drawCircle(cx, cy, radius, paint2);
    455 
    456             canvas->translate(400, 0);
    457         }
    458     }
    459 
    460 private:
    461     bool fDither;
    462 
    463     typedef GM INHERITED;
    464 };
    465 DEF_GM( return new RadialGradient2GM(true); )
    466 DEF_GM( return new RadialGradient2GM(false); )
    467 
    468 // Shallow radial (shows banding on raster)
    469 class RadialGradient3GM : public GM {
    470 public:
    471     RadialGradient3GM(bool dither) : fDither(dither) { }
    472 
    473 protected:
    474     SkString onShortName() override {
    475         return SkString(fDither ? "radial_gradient3" : "radial_gradient3_nodither");
    476     }
    477 
    478     SkISize onISize() override { return SkISize::Make(500, 500); }
    479 
    480     bool runAsBench() const override { return true; }
    481 
    482     void onOnceBeforeDraw() override {
    483         const SkPoint center = { 0, 0 };
    484         const SkScalar kRadius = 3000;
    485         const SkColor gColors[] = { 0xFFFFFFFF, 0xFF000000 };
    486         fShader.reset(SkGradientShader::CreateRadial(center, kRadius, gColors, nullptr, 2,
    487                                                      SkShader::kClamp_TileMode));
    488     }
    489 
    490     void onDraw(SkCanvas* canvas) override {
    491         SkPaint paint;
    492         paint.setShader(fShader);
    493         paint.setDither(fDither);
    494         canvas->drawRect(SkRect::MakeWH(500, 500), paint);
    495     }
    496 
    497 private:
    498     SkAutoTUnref<SkShader> fShader;
    499     bool fDither;
    500 
    501     typedef GM INHERITED;
    502 };
    503 DEF_GM( return new RadialGradient3GM(true); )
    504 DEF_GM( return new RadialGradient3GM(false); )
    505 
    506 class RadialGradient4GM : public GM {
    507 public:
    508     RadialGradient4GM(bool dither) : fDither(dither) { }
    509 
    510 protected:
    511     SkString onShortName() override {
    512         return SkString(fDither ? "radial_gradient4" : "radial_gradient4_nodither");
    513     }
    514 
    515     SkISize onISize() override { return SkISize::Make(500, 500); }
    516 
    517     void onOnceBeforeDraw() override {
    518         const SkPoint center = { 250, 250 };
    519         const SkScalar kRadius = 250;
    520         const SkColor colors[] = { SK_ColorRED, SK_ColorRED, SK_ColorWHITE, SK_ColorWHITE,
    521                 SK_ColorRED };
    522         const SkScalar pos[] = { 0, .4f, .4f, .8f, .8f, 1 };
    523         fShader.reset(SkGradientShader::CreateRadial(center, kRadius, colors, pos,
    524                 SK_ARRAY_COUNT(gColors), SkShader::kClamp_TileMode));
    525     }
    526 
    527     void onDraw(SkCanvas* canvas) override {
    528         SkPaint paint;
    529         paint.setAntiAlias(true);
    530         paint.setDither(fDither);
    531         paint.setShader(fShader);
    532         canvas->drawRect(SkRect::MakeWH(500, 500), paint);
    533     }
    534 
    535 private:
    536     SkAutoTUnref<SkShader> fShader;
    537     bool fDither;
    538 
    539     typedef GM INHERITED;
    540 };
    541 DEF_GM( return new RadialGradient4GM(true); )
    542 DEF_GM( return new RadialGradient4GM(false); )
    543 
    544 class LinearGradientGM : public GM {
    545 public:
    546     LinearGradientGM(bool dither) : fDither(dither) { }
    547 
    548 protected:
    549     SkString onShortName() override {
    550         return SkString(fDither ? "linear_gradient" : "linear_gradient_nodither");
    551     }
    552 
    553     const SkScalar kWidthBump = 30.f;
    554     const SkScalar kHeight = 5.f;
    555     const SkScalar kMinWidth = 540.f;
    556 
    557     SkISize onISize() override { return SkISize::Make(500, 500); }
    558 
    559     void onOnceBeforeDraw() override {
    560         SkPoint pts[2] = { {0, 0}, {0, 0} };
    561         const SkColor colors[] = { SK_ColorWHITE, SK_ColorWHITE, 0xFF008200, 0xFF008200,
    562                 SK_ColorWHITE, SK_ColorWHITE };
    563         const SkScalar unitPos[] = { 0, 50, 70, 500, 540 };
    564         SkScalar pos[6];
    565         pos[5] = 1;
    566         for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) {
    567             pts[1].fX = 500.f + index * kWidthBump;
    568             for (int inner = 0; inner < (int) SK_ARRAY_COUNT(unitPos); ++inner) {
    569                 pos[inner] = unitPos[inner] / (kMinWidth + index * kWidthBump);
    570             }
    571             fShader[index].reset(SkGradientShader::CreateLinear(pts, colors, pos,
    572                     SK_ARRAY_COUNT(gColors), SkShader::kClamp_TileMode));
    573         }
    574     }
    575 
    576     void onDraw(SkCanvas* canvas) override {
    577         SkPaint paint;
    578         paint.setAntiAlias(true);
    579         paint.setDither(fDither);
    580         for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) {
    581             paint.setShader(fShader[index]);
    582             canvas->drawRect(SkRect::MakeLTRB(0, index * kHeight, kMinWidth + index * kWidthBump,
    583                     (index + 1) * kHeight), paint);
    584         }
    585     }
    586 
    587 private:
    588     SkAutoTUnref<SkShader> fShader[100];
    589     bool fDither;
    590 
    591     typedef GM INHERITED;
    592 };
    593 DEF_GM( return new LinearGradientGM(true); )
    594 DEF_GM( return new LinearGradientGM(false); )
    595 
    596 class LinearGradientTinyGM : public GM {
    597 public:
    598     LinearGradientTinyGM(uint32_t flags, const char* suffix = nullptr)
    599     : fName("linear_gradient_tiny")
    600     , fFlags(flags) {
    601         fName.append(suffix);
    602     }
    603 
    604 protected:
    605     SkString onShortName() override {
    606         return fName;
    607     }
    608 
    609     SkISize onISize() override {
    610         return SkISize::Make(600, 500);
    611     }
    612 
    613     void onDraw(SkCanvas* canvas) override {
    614         const SkScalar kRectSize = 100;
    615         const unsigned kStopCount = 3;
    616         const SkColor colors[kStopCount] = { SK_ColorGREEN, SK_ColorRED, SK_ColorGREEN };
    617         const struct {
    618             SkPoint pts[2];
    619             SkScalar pos[kStopCount];
    620         } configs[] = {
    621             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.999999f,    1 }},
    622             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.000001f,    1 }},
    623             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.999999999f, 1 }},
    624             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.000000001f, 1 }},
    625 
    626             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.999999f,    1 }},
    627             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.000001f,    1 }},
    628             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.999999999f, 1 }},
    629             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.000000001f, 1 }},
    630 
    631             { { SkPoint::Make(0, 0),        SkPoint::Make(0.00001f, 0) }, { 0, 0.5f, 1 }},
    632             { { SkPoint::Make(9.99999f, 0), SkPoint::Make(10, 0) },       { 0, 0.5f, 1 }},
    633             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 0.00001f) }, { 0, 0.5f, 1 }},
    634             { { SkPoint::Make(0, 9.99999f), SkPoint::Make(0, 10) },       { 0, 0.5f, 1 }},
    635         };
    636 
    637         SkPaint paint;
    638         for (unsigned i = 0; i < SK_ARRAY_COUNT(configs); ++i) {
    639             SkAutoCanvasRestore acr(canvas, true);
    640             SkAutoTUnref<SkShader> gradient(
    641                 SkGradientShader::CreateLinear(configs[i].pts, colors, configs[i].pos, kStopCount,
    642                                                SkShader::kClamp_TileMode, fFlags, nullptr));
    643             canvas->translate(kRectSize * ((i % 4) * 1.5f + 0.25f),
    644                               kRectSize * ((i / 4) * 1.5f + 0.25f));
    645 
    646             paint.setShader(gradient);
    647             canvas->drawRect(SkRect::MakeWH(kRectSize, kRectSize), paint);
    648         }
    649     }
    650 
    651 private:
    652     typedef GM INHERITED;
    653 
    654     SkString fName;
    655     uint32_t fFlags;
    656 };
    657 DEF_GM( return new LinearGradientTinyGM(0); )
    658 DEF_GM( return new LinearGradientTinyGM(SkLinearGradient::kForce4fContext_PrivateFlag, "_4f"); )
    659 }
    660 
    661 ///////////////////////////////////////////////////////////////////////////////////////////////////
    662 
    663 struct GradRun {
    664     SkColor  fColors[4];
    665     SkScalar fPos[4];
    666     int      fCount;
    667 };
    668 
    669 #define SIZE 121
    670 
    671 static SkShader* make_linear(const GradRun& run, SkShader::TileMode mode) {
    672     const SkPoint pts[] { { 30, 30 }, { SIZE - 30, SIZE - 30 } };
    673     return SkGradientShader::CreateLinear(pts, run.fColors, run.fPos, run.fCount, mode);
    674 }
    675 
    676 static SkShader* make_radial(const GradRun& run, SkShader::TileMode mode) {
    677     const SkScalar half = SIZE * 0.5f;
    678     return SkGradientShader::CreateRadial({half,half}, half - 10,
    679                                           run.fColors, run.fPos, run.fCount, mode);
    680 }
    681 
    682 static SkShader* make_conical(const GradRun& run, SkShader::TileMode mode) {
    683     const SkScalar half = SIZE * 0.5f;
    684     const SkPoint center { half, half };
    685     return SkGradientShader::CreateTwoPointConical(center, 20, center, half - 10,
    686                                           run.fColors, run.fPos, run.fCount, mode);
    687 }
    688 
    689 static SkShader* make_sweep(const GradRun& run, SkShader::TileMode) {
    690     const SkScalar half = SIZE * 0.5f;
    691     return SkGradientShader::CreateSweep(half, half, run.fColors, run.fPos, run.fCount);
    692 }
    693 
    694 /*
    695  *  Exercise duplicate color-stops, at the ends, and in the middle
    696  *
    697  *  At the time of this writing, only Linear correctly deals with duplicates at the ends,
    698  *  and then only correctly on CPU backend.
    699  */
    700 DEF_SIMPLE_GM(gradients_dup_color_stops, canvas, 704, 564) {
    701     const SkColor preColor  = 0xFFFF0000;   // clamp color before start
    702     const SkColor postColor = 0xFF0000FF;   // clamp color after end
    703     const SkColor color0    = 0xFF000000;
    704     const SkColor color1    = 0xFF00FF00;
    705     const SkColor badColor  = 0xFF3388BB;   // should never be seen, fills out fixed-size array
    706 
    707     const GradRun runs[] = {
    708         {   { color0, color1, badColor, badColor },
    709             { 0, 1, -1, -1 },
    710             2,
    711         },
    712         {   { preColor, color0, color1, badColor },
    713             { 0, 0, 1, -1 },
    714             3,
    715         },
    716         {   { color0, color1, postColor, badColor },
    717             { 0, 1, 1, -1 },
    718             3,
    719         },
    720         {   { preColor, color0, color1, postColor },
    721             { 0, 0, 1, 1 },
    722             4,
    723         },
    724         {   { color0, color0, color1, color1 },
    725             { 0, 0.5f, 0.5f, 1 },
    726             4,
    727         },
    728     };
    729     SkShader* (*factories[])(const GradRun&, SkShader::TileMode) {
    730         make_linear, make_radial, make_conical, make_sweep
    731     };
    732 
    733     const SkRect rect = SkRect::MakeWH(SIZE, SIZE);
    734     const SkScalar dx = SIZE + 20;
    735     const SkScalar dy = SIZE + 20;
    736     const SkShader::TileMode mode = SkShader::kClamp_TileMode;
    737 
    738     SkPaint paint;
    739     canvas->translate(10, 10 - dy);
    740     for (auto factory : factories) {
    741         canvas->translate(0, dy);
    742         SkAutoCanvasRestore acr(canvas, true);
    743         for (const auto& run : runs) {
    744             paint.setShader(factory(run, mode))->unref();
    745             canvas->drawRect(rect, paint);
    746             canvas->translate(dx, 0);
    747         }
    748     }
    749 }
    750 
    751 static void draw_many_stops(SkCanvas* canvas, uint32_t flags) {
    752     const unsigned kStopCount = 200;
    753     const SkPoint pts[] = { {50, 50}, {450, 465}};
    754 
    755     SkColor colors[kStopCount];
    756     for (unsigned i = 0; i < kStopCount; i++) {
    757         switch (i % 5) {
    758         case 0: colors[i] = SK_ColorRED; break;
    759         case 1: colors[i] = SK_ColorGREEN; break;
    760         case 2: colors[i] = SK_ColorGREEN; break;
    761         case 3: colors[i] = SK_ColorBLUE; break;
    762         case 4: colors[i] = SK_ColorRED; break;
    763         }
    764     }
    765 
    766     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(
    767         pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, flags, nullptr));
    768 
    769     SkPaint p;
    770     p.setShader(shader);
    771 
    772     canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p);
    773 }
    774 
    775 DEF_SIMPLE_GM(gradient_many_stops, canvas, 500, 500) {
    776     draw_many_stops(canvas, 0);
    777 }
    778 
    779 DEF_SIMPLE_GM(gradient_many_stops_4f, canvas, 500, 500) {
    780     draw_many_stops(canvas, SkLinearGradient::kForce4fContext_PrivateFlag);
    781 }
    782