1 /*
2  * Copyright 2018 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 "SkFont.h"
10 #include "SkGradientShader.h"
11 
12 // NOTE: The positions define hardstops for the red and green borders. For the repeating degenerate
13 // gradients, that means the red and green are never visible, so the average color used should only
14 // be based off of the white, blue, black blend.
15 static const SkColor COLORS[] = { SK_ColorRED, SK_ColorWHITE, SK_ColorBLUE,
16                                   SK_ColorBLACK, SK_ColorGREEN };
17 static const SkScalar POS[] = { 0.0, 0.0, 0.5, 1.0, 1.0 };
18 static const int COLOR_CT = SK_ARRAY_COUNT(COLORS);
19 
20 static const SkShader::TileMode TILE_MODES[] = { SkShader::kDecal_TileMode,
21                                                  SkShader::kRepeat_TileMode,
22                                                  SkShader::kMirror_TileMode,
23                                                  SkShader::kClamp_TileMode };
24 static const char* TILE_NAMES[] = { "decal", "repeat", "mirror", "clamp" };
25 static const int TILE_MODE_CT = SK_ARRAY_COUNT(TILE_MODES);
26 
27 static constexpr int TILE_SIZE = 100;
28 static constexpr int TILE_GAP = 10;
29 
30 static const SkPoint CENTER = SkPoint::Make(TILE_SIZE / 2, TILE_SIZE / 2);
31 
32 typedef sk_sp<SkShader> (*GradientFactory)(SkShader::TileMode tm);
33 
draw_tile_header(SkCanvas * canvas)34 static void draw_tile_header(SkCanvas* canvas) {
35     canvas->save();
36 
37     for (int i = 0; i < TILE_MODE_CT; ++i) {
38         canvas->drawString(TILE_NAMES[i], 0, 0, SkFont(), SkPaint());
39         canvas->translate(TILE_SIZE + TILE_GAP, 0);
40     }
41 
42     canvas->restore();
43 
44     // Now adjust to start at rows below the header
45     canvas->translate(0, 2 * TILE_GAP);
46 }
47 
draw_row(SkCanvas * canvas,const char * desc,GradientFactory factory)48 static void draw_row(SkCanvas* canvas, const char* desc, GradientFactory factory) {
49     canvas->save();
50 
51     SkPaint text;
52     text.setAntiAlias(true);
53 
54     canvas->translate(0, TILE_GAP);
55     canvas->drawString(desc, 0, 0, SkFont(), text);
56     canvas->translate(0, TILE_GAP);
57 
58     SkPaint paint;
59     paint.setColor(SK_ColorBLACK);
60     paint.setStyle(SkPaint::kStrokeAndFill_Style);
61     paint.setStrokeWidth(2.0f);
62 
63     for (int i = 0; i < TILE_MODE_CT; ++i) {
64         paint.setShader(factory(TILE_MODES[i]));
65         canvas->drawRect(SkRect::MakeWH(TILE_SIZE, TILE_SIZE), paint);
66         canvas->translate(TILE_SIZE + TILE_GAP, 0);
67     }
68 
69     canvas->restore();
70 
71     // Now adjust to start the next row below this one (1 gap for text and 2 gap for margin)
72     canvas->translate(0, 3 * TILE_GAP + TILE_SIZE);
73 }
74 
make_linear(SkShader::TileMode mode)75 static sk_sp<SkShader> make_linear(SkShader::TileMode mode) {
76     // Same position
77     SkPoint pts[2] = {CENTER, CENTER};
78     return SkGradientShader::MakeLinear(pts, COLORS, POS, COLOR_CT, mode);
79 }
80 
make_radial(SkShader::TileMode mode)81 static sk_sp<SkShader> make_radial(SkShader::TileMode mode) {
82     // Radius = 0
83     return SkGradientShader::MakeRadial(CENTER, 0.0, COLORS, POS, COLOR_CT, mode);
84 }
85 
make_sweep(SkShader::TileMode mode)86 static sk_sp<SkShader> make_sweep(SkShader::TileMode mode) {
87     // Start and end angles at 45
88     static constexpr SkScalar SWEEP_ANG = 45.0;
89     return SkGradientShader::MakeSweep(CENTER.fX, CENTER.fY, COLORS, POS, COLOR_CT, mode,
90                                        SWEEP_ANG, SWEEP_ANG, 0, nullptr);
91 }
92 
make_sweep_zero_ang(SkShader::TileMode mode)93 static sk_sp<SkShader> make_sweep_zero_ang(SkShader::TileMode mode) {
94     // Start and end angles at 0
95     return SkGradientShader::MakeSweep(CENTER.fX, CENTER.fY, COLORS, POS, COLOR_CT, mode,
96                                        0.0, 0.0, 0, nullptr);
97 }
98 
make_2pt_conic(SkShader::TileMode mode)99 static sk_sp<SkShader> make_2pt_conic(SkShader::TileMode mode) {
100     // Start and end radius = TILE_SIZE, same position
101     return SkGradientShader::MakeTwoPointConical(CENTER, TILE_SIZE / 2, CENTER, TILE_SIZE / 2,
102                                                  COLORS, POS, COLOR_CT, mode);
103 }
104 
make_2pt_conic_zero_rad(SkShader::TileMode mode)105 static sk_sp<SkShader> make_2pt_conic_zero_rad(SkShader::TileMode mode) {
106     // Start and end radius = 0, same position
107     return SkGradientShader::MakeTwoPointConical(CENTER, 0.0, CENTER, 0.0, COLORS, POS,
108                                                  COLOR_CT, mode);
109 }
110 
111 class DegenerateGradientGM : public skiagm::GM {
112 public:
DegenerateGradientGM()113     DegenerateGradientGM() {
114 
115     }
116 
117 protected:
onShortName()118     SkString onShortName() override {
119         return SkString("degenerate_gradients");
120     }
121 
onISize()122     SkISize onISize() override {
123         return SkISize::Make(800, 800);
124     }
125 
onDraw(SkCanvas * canvas)126     void onDraw(SkCanvas* canvas) override {
127         canvas->translate(3 * TILE_GAP, 3 * TILE_GAP);
128         draw_tile_header(canvas);
129 
130         draw_row(canvas, "linear: empty, blue, blue, green", make_linear);
131         draw_row(canvas, "radial:  empty, blue, blue, green", make_radial);
132         draw_row(canvas, "sweep-0: empty, blue, blue, green", make_sweep_zero_ang);
133         draw_row(canvas, "sweep-45: empty, blue, blue, red 45 degree sector then green",
134                  make_sweep);
135         draw_row(canvas, "2pt-conic-0: empty, blue, blue, green", make_2pt_conic_zero_rad);
136         draw_row(canvas, "2pt-conic-1: empty, blue, blue, full red circle on green",
137                  make_2pt_conic);
138     }
139 
140 private:
141     typedef skiagm::GM INHERITED;
142 };
143 
144 DEF_GM(return new DegenerateGradientGM;)
145