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 
     11 namespace skiagm {
     12 
     13 struct GradData {
     14     int              fCount;
     15     const SkColor*   fColors;
     16     const SkColor4f* fColors4f;
     17     const SkScalar*  fPos;
     18 };
     19 
     20 constexpr SkColor gColors[] = {
     21     SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
     22 };
     23 constexpr SkColor4f gColors4f[] ={
     24     { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
     25     { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
     26     { 0.0f, 0.0f, 1.0f, 1.0f }, // Blue
     27     { 1.0f, 1.0f, 1.0f, 1.0f }, // White
     28     { 0.0f, 0.0f, 0.0f, 1.0f }  // Black
     29 };
     30 constexpr SkScalar gPos0[] = { 0, SK_Scalar1 };
     31 constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
     32 constexpr SkScalar gPos2[] = {
     33     0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
     34 };
     35 
     36 constexpr SkScalar gPosClamp[]   = {0.0f, 0.0f, 1.0f, 1.0f};
     37 constexpr SkColor  gColorClamp[] = {
     38     SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
     39 };
     40 constexpr SkColor4f gColor4fClamp[] ={
     41     { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
     42     { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
     43     { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
     44     { 0.0f, 0.0f, 1.0f, 1.0f }  // Blue
     45 };
     46 constexpr GradData gGradData[] = {
     47     { 2, gColors, gColors4f, nullptr },
     48     { 2, gColors, gColors4f, gPos0 },
     49     { 2, gColors, gColors4f, gPos1 },
     50     { 5, gColors, gColors4f, nullptr },
     51     { 5, gColors, gColors4f, gPos2 },
     52     { 4, gColorClamp, gColor4fClamp, gPosClamp }
     53 };
     54 
     55 static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data,
     56                                   SkShader::TileMode tm, const SkMatrix& localMatrix) {
     57     return SkGradientShader::MakeLinear(pts, data.fColors, data.fPos, data.fCount, tm, 0,
     58                                         &localMatrix);
     59 }
     60 
     61 static sk_sp<SkShader> MakeLinear4f(const SkPoint pts[2], const GradData& data,
     62                                     SkShader::TileMode tm, const SkMatrix& localMatrix) {
     63     auto srgb = SkColorSpace::MakeSRGB();
     64     return SkGradientShader::MakeLinear(pts, data.fColors4f, srgb, data.fPos, data.fCount, tm, 0,
     65                                         &localMatrix);
     66 }
     67 
     68 static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data,
     69                                   SkShader::TileMode tm, const SkMatrix& localMatrix) {
     70     SkPoint center;
     71     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
     72                SkScalarAve(pts[0].fY, pts[1].fY));
     73     return SkGradientShader::MakeRadial(center, center.fX, data.fColors, data.fPos, data.fCount,
     74                                         tm, 0, &localMatrix);
     75 }
     76 
     77 static sk_sp<SkShader> MakeRadial4f(const SkPoint pts[2], const GradData& data,
     78                                     SkShader::TileMode tm, const SkMatrix& localMatrix) {
     79     SkPoint center;
     80     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
     81                SkScalarAve(pts[0].fY, pts[1].fY));
     82     auto srgb = SkColorSpace::MakeSRGB();
     83     return SkGradientShader::MakeRadial(center, center.fX, data.fColors4f, srgb, data.fPos,
     84                                         data.fCount, tm, 0, &localMatrix);
     85 }
     86 
     87 static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data,
     88                                  SkShader::TileMode, const SkMatrix& localMatrix) {
     89     SkPoint center;
     90     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
     91                SkScalarAve(pts[0].fY, pts[1].fY));
     92     return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount,
     93                                        0, &localMatrix);
     94 }
     95 
     96 static sk_sp<SkShader> MakeSweep4f(const SkPoint pts[2], const GradData& data,
     97                                    SkShader::TileMode, const SkMatrix& localMatrix) {
     98     SkPoint center;
     99     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
    100                SkScalarAve(pts[0].fY, pts[1].fY));
    101     auto srgb = SkColorSpace::MakeSRGB();
    102     return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors4f, srgb, data.fPos,
    103                                        data.fCount, 0, &localMatrix);
    104 }
    105 
    106 static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data,
    107                                    SkShader::TileMode tm, const SkMatrix& localMatrix) {
    108     SkPoint center0, center1;
    109     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
    110                 SkScalarAve(pts[0].fY, pts[1].fY));
    111     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
    112                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
    113     return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
    114                                                  center0, (pts[1].fX - pts[0].fX) / 2,
    115                                                  data.fColors, data.fPos, data.fCount, tm,
    116                                                  0, &localMatrix);
    117 }
    118 
    119 static sk_sp<SkShader> Make2Radial4f(const SkPoint pts[2], const GradData& data,
    120                                      SkShader::TileMode tm, const SkMatrix& localMatrix) {
    121     SkPoint center0, center1;
    122     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
    123                 SkScalarAve(pts[0].fY, pts[1].fY));
    124     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3) / 5),
    125                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1) / 4));
    126     auto srgb = SkColorSpace::MakeSRGB();
    127     return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
    128                                                  center0, (pts[1].fX - pts[0].fX) / 2,
    129                                                  data.fColors4f, srgb, data.fPos, data.fCount, tm,
    130                                                  0, &localMatrix);
    131 }
    132 
    133 static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data,
    134                                     SkShader::TileMode tm, const SkMatrix& localMatrix) {
    135     SkPoint center0, center1;
    136     SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
    137     SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
    138     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
    139     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
    140     return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
    141                                                  data.fColors, data.fPos,
    142                                                  data.fCount, tm, 0, &localMatrix);
    143 }
    144 
    145 static sk_sp<SkShader> Make2Conical4f(const SkPoint pts[2], const GradData& data,
    146                                       SkShader::TileMode tm, const SkMatrix& localMatrix) {
    147     SkPoint center0, center1;
    148     SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
    149     SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
    150     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
    151     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
    152     auto srgb = SkColorSpace::MakeSRGB();
    153     return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
    154                                                  data.fColors4f, srgb, data.fPos,
    155                                                  data.fCount, tm, 0, &localMatrix);
    156 }
    157 
    158 typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data,
    159                                      SkShader::TileMode tm, const SkMatrix& localMatrix);
    160 constexpr GradMaker gGradMakers[] = {
    161     MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
    162 };
    163 constexpr GradMaker gGradMakers4f[] ={
    164     MakeLinear4f, MakeRadial4f, MakeSweep4f, Make2Radial4f, Make2Conical4f
    165 };
    166 
    167 ///////////////////////////////////////////////////////////////////////////////
    168 
    169 class GradientsGM : public GM {
    170 public:
    171     GradientsGM(bool dither) : fDither(dither) {
    172         this->setBGColor(0xFFDDDDDD);
    173     }
    174 
    175 protected:
    176 
    177     SkString onShortName() {
    178         return SkString(fDither ? "gradients" : "gradients_nodither");
    179     }
    180 
    181     virtual SkISize onISize() { return SkISize::Make(840, 815); }
    182 
    183     virtual void onDraw(SkCanvas* canvas) {
    184 
    185         SkPoint pts[2] = {
    186             { 0, 0 },
    187             { SkIntToScalar(100), SkIntToScalar(100) }
    188         };
    189         SkShader::TileMode tm = SkShader::kClamp_TileMode;
    190         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
    191         SkPaint paint;
    192         paint.setAntiAlias(true);
    193         paint.setDither(fDither);
    194 
    195         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
    196         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
    197             canvas->save();
    198             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
    199                 SkMatrix scale = SkMatrix::I();
    200 
    201                 if (i == 5) { // if the clamp case
    202                     scale.setScale(0.5f, 0.5f);
    203                     scale.postTranslate(25.f, 25.f);
    204                 }
    205 
    206                 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, scale));
    207                 canvas->drawRect(r, paint);
    208                 canvas->translate(0, SkIntToScalar(120));
    209             }
    210             canvas->restore();
    211             canvas->translate(SkIntToScalar(120), 0);
    212         }
    213     }
    214 
    215 protected:
    216     bool fDither;
    217 
    218 private:
    219     typedef GM INHERITED;
    220 };
    221 DEF_GM( return new GradientsGM(true); )
    222 DEF_GM( return new GradientsGM(false); )
    223 
    224 // Like the original gradients GM, but using the SkColor4f shader factories. Should be identical.
    225 class Gradients4fGM : public GM {
    226 public:
    227     Gradients4fGM(bool dither) : fDither(dither) {
    228         this->setBGColor(0xFFDDDDDD);
    229     }
    230 
    231 protected:
    232 
    233     SkString onShortName() {
    234         return SkString(fDither ? "gradients4f" : "gradients4f_nodither");
    235     }
    236 
    237     virtual SkISize onISize() { return SkISize::Make(840, 815); }
    238 
    239     virtual void onDraw(SkCanvas* canvas) {
    240 
    241         SkPoint pts[2] ={
    242             { 0, 0 },
    243             { SkIntToScalar(100), SkIntToScalar(100) }
    244         };
    245         SkShader::TileMode tm = SkShader::kClamp_TileMode;
    246         SkRect r ={ 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
    247         SkPaint paint;
    248         paint.setAntiAlias(true);
    249         paint.setDither(fDither);
    250 
    251         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
    252         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
    253             canvas->save();
    254             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers4f); j++) {
    255                 SkMatrix scale = SkMatrix::I();
    256 
    257                 if (i == 5) { // if the clamp case
    258                     scale.setScale(0.5f, 0.5f);
    259                     scale.postTranslate(25.f, 25.f);
    260                 }
    261 
    262                 paint.setShader(gGradMakers4f[j](pts, gGradData[i], tm, scale));
    263                 canvas->drawRect(r, paint);
    264                 canvas->translate(0, SkIntToScalar(120));
    265             }
    266             canvas->restore();
    267             canvas->translate(SkIntToScalar(120), 0);
    268         }
    269     }
    270 
    271 protected:
    272     bool fDither;
    273 
    274 private:
    275     typedef GM INHERITED;
    276 };
    277 DEF_GM(return new Gradients4fGM(true); )
    278 DEF_GM(return new Gradients4fGM(false); )
    279 
    280 // Based on the original gradient slide, but with perspective applied to the
    281 // gradient shaders' local matrices
    282 class GradientsLocalPerspectiveGM : public GM {
    283 public:
    284     GradientsLocalPerspectiveGM(bool dither) : fDither(dither) {
    285         this->setBGColor(0xFFDDDDDD);
    286     }
    287 
    288 protected:
    289 
    290     SkString onShortName() {
    291         return SkString(fDither ? "gradients_local_perspective" :
    292                                   "gradients_local_perspective_nodither");
    293     }
    294 
    295     virtual SkISize onISize() { return SkISize::Make(840, 815); }
    296 
    297     virtual void onDraw(SkCanvas* canvas) {
    298 
    299         SkPoint pts[2] = {
    300             { 0, 0 },
    301             { SkIntToScalar(100), SkIntToScalar(100) }
    302         };
    303         SkShader::TileMode tm = SkShader::kClamp_TileMode;
    304         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
    305         SkPaint paint;
    306         paint.setAntiAlias(true);
    307         paint.setDither(fDither);
    308 
    309         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
    310         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
    311             canvas->save();
    312             for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
    313                 // apply an increasing y perspective as we move to the right
    314                 SkMatrix perspective;
    315                 perspective.setIdentity();
    316                 perspective.setPerspY(SkIntToScalar(i+1) / 500);
    317                 perspective.setSkewX(SkIntToScalar(i+1) / 10);
    318 
    319                 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, perspective));
    320                 canvas->drawRect(r, paint);
    321                 canvas->translate(0, SkIntToScalar(120));
    322             }
    323             canvas->restore();
    324             canvas->translate(SkIntToScalar(120), 0);
    325         }
    326     }
    327 
    328 private:
    329     bool fDither;
    330 
    331     typedef GM INHERITED;
    332 };
    333 DEF_GM( return new GradientsLocalPerspectiveGM(true); )
    334 DEF_GM( return new GradientsLocalPerspectiveGM(false); )
    335 
    336 // Based on the original gradient slide, but with perspective applied to
    337 // the view matrix
    338 class GradientsViewPerspectiveGM : public GradientsGM {
    339 public:
    340     GradientsViewPerspectiveGM(bool dither) : INHERITED(dither) { }
    341 
    342 protected:
    343     SkString onShortName() {
    344         return SkString(fDither ? "gradients_view_perspective" :
    345                                   "gradients_view_perspective_nodither");
    346     }
    347 
    348     virtual SkISize onISize() { return SkISize::Make(840, 500); }
    349 
    350     virtual void onDraw(SkCanvas* canvas) {
    351         SkMatrix perspective;
    352         perspective.setIdentity();
    353         perspective.setPerspY(0.001f);
    354         perspective.setSkewX(SkIntToScalar(8) / 25);
    355         canvas->concat(perspective);
    356         INHERITED::onDraw(canvas);
    357     }
    358 
    359 private:
    360     typedef GradientsGM INHERITED;
    361 };
    362 DEF_GM( return new GradientsViewPerspectiveGM(true); )
    363 DEF_GM( return new GradientsViewPerspectiveGM(false); )
    364 
    365 /*
    366  Inspired by this <canvas> javascript, where we need to detect that we are not
    367  solving a quadratic equation, but must instead solve a linear (since our X^2
    368  coefficient is 0)
    369 
    370  ctx.fillStyle = '#f00';
    371  ctx.fillRect(0, 0, 100, 50);
    372 
    373  var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
    374  g.addColorStop(0, '#f00');
    375  g.addColorStop(0.01, '#0f0');
    376  g.addColorStop(0.99, '#0f0');
    377  g.addColorStop(1, '#f00');
    378  ctx.fillStyle = g;
    379  ctx.fillRect(0, 0, 100, 50);
    380  */
    381 class GradientsDegenrate2PointGM : public GM {
    382 public:
    383     GradientsDegenrate2PointGM(bool dither) : fDither(dither) {}
    384 
    385 protected:
    386     SkString onShortName() {
    387         return SkString(fDither ? "gradients_degenerate_2pt" : "gradients_degenerate_2pt_nodither");
    388     }
    389 
    390     virtual SkISize onISize() { return SkISize::Make(320, 320); }
    391 
    392     void drawBG(SkCanvas* canvas) {
    393         canvas->drawColor(SK_ColorBLUE);
    394     }
    395 
    396     virtual void onDraw(SkCanvas* canvas) {
    397         this->drawBG(canvas);
    398 
    399         SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
    400         SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 };
    401         SkPoint c0;
    402         c0.iset(-80, 25);
    403         SkScalar r0 = SkIntToScalar(70);
    404         SkPoint c1;
    405         c1.iset(0, 25);
    406         SkScalar r1 = SkIntToScalar(150);
    407         SkPaint paint;
    408         paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors,
    409                                                               pos, SK_ARRAY_COUNT(pos),
    410                                                               SkShader::kClamp_TileMode));
    411         paint.setDither(fDither);
    412         canvas->drawPaint(paint);
    413     }
    414 
    415 private:
    416     bool fDither;
    417 
    418     typedef GM INHERITED;
    419 };
    420 DEF_GM( return new GradientsDegenrate2PointGM(true); )
    421 DEF_GM( return new GradientsDegenrate2PointGM(false); )
    422 
    423 /* bug.skia.org/517
    424 <canvas id="canvas"></canvas>
    425 <script>
    426 var c = document.getElementById("canvas");
    427 var ctx = c.getContext("2d");
    428 ctx.fillStyle = '#ff0';
    429 ctx.fillRect(0, 0, 100, 50);
    430 
    431 var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
    432 g.addColorStop(0, '#0f0');
    433 g.addColorStop(0.003, '#f00');  // 0.004 makes this work
    434 g.addColorStop(1, '#ff0');
    435 ctx.fillStyle = g;
    436 ctx.fillRect(0, 0, 100, 50);
    437 </script>
    438 */
    439 
    440 // should draw only green
    441 DEF_SIMPLE_GM(small_color_stop, canvas, 100, 150) {
    442     SkColor colors[] = { SK_ColorGREEN, SK_ColorRED, SK_ColorYELLOW };
    443     SkScalar pos[] = { 0, 0.003f, SK_Scalar1 };  // 0.004f makes this work
    444     SkPoint c0 = { 200, 25 };
    445     SkScalar r0 = 20;
    446     SkPoint c1 = { 200, 25 };
    447     SkScalar r1 = 10;
    448 
    449     SkPaint paint;
    450     paint.setColor(SK_ColorYELLOW);
    451     canvas->drawRect(SkRect::MakeWH(100, 150), paint);
    452     paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors, pos,
    453                                                           SK_ARRAY_COUNT(pos),
    454                                                           SkShader::kClamp_TileMode));
    455     canvas->drawRect(SkRect::MakeWH(100, 150), paint);
    456 }
    457 
    458 
    459 /// Tests correctness of *optimized* codepaths in gradients.
    460 
    461 class ClampedGradientsGM : public GM {
    462 public:
    463     ClampedGradientsGM(bool dither) : fDither(dither) {}
    464 
    465 protected:
    466     SkString onShortName() {
    467         return SkString(fDither ? "clamped_gradients" : "clamped_gradients_nodither");
    468     }
    469 
    470     virtual SkISize onISize() { return SkISize::Make(640, 510); }
    471 
    472     void drawBG(SkCanvas* canvas) {
    473         canvas->drawColor(0xFFDDDDDD);
    474     }
    475 
    476     virtual void onDraw(SkCanvas* canvas) {
    477         this->drawBG(canvas);
    478 
    479         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
    480         SkPaint paint;
    481         paint.setDither(fDither);
    482         paint.setAntiAlias(true);
    483 
    484         SkPoint center;
    485         center.iset(0, 300);
    486         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
    487         paint.setShader(SkGradientShader::MakeRadial(
    488             SkPoint(center),
    489             SkIntToScalar(200), gColors, nullptr, 5,
    490             SkShader::kClamp_TileMode));
    491         canvas->drawRect(r, paint);
    492     }
    493 
    494 private:
    495     bool fDither;
    496 
    497     typedef GM INHERITED;
    498 };
    499 DEF_GM( return new ClampedGradientsGM(true); )
    500 DEF_GM( return new ClampedGradientsGM(false); )
    501 
    502 /// Checks quality of large radial gradients, which may display
    503 /// some banding.
    504 
    505 class RadialGradientGM : public GM {
    506 public:
    507     RadialGradientGM() {}
    508 
    509 protected:
    510 
    511     SkString onShortName() override { return SkString("radial_gradient"); }
    512     SkISize onISize() override { return SkISize::Make(1280, 1280); }
    513     void drawBG(SkCanvas* canvas) {
    514         canvas->drawColor(0xFF000000);
    515     }
    516     void onDraw(SkCanvas* canvas) override {
    517         const SkISize dim = this->getISize();
    518 
    519         this->drawBG(canvas);
    520 
    521         SkPaint paint;
    522         paint.setDither(true);
    523         SkPoint center;
    524         center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
    525         SkScalar radius = SkIntToScalar(dim.width())/2;
    526         const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
    527         const SkScalar pos[] = { 0.0f,
    528                              0.35f,
    529                              1.0f };
    530         paint.setShader(SkGradientShader::MakeRadial(center, radius, colors, pos,
    531                                                      SK_ARRAY_COUNT(pos),
    532                                                      SkShader::kClamp_TileMode));
    533         SkRect r = {
    534             0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
    535         };
    536         canvas->drawRect(r, paint);
    537     }
    538 private:
    539     typedef GM INHERITED;
    540 };
    541 DEF_GM( return new RadialGradientGM; )
    542 
    543 class RadialGradient2GM : public GM {
    544 public:
    545     RadialGradient2GM(bool dither) : fDither(dither) {}
    546 
    547 protected:
    548 
    549     SkString onShortName() override {
    550         return SkString(fDither ? "radial_gradient2" : "radial_gradient2_nodither");
    551     }
    552 
    553     SkISize onISize() override { return SkISize::Make(800, 400); }
    554     void drawBG(SkCanvas* canvas) {
    555         canvas->drawColor(0xFF000000);
    556     }
    557 
    558     // Reproduces the example given in bug 7671058.
    559     void onDraw(SkCanvas* canvas) override {
    560         SkPaint paint1, paint2, paint3;
    561         paint1.setStyle(SkPaint::kFill_Style);
    562         paint2.setStyle(SkPaint::kFill_Style);
    563         paint3.setStyle(SkPaint::kFill_Style);
    564 
    565         const SkColor sweep_colors[] =
    566             { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 };
    567         const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 };
    568         const SkColor colors2[] = { 0xFF000000, 0x00000000 };
    569 
    570         const SkScalar cx = 200, cy = 200, radius = 150;
    571         SkPoint center;
    572         center.set(cx, cy);
    573 
    574         // We can either interpolate endpoints and premultiply each point (default, more precision),
    575         // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap).
    576         const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag };
    577 
    578         for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) {
    579             paint1.setShader(SkGradientShader::MakeSweep(cx, cy, sweep_colors,
    580                                                          nullptr, SK_ARRAY_COUNT(sweep_colors),
    581                                                          flags[i], nullptr));
    582             paint2.setShader(SkGradientShader::MakeRadial(center, radius, colors1,
    583                                                           nullptr, SK_ARRAY_COUNT(colors1),
    584                                                           SkShader::kClamp_TileMode,
    585                                                           flags[i], nullptr));
    586             paint3.setShader(SkGradientShader::MakeRadial(center, radius, colors2,
    587                                                           nullptr, SK_ARRAY_COUNT(colors2),
    588                                                           SkShader::kClamp_TileMode,
    589                                                           flags[i], nullptr));
    590             paint1.setDither(fDither);
    591             paint2.setDither(fDither);
    592             paint3.setDither(fDither);
    593 
    594             canvas->drawCircle(cx, cy, radius, paint1);
    595             canvas->drawCircle(cx, cy, radius, paint3);
    596             canvas->drawCircle(cx, cy, radius, paint2);
    597 
    598             canvas->translate(400, 0);
    599         }
    600     }
    601 
    602 private:
    603     bool fDither;
    604 
    605     typedef GM INHERITED;
    606 };
    607 DEF_GM( return new RadialGradient2GM(true); )
    608 DEF_GM( return new RadialGradient2GM(false); )
    609 
    610 // Shallow radial (shows banding on raster)
    611 class RadialGradient3GM : public GM {
    612 public:
    613     RadialGradient3GM(bool dither) : fDither(dither) { }
    614 
    615 protected:
    616     SkString onShortName() override {
    617         return SkString(fDither ? "radial_gradient3" : "radial_gradient3_nodither");
    618     }
    619 
    620     SkISize onISize() override { return SkISize::Make(500, 500); }
    621 
    622     bool runAsBench() const override { return true; }
    623 
    624     void onOnceBeforeDraw() override {
    625         const SkPoint center = { 0, 0 };
    626         const SkScalar kRadius = 3000;
    627         const SkColor gColors[] = { 0xFFFFFFFF, 0xFF000000 };
    628         fShader = SkGradientShader::MakeRadial(center, kRadius, gColors, nullptr, 2,
    629                                                SkShader::kClamp_TileMode);
    630     }
    631 
    632     void onDraw(SkCanvas* canvas) override {
    633         SkPaint paint;
    634         paint.setShader(fShader);
    635         paint.setDither(fDither);
    636         canvas->drawRect(SkRect::MakeWH(500, 500), paint);
    637     }
    638 
    639 private:
    640     sk_sp<SkShader> fShader;
    641     bool fDither;
    642 
    643     typedef GM INHERITED;
    644 };
    645 DEF_GM( return new RadialGradient3GM(true); )
    646 DEF_GM( return new RadialGradient3GM(false); )
    647 
    648 class RadialGradient4GM : public GM {
    649 public:
    650     RadialGradient4GM(bool dither) : fDither(dither) { }
    651 
    652 protected:
    653     SkString onShortName() override {
    654         return SkString(fDither ? "radial_gradient4" : "radial_gradient4_nodither");
    655     }
    656 
    657     SkISize onISize() override { return SkISize::Make(500, 500); }
    658 
    659     void onOnceBeforeDraw() override {
    660         const SkPoint center = { 250, 250 };
    661         const SkScalar kRadius = 250;
    662         const SkColor colors[] = { SK_ColorRED, SK_ColorRED, SK_ColorWHITE, SK_ColorWHITE,
    663                 SK_ColorRED };
    664         const SkScalar pos[] = { 0, .4f, .4f, .8f, .8f, 1 };
    665         fShader = SkGradientShader::MakeRadial(center, kRadius, colors, pos,
    666                                                SK_ARRAY_COUNT(gColors), SkShader::kClamp_TileMode);
    667     }
    668 
    669     void onDraw(SkCanvas* canvas) override {
    670         SkPaint paint;
    671         paint.setAntiAlias(true);
    672         paint.setDither(fDither);
    673         paint.setShader(fShader);
    674         canvas->drawRect(SkRect::MakeWH(500, 500), paint);
    675     }
    676 
    677 private:
    678     sk_sp<SkShader> fShader;
    679     bool fDither;
    680 
    681     typedef GM INHERITED;
    682 };
    683 DEF_GM( return new RadialGradient4GM(true); )
    684 DEF_GM( return new RadialGradient4GM(false); )
    685 
    686 class LinearGradientGM : public GM {
    687 public:
    688     LinearGradientGM(bool dither) : fDither(dither) { }
    689 
    690 protected:
    691     SkString onShortName() override {
    692         return SkString(fDither ? "linear_gradient" : "linear_gradient_nodither");
    693     }
    694 
    695     const SkScalar kWidthBump = 30.f;
    696     const SkScalar kHeight = 5.f;
    697     const SkScalar kMinWidth = 540.f;
    698 
    699     SkISize onISize() override { return SkISize::Make(500, 500); }
    700 
    701     void onOnceBeforeDraw() override {
    702         SkPoint pts[2] = { {0, 0}, {0, 0} };
    703         const SkColor colors[] = { SK_ColorWHITE, SK_ColorWHITE, 0xFF008200, 0xFF008200,
    704                 SK_ColorWHITE, SK_ColorWHITE };
    705         const SkScalar unitPos[] = { 0, 50, 70, 500, 540 };
    706         SkScalar pos[6];
    707         pos[5] = 1;
    708         for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) {
    709             pts[1].fX = 500.f + index * kWidthBump;
    710             for (int inner = 0; inner < (int) SK_ARRAY_COUNT(unitPos); ++inner) {
    711                 pos[inner] = unitPos[inner] / (kMinWidth + index * kWidthBump);
    712             }
    713             fShader[index] = SkGradientShader::MakeLinear(pts, colors, pos,
    714                     SK_ARRAY_COUNT(gColors), SkShader::kClamp_TileMode);
    715         }
    716     }
    717 
    718     void onDraw(SkCanvas* canvas) override {
    719         SkPaint paint;
    720         paint.setAntiAlias(true);
    721         paint.setDither(fDither);
    722         for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) {
    723             paint.setShader(fShader[index]);
    724             canvas->drawRect(SkRect::MakeLTRB(0, index * kHeight, kMinWidth + index * kWidthBump,
    725                     (index + 1) * kHeight), paint);
    726         }
    727     }
    728 
    729 private:
    730     sk_sp<SkShader> fShader[100];
    731     bool fDither;
    732 
    733     typedef GM INHERITED;
    734 };
    735 DEF_GM( return new LinearGradientGM(true); )
    736 DEF_GM( return new LinearGradientGM(false); )
    737 
    738 class LinearGradientTinyGM : public GM {
    739 public:
    740     LinearGradientTinyGM(uint32_t flags, const char* suffix = nullptr)
    741     : fName("linear_gradient_tiny")
    742     , fFlags(flags) {
    743         fName.append(suffix);
    744     }
    745 
    746 protected:
    747     SkString onShortName() override {
    748         return fName;
    749     }
    750 
    751     SkISize onISize() override {
    752         return SkISize::Make(600, 500);
    753     }
    754 
    755     void onDraw(SkCanvas* canvas) override {
    756         const SkScalar kRectSize = 100;
    757         const unsigned kStopCount = 3;
    758         const SkColor colors[kStopCount] = { SK_ColorGREEN, SK_ColorRED, SK_ColorGREEN };
    759         const struct {
    760             SkPoint pts[2];
    761             SkScalar pos[kStopCount];
    762         } configs[] = {
    763             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.999999f,    1 }},
    764             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.000001f,    1 }},
    765             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.999999999f, 1 }},
    766             { { SkPoint::Make(0, 0),        SkPoint::Make(10, 0) },       { 0, 0.000000001f, 1 }},
    767 
    768             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.999999f,    1 }},
    769             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.000001f,    1 }},
    770             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.999999999f, 1 }},
    771             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 10) },       { 0, 0.000000001f, 1 }},
    772 
    773             { { SkPoint::Make(0, 0),        SkPoint::Make(0.00001f, 0) }, { 0, 0.5f, 1 }},
    774             { { SkPoint::Make(9.99999f, 0), SkPoint::Make(10, 0) },       { 0, 0.5f, 1 }},
    775             { { SkPoint::Make(0, 0),        SkPoint::Make(0, 0.00001f) }, { 0, 0.5f, 1 }},
    776             { { SkPoint::Make(0, 9.99999f), SkPoint::Make(0, 10) },       { 0, 0.5f, 1 }},
    777         };
    778 
    779         SkPaint paint;
    780         for (unsigned i = 0; i < SK_ARRAY_COUNT(configs); ++i) {
    781             SkAutoCanvasRestore acr(canvas, true);
    782             paint.setShader(SkGradientShader::MakeLinear(configs[i].pts, colors, configs[i].pos,
    783                                                          kStopCount, SkShader::kClamp_TileMode,
    784                                                          fFlags, nullptr));
    785             canvas->translate(kRectSize * ((i % 4) * 1.5f + 0.25f),
    786                               kRectSize * ((i / 4) * 1.5f + 0.25f));
    787 
    788             canvas->drawRect(SkRect::MakeWH(kRectSize, kRectSize), paint);
    789         }
    790     }
    791 
    792 private:
    793     typedef GM INHERITED;
    794 
    795     SkString fName;
    796     uint32_t fFlags;
    797 };
    798 DEF_GM( return new LinearGradientTinyGM(0); )
    799 }
    800 
    801 ///////////////////////////////////////////////////////////////////////////////////////////////////
    802 
    803 struct GradRun {
    804     SkColor  fColors[4];
    805     SkScalar fPos[4];
    806     int      fCount;
    807 };
    808 
    809 #define SIZE 121
    810 
    811 static sk_sp<SkShader> make_linear(const GradRun& run, SkShader::TileMode mode) {
    812     const SkPoint pts[] { { 30, 30 }, { SIZE - 30, SIZE - 30 } };
    813     return SkGradientShader::MakeLinear(pts, run.fColors, run.fPos, run.fCount, mode);
    814 }
    815 
    816 static sk_sp<SkShader> make_radial(const GradRun& run, SkShader::TileMode mode) {
    817     const SkScalar half = SIZE * 0.5f;
    818     return SkGradientShader::MakeRadial({half,half}, half - 10, run.fColors, run.fPos,
    819                                         run.fCount, mode);
    820 }
    821 
    822 static sk_sp<SkShader> make_conical(const GradRun& run, SkShader::TileMode mode) {
    823     const SkScalar half = SIZE * 0.5f;
    824     const SkPoint center { half, half };
    825     return SkGradientShader::MakeTwoPointConical(center, 20, center, half - 10,
    826                                                  run.fColors, run.fPos, run.fCount, mode);
    827 }
    828 
    829 static sk_sp<SkShader> make_sweep(const GradRun& run, SkShader::TileMode) {
    830     const SkScalar half = SIZE * 0.5f;
    831     return SkGradientShader::MakeSweep(half, half, run.fColors, run.fPos, run.fCount);
    832 }
    833 
    834 /*
    835  *  Exercise duplicate color-stops, at the ends, and in the middle
    836  *
    837  *  At the time of this writing, only Linear correctly deals with duplicates at the ends,
    838  *  and then only correctly on CPU backend.
    839  */
    840 DEF_SIMPLE_GM(gradients_dup_color_stops, canvas, 704, 564) {
    841     const SkColor preColor  = 0xFFFF0000;   // clamp color before start
    842     const SkColor postColor = 0xFF0000FF;   // clamp color after end
    843     const SkColor color0    = 0xFF000000;
    844     const SkColor color1    = 0xFF00FF00;
    845     const SkColor badColor  = 0xFF3388BB;   // should never be seen, fills out fixed-size array
    846 
    847     const GradRun runs[] = {
    848         {   { color0, color1, badColor, badColor },
    849             { 0, 1, -1, -1 },
    850             2,
    851         },
    852         {   { preColor, color0, color1, badColor },
    853             { 0, 0, 1, -1 },
    854             3,
    855         },
    856         {   { color0, color1, postColor, badColor },
    857             { 0, 1, 1, -1 },
    858             3,
    859         },
    860         {   { preColor, color0, color1, postColor },
    861             { 0, 0, 1, 1 },
    862             4,
    863         },
    864         {   { color0, color0, color1, color1 },
    865             { 0, 0.5f, 0.5f, 1 },
    866             4,
    867         },
    868     };
    869     sk_sp<SkShader> (*factories[])(const GradRun&, SkShader::TileMode) {
    870         make_linear, make_radial, make_conical, make_sweep
    871     };
    872 
    873     const SkRect rect = SkRect::MakeWH(SIZE, SIZE);
    874     const SkScalar dx = SIZE + 20;
    875     const SkScalar dy = SIZE + 20;
    876     const SkShader::TileMode mode = SkShader::kClamp_TileMode;
    877 
    878     SkPaint paint;
    879     canvas->translate(10, 10 - dy);
    880     for (auto factory : factories) {
    881         canvas->translate(0, dy);
    882         SkAutoCanvasRestore acr(canvas, true);
    883         for (const auto& run : runs) {
    884             paint.setShader(factory(run, mode));
    885             canvas->drawRect(rect, paint);
    886             canvas->translate(dx, 0);
    887         }
    888     }
    889 }
    890 
    891 static void draw_many_stops(SkCanvas* canvas) {
    892     const unsigned kStopCount = 200;
    893     const SkPoint pts[] = { {50, 50}, {450, 465}};
    894 
    895     SkColor colors[kStopCount];
    896     for (unsigned i = 0; i < kStopCount; i++) {
    897         switch (i % 5) {
    898         case 0: colors[i] = SK_ColorRED; break;
    899         case 1: colors[i] = SK_ColorGREEN; break;
    900         case 2: colors[i] = SK_ColorGREEN; break;
    901         case 3: colors[i] = SK_ColorBLUE; break;
    902         case 4: colors[i] = SK_ColorRED; break;
    903         }
    904     }
    905 
    906     SkPaint p;
    907     p.setShader(SkGradientShader::MakeLinear(
    908         pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode));
    909 
    910     canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p);
    911 }
    912 
    913 DEF_SIMPLE_GM(gradient_many_stops, canvas, 500, 500) {
    914     draw_many_stops(canvas);
    915 }
    916 
    917 #include "SkPictureRecorder.h"
    918 
    919 static void draw_circle_shader(SkCanvas* canvas, SkScalar cx, SkScalar cy, SkScalar r,
    920                                sk_sp<SkShader> (*shaderFunc)()) {
    921     SkPaint p;
    922     p.setAntiAlias(true);
    923     p.setShader(shaderFunc());
    924     canvas->drawCircle(cx, cy, r, p);
    925 
    926     p.setShader(nullptr);
    927     p.setColor(SK_ColorGRAY);
    928     p.setStyle(SkPaint::kStroke_Style);
    929     p.setStrokeWidth(2);
    930     canvas->drawCircle(cx, cy, r, p);
    931 }
    932 
    933 DEF_SIMPLE_GM(fancy_gradients, canvas, 800, 300) {
    934     draw_circle_shader(canvas, 150, 150, 100, []() -> sk_sp<SkShader> {
    935         // Checkerboard using two linear gradients + picture shader.
    936         SkScalar kTileSize = 80 / sqrtf(2);
    937         SkColor colors1[] = { 0xff000000, 0xff000000,
    938                               0xffffffff, 0xffffffff,
    939                               0xff000000, 0xff000000 };
    940         SkColor colors2[] = { 0xff000000, 0xff000000,
    941                               0x00000000, 0x00000000,
    942                               0xff000000, 0xff000000 };
    943         SkScalar pos[] = { 0, .25f, .25f, .75f, .75f, 1 };
    944         static_assert(SK_ARRAY_COUNT(colors1) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
    945         static_assert(SK_ARRAY_COUNT(colors2) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
    946 
    947         SkPictureRecorder recorder;
    948         recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize));
    949 
    950         SkPaint p;
    951 
    952         SkPoint pts1[] = { { 0, 0 }, { kTileSize, kTileSize }};
    953         p.setShader(SkGradientShader::MakeLinear(pts1, colors1, pos, SK_ARRAY_COUNT(colors1),
    954                                                  SkShader::kClamp_TileMode, 0, nullptr));
    955         recorder.getRecordingCanvas()->drawPaint(p);
    956 
    957         SkPoint pts2[] = { { 0, kTileSize }, { kTileSize, 0 }};
    958         p.setShader(SkGradientShader::MakeLinear(pts2, colors2, pos, SK_ARRAY_COUNT(colors2),
    959                                                  SkShader::kClamp_TileMode, 0, nullptr));
    960         recorder.getRecordingCanvas()->drawPaint(p);
    961 
    962         SkMatrix m = SkMatrix::I();
    963         m.preRotate(45);
    964         return SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
    965                                            SkShader::kRepeat_TileMode,
    966                                            SkShader::kRepeat_TileMode, &m, nullptr);
    967     });
    968 
    969     draw_circle_shader(canvas, 400, 150, 100, []() -> sk_sp<SkShader> {
    970         // Checkerboard using a sweep gradient + picture shader.
    971         SkScalar kTileSize = 80;
    972         SkColor colors[] = { 0xff000000, 0xff000000,
    973                              0xffffffff, 0xffffffff,
    974                              0xff000000, 0xff000000,
    975                              0xffffffff, 0xffffffff };
    976         SkScalar pos[] = { 0, .25f, .25f, .5f, .5f, .75f, .75f, 1 };
    977         static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
    978 
    979         SkPaint p;
    980         p.setShader(SkGradientShader::MakeSweep(kTileSize / 2, kTileSize / 2,
    981                                                 colors, pos, SK_ARRAY_COUNT(colors), 0, nullptr));
    982         SkPictureRecorder recorder;
    983         recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize))->drawPaint(p);
    984 
    985         return SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
    986                                            SkShader::kRepeat_TileMode,
    987                                            SkShader::kRepeat_TileMode, nullptr, nullptr);
    988     });
    989 
    990     draw_circle_shader(canvas, 650, 150, 100, []() -> sk_sp<SkShader> {
    991         // Dartboard using sweep + radial.
    992         const SkColor a = 0xffffffff;
    993         const SkColor b = 0xff000000;
    994         SkColor colors[] = { a, a, b, b, a, a, b, b, a, a, b, b, a, a, b, b};
    995         SkScalar pos[] = { 0, .125f, .125f, .25f, .25f, .375f, .375f, .5f, .5f,
    996                            .625f, .625f, .75f, .75f, .875f, .875f, 1};
    997         static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
    998 
    999         SkPoint center = { 650, 150 };
   1000         sk_sp<SkShader> sweep1 = SkGradientShader::MakeSweep(center.x(), center.y(), colors, pos,
   1001                                                              SK_ARRAY_COUNT(colors), 0, nullptr);
   1002         SkMatrix m = SkMatrix::I();
   1003         m.preRotate(22.5f, center.x(), center.y());
   1004         sk_sp<SkShader> sweep2 = SkGradientShader::MakeSweep(center.x(), center.y(), colors, pos,
   1005                                                              SK_ARRAY_COUNT(colors), 0, &m);
   1006 
   1007         sk_sp<SkShader> sweep(SkShader::MakeComposeShader(sweep1, sweep2, SkBlendMode::kExclusion));
   1008 
   1009         SkScalar radialPos[] = { 0, .02f, .02f, .04f, .04f, .08f, .08f, .16f, .16f, .31f, .31f,
   1010                                  .62f, .62f, 1, 1, 1 };
   1011         static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(radialPos),
   1012                       "color/pos size mismatch");
   1013 
   1014         return SkShader::MakeComposeShader(sweep,
   1015                                            SkGradientShader::MakeRadial(center, 100, colors,
   1016                                                                         radialPos,
   1017                                                                         SK_ARRAY_COUNT(radialPos),
   1018                                                                         SkShader::kClamp_TileMode),
   1019                                            SkBlendMode::kExclusion);
   1020     });
   1021 }
   1022 
   1023 DEF_SIMPLE_GM(sweep_tiling, canvas, 690, 512) {
   1024     static constexpr SkScalar size = 160;
   1025     static constexpr SkColor colors[] = { SK_ColorBLUE, SK_ColorYELLOW, SK_ColorGREEN };
   1026     static constexpr SkScalar   pos[] = { 0, .25f, .50f };
   1027     static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "size mismatch");
   1028 
   1029     static constexpr SkShader::TileMode modes[] = { SkShader::kClamp_TileMode,
   1030                                                     SkShader::kRepeat_TileMode,
   1031                                                     SkShader::kMirror_TileMode };
   1032 
   1033     static const struct {
   1034         SkScalar start, end;
   1035     } angles[] = {
   1036         { -330, -270 },
   1037         {   30,   90 },
   1038         {  390,  450 },
   1039         {  -30,  800 },
   1040     };
   1041 
   1042     SkPaint p;
   1043     const SkRect r = SkRect::MakeWH(size, size);
   1044 
   1045     for (auto mode : modes) {
   1046         {
   1047             SkAutoCanvasRestore acr(canvas, true);
   1048 
   1049             for (auto angle : angles) {
   1050                 p.setShader(SkGradientShader::MakeSweep(size / 2, size / 2, colors, pos,
   1051                                                         SK_ARRAY_COUNT(colors), mode,
   1052                                                         angle.start, angle.end, 0, nullptr));
   1053 
   1054                 canvas->drawRect(r, p);
   1055                 canvas->translate(size * 1.1f, 0);
   1056             }
   1057         }
   1058         canvas->translate(0, size * 1.1f);
   1059     }
   1060 }
   1061 
   1062 // Exercises the special-case Ganesh gradient effects.
   1063 DEF_SIMPLE_GM(gradients_interesting, canvas, 640, 1300) {
   1064     static const SkColor colors2[] = { SK_ColorRED, SK_ColorBLUE };
   1065     static const SkColor colors3[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorBLUE };
   1066     static const SkColor colors4[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorYELLOW, SK_ColorBLUE };
   1067 
   1068     static const SkScalar softRight[]  = { 0, .999f,   1 }; // Based on Android launcher "clipping"
   1069     static const SkScalar hardLeft[]   = { 0,     0,   1 };
   1070     static const SkScalar hardRight[]  = { 0,     1,   1 };
   1071     static const SkScalar hardCenter[] = { 0,   .5f, .5f, 1 };
   1072 
   1073     static const struct {
   1074         const SkColor*  colors;
   1075         const SkScalar* pos;
   1076         int             count;
   1077     } configs[] = {
   1078         { colors2,    nullptr, 2 }, // kTwo_ColorType
   1079         { colors3,    nullptr, 3 }, // kThree_ColorType (simple)
   1080         { colors3,  softRight, 3 }, // kThree_ColorType (tricky)
   1081         { colors3,   hardLeft, 3 }, // kHardStopLeftEdged_ColorType
   1082         { colors3,  hardRight, 3 }, // kHardStopRightEdged_ColorType
   1083         { colors4, hardCenter, 4 }, // kSingleHardStop_ColorType
   1084     };
   1085 
   1086     static const SkShader::TileMode modes[] = {
   1087         SkShader::kClamp_TileMode,
   1088         SkShader::kRepeat_TileMode,
   1089         SkShader::kMirror_TileMode,
   1090     };
   1091 
   1092     static constexpr SkScalar size = 200;
   1093     static const SkPoint pts[] = { { size / 3, size / 3 }, { size * 2 / 3, size * 2 / 3} };
   1094 
   1095     SkPaint p;
   1096     for (const auto& cfg : configs) {
   1097         {
   1098             SkAutoCanvasRestore acr(canvas, true);
   1099             for (auto mode : modes) {
   1100                 p.setShader(SkGradientShader::MakeLinear(pts, cfg.colors, cfg.pos, cfg.count,
   1101                                                          mode));
   1102                 canvas->drawRect(SkRect::MakeWH(size, size), p);
   1103                 canvas->translate(size * 1.1f, 0);
   1104             }
   1105         }
   1106         canvas->translate(0, size * 1.1f);
   1107     }
   1108 }
   1109