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