xref: /third_party/skia/gm/fillrect_gradient.cpp (revision cb93a386)
1/*
2 * Copyright 2021 Google LLC
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/*
9 * This GM creates the same gradients as the Chromium test fillrect_gradient:
10 * http://osscs/chromium/chromium/src/+/main:third_party/blink/web_tests/fast/canvas/fillrect_gradient.html
11 */
12
13#include "gm/gm.h"
14#include "include/core/SkCanvas.h"
15#include "include/core/SkColor.h"
16#include "include/core/SkPaint.h"
17#include "include/core/SkPoint.h"
18#include "include/core/SkRect.h"
19#include "include/core/SkRefCnt.h"
20#include "include/core/SkScalar.h"
21#include "include/core/SkShader.h"
22#include "include/core/SkSize.h"
23#include "include/core/SkString.h"
24#include "include/core/SkTileMode.h"
25#include "include/effects/SkGradientShader.h"
26
27const int kCellSize = 50;
28const int kNumColumns = 2;
29const int kNumRows = 9;
30const int kPadSize = 10;
31
32class FillrectGradientGM : public skiagm::GM {
33public:
34    FillrectGradientGM() {}
35
36protected:
37    struct GradientStop {
38        float pos;
39        SkColor color;
40    };
41
42    SkString onShortName() override {
43        return SkString("fillrect_gradient");
44    }
45
46    SkISize onISize() override {
47        return SkISize::Make(kNumColumns * (kCellSize + kPadSize),
48                             kNumRows * (kCellSize + kPadSize));
49    }
50
51    void drawGradient(SkCanvas* canvas, std::initializer_list<GradientStop> stops) {
52        std::vector<SkColor> colors;
53        std::vector<SkScalar> positions;
54        colors.reserve(stops.size());
55        positions.reserve(stops.size());
56
57        for (const GradientStop& stop : stops) {
58            colors.push_back(stop.color);
59            positions.push_back(stop.pos);
60        }
61
62        static constexpr SkPoint points[] = {
63            SkPoint::Make(kCellSize, 0),
64            SkPoint::Make(kCellSize, kCellSize),
65        };
66
67        // Draw the gradient linearly.
68        sk_sp<SkShader> shader = SkGradientShader::MakeLinear(points,
69                                                              colors.data(),
70                                                              positions.data(),
71                                                              colors.size(),
72                                                              SkTileMode::kClamp);
73        SkPaint paint;
74        paint.setShader(shader);
75        canvas->drawRect(SkRect::MakeXYWH(0, 0, kCellSize, kCellSize), paint);
76
77        canvas->save();
78        canvas->translate(kCellSize + kPadSize, 0);
79
80        // Draw the gradient radially.
81        shader = SkGradientShader::MakeRadial(SkPoint::Make(kCellSize / 2, kCellSize / 2),
82                                              kCellSize / 2,
83                                              colors.data(),
84                                              positions.data(),
85                                              colors.size(),
86                                              SkTileMode::kClamp);
87        paint.setShader(shader);
88        canvas->drawRect(SkRect::MakeXYWH(0, 0, kCellSize, kCellSize), paint);
89
90        canvas->restore();
91        canvas->translate(0, kCellSize + kPadSize);
92    }
93
94    void onDraw(SkCanvas* canvas) override {
95        // Simple gradient: Green to white
96        this->drawGradient(canvas, {{0.0f, SK_ColorGREEN}, {1.0f, SK_ColorWHITE}});
97
98        // Multiple sections: Green to white to red
99        this->drawGradient(canvas,
100                           {{0.0f, SK_ColorGREEN}, {0.5f, SK_ColorWHITE}, {1.0f, SK_ColorRED}});
101
102        // No stops at 0.0 or 1.0: Larger green to white to larger red
103        this->drawGradient(canvas,
104                           {{0.4f, SK_ColorGREEN}, {0.5f, SK_ColorWHITE}, {0.6f, SK_ColorRED}});
105
106        // Only one stop, at zero: Solid red
107        this->drawGradient(canvas, {{0.0f, SK_ColorRED}});
108
109        // Only one stop, at 1.0: Solid red
110        this->drawGradient(canvas, {{1.0f, SK_ColorRED}});
111
112        // Only one stop, in the middle: Solid red
113        this->drawGradient(canvas, {{0.5f, SK_ColorRED}});
114
115        // Disjoint gradients (multiple stops at the same offset)
116        // Blue to white in the top (inner) half, red to yellow in the bottom (outer) half
117        this->drawGradient(canvas,
118                           {{0.0f, SK_ColorBLUE},
119                            {0.5f, SK_ColorWHITE},
120                            {0.5f, SK_ColorRED},
121                            {1.0f, SK_ColorYELLOW}});
122
123        // Ignored stops: Blue to white, red to yellow (same as previous)
124        this->drawGradient(canvas,
125                           {{0.0f, SK_ColorBLUE},
126                            {0.5f, SK_ColorWHITE},
127                            {0.5f, SK_ColorGRAY},
128                            {0.5f, SK_ColorCYAN},
129                            {0.5f, SK_ColorRED},
130                            {1.0f, SK_ColorYELLOW}});
131
132        // Unsorted stops: Blue to white, red to yellow
133        // Unlike Chrome, we don't sort the stops, so this renders differently than the prior cell.
134        this->drawGradient(canvas,
135                           {{0.5f, SK_ColorWHITE},
136                            {0.5f, SK_ColorGRAY},
137                            {1.0f, SK_ColorYELLOW},
138                            {0.5f, SK_ColorCYAN},
139                            {0.5f, SK_ColorRED},
140                            {0.0f, SK_ColorBLUE}});
141    }
142
143private:
144    using INHERITED = skiagm::GM;
145};
146
147DEF_GM(return new FillrectGradientGM;)
148