• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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/gm.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkFont.h"
12 #include "include/core/SkFontMetrics.h"
13 #include "include/core/SkGraphics.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkSize.h"
18 #include "include/core/SkString.h"
19 #include "include/core/SkTypeface.h"
20 #include "tools/Resources.h"
21 #include "tools/ToolUtils.h"
22 
23 #include <string.h>
24 #include <initializer_list>
25 
26 namespace skiagm {
27 
28 namespace {
29 const SkScalar kTextSizes[] = {12, 18, 30, 120};
30 const char kTestFontName[] = "fonts/test_glyphs-glyf_colr_1.ttf";
31 const char kTestFontNameVariable[] = "fonts/test_glyphs-glyf_colr_1_variable.ttf";
32 const SkScalar xWidth = 1200;
33 const SkScalar xTranslate = 200;
34 }
35 
36 class ColrV1GM : public GM {
37 public:
ColrV1GM(const char * testName,SkSpan<const uint32_t> codepoints,SkScalar skewX,SkScalar rotateDeg,std::initializer_list<SkFontArguments::VariationPosition::Coordinate> specifiedVariations)38     ColrV1GM(const char* testName,
39              SkSpan<const uint32_t> codepoints,
40              SkScalar skewX,
41              SkScalar rotateDeg,
42              std::initializer_list<SkFontArguments::VariationPosition::Coordinate>
43                      specifiedVariations)
44             : fTestName(testName)
45             , fCodepoints(codepoints)
46             , fSkewX(skewX)
47             , fRotateDeg(rotateDeg) {
48         fVariationPosition.coordinateCount = specifiedVariations.size();
49         fCoordinates = std::make_unique<SkFontArguments::VariationPosition::Coordinate[]>(
50                 specifiedVariations.size());
51         for (size_t i = 0; i < specifiedVariations.size(); ++i) {
52             fCoordinates[i] = std::data(specifiedVariations)[i];
53         }
54 
55         fVariationPosition.coordinates = fCoordinates.get();
56     }
57 
58 protected:
onOnceBeforeDraw()59     void onOnceBeforeDraw() override {
60         if (fVariationPosition.coordinateCount) {
61             fTypeface = MakeResourceAsTypeface(kTestFontNameVariable);
62         } else {
63             fTypeface = MakeResourceAsTypeface(kTestFontName);
64         }
65         fVariationSliders = ToolUtils::VariationSliders(fTypeface.get(), fVariationPosition);
66     }
67 
onShortName()68     SkString onShortName() override {
69         SkASSERT(!fTestName.isEmpty());
70         SkString gm_name = SkStringPrintf("colrv1_%s", fTestName.c_str());
71 
72         if (fSkewX) {
73             gm_name.append(SkStringPrintf("_skew_%.2f", fSkewX));
74         }
75 
76         if (fRotateDeg) {
77             gm_name.append(SkStringPrintf("_rotate_%.2f", fRotateDeg));
78         }
79 
80         for (int i = 0; i < fVariationPosition.coordinateCount; ++i) {
81             SkString tagName = ToolUtils::VariationSliders::tagToString(
82                     fVariationPosition.coordinates[i].axis);
83             gm_name.append(SkStringPrintf(
84                     "_%s_%.2f", tagName.c_str(), fVariationPosition.coordinates[i].value));
85         }
86 
87         return gm_name;
88     }
89 
onGetControls(SkMetaData * controls)90     bool onGetControls(SkMetaData* controls) override {
91         return fVariationSliders.writeControls(controls);
92     }
93 
onSetControls(const SkMetaData & controls)94     void onSetControls(const SkMetaData& controls) override {
95         return fVariationSliders.readControls(controls);
96     }
97 
onISize()98     SkISize onISize() override {
99         // Sweep tests get a slightly wider canvas so that glyphs from one group fit in one row.
100         if (fTestName.equals("sweep_varsweep")) {
101             return SkISize::Make(xWidth + 500, xWidth);
102         }
103         return SkISize::Make(xWidth, xWidth);
104     }
105 
makeVariedTypeface()106     sk_sp<SkTypeface> makeVariedTypeface() {
107         if (!fTypeface) {
108             return nullptr;
109         }
110         SkSpan<const SkFontArguments::VariationPosition::Coordinate> coords =
111                 fVariationSliders.getCoordinates();
112         SkFontArguments::VariationPosition varPos = {coords.data(),
113                                                      static_cast<int>(coords.size())};
114         SkFontArguments args;
115         args.setVariationDesignPosition(varPos);
116         return fTypeface->makeClone(args);
117     }
118 
onDraw(SkCanvas * canvas,SkString * errorMsg)119     DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
120         canvas->drawColor(SK_ColorWHITE);
121         SkPaint paint;
122 
123         canvas->translate(xTranslate, 20);
124 
125         if (!fTypeface) {
126           *errorMsg = "Did not recognize COLR v1 font format.";
127           return DrawResult::kSkip;
128         }
129 
130         canvas->rotate(fRotateDeg);
131         canvas->skew(fSkewX, 0);
132 
133         SkFont font(makeVariedTypeface());
134 
135         SkFontMetrics metrics;
136         SkScalar y = 0;
137         std::vector<SkColor> paint_colors = {
138                 SK_ColorBLACK, SK_ColorGREEN, SK_ColorRED, SK_ColorBLUE};
139         auto paint_color_iterator = paint_colors.begin();
140         for (SkScalar textSize : kTextSizes) {
141             font.setSize(textSize);
142             font.getMetrics(&metrics);
143             SkScalar y_shift = -(metrics.fAscent + metrics.fDescent + metrics.fLeading) * 1.2;
144             y += y_shift;
145             paint.setColor(*paint_color_iterator);
146             int x = 0;
147             // Perform simple line breaking to fit more glyphs into the GM canvas.
148             for (size_t i = 0; i < fCodepoints.size(); ++i) {
149                 canvas->drawSimpleText(&fCodepoints[i],
150                                        sizeof(uint32_t),
151                                        SkTextEncoding::kUTF32,
152                                        x,
153                                        y,
154                                        font,
155                                        paint);
156                 SkScalar glyphAdvance = font.measureText(
157                         &fCodepoints[i], sizeof(uint32_t), SkTextEncoding::kUTF32, nullptr);
158                 if (x + glyphAdvance < onISize().width() - xTranslate) {
159                     x += glyphAdvance + glyphAdvance * 0.05f;
160                 } else {
161                     y += y_shift;
162                     x = 0;
163                 }
164             }
165             paint_color_iterator++;
166         }
167         return DrawResult::kOk;
168     }
169 
170 private:
171     using INHERITED = GM;
172 
173     SkString fTestName;
174     sk_sp<SkTypeface> fTypeface;
175     SkSpan<const uint32_t> fCodepoints;
176     SkScalar fSkewX;
177     SkScalar fRotateDeg;
178     std::unique_ptr<SkFontArguments::VariationPosition::Coordinate[]> fCoordinates;
179     SkFontArguments::VariationPosition fVariationPosition;
180     ToolUtils::VariationSliders fVariationSliders;
181 };
182 
183 // Generated using test glyphs generator script from https://github.com/googlefonts/color-fonts:
184 // $ python3 config/test_glyphs-glyf_colr_1.py -vvv  --generate-descriptions fonts/
185 // Regenerate descriptions and paste the generated arrays here when updating the test font.
186 namespace ColrV1TestDefinitions {
187 const uint32_t gradient_stops_repeat[] = {0xf0100, 0xf0101, 0xf0102, 0xf0103};
188 const uint32_t sweep_varsweep[] = {
189         0xf0200, 0xf0201, 0xf0202, 0xf0203, 0xf0204, 0xf0205, 0xf0206, 0xf0207, 0xf0208,
190         0xf0209, 0xf020a, 0xf020b, 0xf020c, 0xf020d, 0xf020e, 0xf020f, 0xf0210, 0xf0211,
191         0xf0212, 0xf0213, 0xf0214, 0xf0215, 0xf0216, 0xf0217, 0xf0218, 0xf0219, 0xf021a,
192         0xf021b, 0xf021c, 0xf021d, 0xf021e, 0xf021f, 0xf0220, 0xf0221, 0xf0222, 0xf0223,
193         0xf0224, 0xf0225, 0xf0226, 0xf0227, 0xf0228, 0xf0229, 0xf022a, 0xf022b, 0xf022c,
194         0xf022d, 0xf022e, 0xf022f, 0xf0230, 0xf0231, 0xf0232, 0xf0233, 0xf0234, 0xf0235,
195         0xf0236, 0xf0237, 0xf0238, 0xf0239, 0xf023a, 0xf023b, 0xf023c, 0xf023d, 0xf023e,
196         0xf023f, 0xf0240, 0xf0241, 0xf0242, 0xf0243, 0xf0244, 0xf0245, 0xf0246, 0xf0247};
197 const uint32_t paint_scale[] = {0xf0300, 0xf0301, 0xf0302, 0xf0303, 0xf0304, 0xf0305};
198 const uint32_t extend_mode[] = {
199         0xf0500, 0xf0501, 0xf0502, 0xf0503, 0xf0504, 0xf0505, 0xf0506, 0xf0507, 0xf0508};
200 const uint32_t paint_rotate[] = {0xf0600, 0xf0601, 0xf0602, 0xf0603};
201 const uint32_t paint_skew[] = {0xf0700, 0xf0701, 0xf0702, 0xf0703, 0xf0704, 0xf0705};
202 const uint32_t paint_transform[] = {0xf0800, 0xf0801, 0xf0802, 0xf0803};
203 const uint32_t paint_translate[] = {0xf0900, 0xf0901, 0xf0902, 0xf0903, 0xf0904, 0xf0905, 0xf0906};
204 const uint32_t composite_mode[] = {0xf0a00, 0xf0a01, 0xf0a02, 0xf0a03, 0xf0a04, 0xf0a05, 0xf0a06,
205                                    0xf0a07, 0xf0a08, 0xf0a09, 0xf0a0a, 0xf0a0b, 0xf0a0c, 0xf0a0d,
206                                    0xf0a0e, 0xf0a0f, 0xf0a10, 0xf0a11, 0xf0a12, 0xf0a13, 0xf0a14,
207                                    0xf0a15, 0xf0a16, 0xf0a17, 0xf0a18, 0xf0a19, 0xf0a1a, 0xf0a1b};
208 const uint32_t foreground_color[] = {
209         0xf0b00, 0xf0b01, 0xf0b02, 0xf0b03, 0xf0b04, 0xf0b05, 0xf0b06, 0xf0b07};
210 const uint32_t clipbox[] = {0xf0c00, 0xf0c01, 0xf0c02, 0xf0c03, 0xf0c04};
211 const uint32_t gradient_p2_skewed[] = {0xf0d00};
212 const uint32_t variable_alpha[] = {0xf1000};
213 };  // namespace ColrV1TestDefinitions
214 
215 namespace {
F(const char * name,SkSpan<const uint32_t> codepoints,SkScalar skewX,SkScalar rotateDeg,std::initializer_list<SkFontArguments::VariationPosition::Coordinate> variations)216 std::unique_ptr<ColrV1GM> F(
217     const char* name,
218     SkSpan<const uint32_t> codepoints,
219     SkScalar skewX,
220     SkScalar rotateDeg,
221     std::initializer_list<SkFontArguments::VariationPosition::Coordinate> variations)
222 {
223     return std::make_unique<ColrV1GM>(name, codepoints, skewX, rotateDeg, variations);
224 }
225 
operator ""_t(const char * tagName,size_t size)226 SkFourByteTag constexpr operator "" _t(const char* tagName, size_t size) {
227     SkASSERT(size == 4);
228     return SkSetFourByteTag(tagName[0], tagName[1], tagName[2], tagName[3]);
229 }
230 }
231 #define C(TEST_CATEGORY) #TEST_CATEGORY, ColrV1TestDefinitions::TEST_CATEGORY
232 DEF_GM(return F(C(clipbox),                0.0f,  0.0f, {}))
233 DEF_GM(return F(C(clipbox),                0.0f,  0.0f, {{"CLIO"_t, 200.f}}))
234 DEF_GM(return F(C(composite_mode),         0.0f,  0.0f, {}))
235 DEF_GM(return F(C(composite_mode),        -0.5f,  0.0f, {}))
236 DEF_GM(return F(C(composite_mode),        -0.5f, 20.0f, {}))
237 DEF_GM(return F(C(composite_mode),         0.0f, 20.0f, {}))
238 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {}))
239 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL1"_t, -0.25f}, {"COL3"_t, 0.25f}}))
240 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL1"_t, 0.5f}, {"COL3"_t, -0.5f}}))
241 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL3"_t, 0.5f}}))
242 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL3"_t, 1.f}}))
243 // Radial gradient tests where radii become negative
244 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL1"_t, -1.5f}}))
245 // Both radii negative and equal, nothing should render.
246 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRR0"_t, -200.f}, {"GRR1"_t, -300.f}}))
247 // Small cones opening to the right.
248 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRX0"_t, -1000.f}, {"GRX1"_t, -1000.f}, {"GRR0"_t, -1000.f}, {"GRR1"_t, -900.f}}))
249 // Small cones opening to the left.
250 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRX0"_t, 1000.f}, {"GRX1"_t, -1000.f}, {"GRR0"_t, -1000.f}, {"GRR1"_t, 200.f}}))
251 // Pad cone should appear green.
252 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRR0"_t, -50.f}, {"COL3"_t, -2.f}, {"COL2"_t, -2.f}, {"COL1"_t, -0.9f}}))
253 // Pad cone should appear red.
254 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRR0"_t, -50.f}, {"COL3"_t, -2.f}, {"COL2"_t, -2.f}, {"COL1"_t, -1.1f}}))
255 // Hard boundary for pad mode, should appear on the right inside the glyph for linear and radial.
256 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL3"_t, 1.f}, {"COL2"_t, 1.5f}, {"COL1"_t, 2.f}}))
257 // Extend mode with rotation or skew below.
258 DEF_GM(return F(C(extend_mode),           -0.5f,  0.0f, {}))
259 DEF_GM(return F(C(extend_mode),           -0.5f, 20.0f, {}))
260 DEF_GM(return F(C(extend_mode),            0.0f, 20.0f, {}))
261 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"COL2"_t, -0.3f}}))
262 DEF_GM(return F(C(extend_mode),            0.0f,  0.0f, {{"GRR0"_t, 430.f}, {"GRR1"_t, 40.f}}))
263 DEF_GM(return F(C(foreground_color),       0.0f,  0.0f, {}))
264 DEF_GM(return F(C(gradient_p2_skewed),     0.0f,  0.0f, {}))
265 DEF_GM(return F(C(gradient_stops_repeat),  0.0f,  0.0f, {}))
266 DEF_GM(return F(C(gradient_stops_repeat), -0.5f,  0.0f, {}))
267 DEF_GM(return F(C(gradient_stops_repeat), -0.5f, 20.0f, {}))
268 DEF_GM(return F(C(gradient_stops_repeat),  0.0f, 20.0f, {}))
269 DEF_GM(return F(C(paint_rotate),           0.0f,  0.0f, {}))
270 DEF_GM(return F(C(paint_rotate),           0.0f,  0.0f, {{"ROTA"_t, 40.f}}))
271 DEF_GM(return F(C(paint_rotate),           0.0f,  0.0f, {{"ROTX"_t, -250.f}, {"ROTY"_t, -250.f}}))
272 DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {}))
273 DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {{"SCOX"_t, 200.f}, {"SCOY"_t, 200.f}}))
274 DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {{"SCSX"_t, 0.25f}, {"SCOY"_t, 0.25f}}))
275 DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {{"SCSX"_t, -1.f}, {"SCOY"_t, -1.f}}))
276 DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {}))
277 DEF_GM(return F(C(paint_scale),            0.0f,  0.0f, {}))
278 DEF_GM(return F(C(paint_skew),             0.0f,  0.0f, {}))
279 DEF_GM(return F(C(paint_skew),             0.0f,  0.0f, {{"SKXA"_t, 20.f}}))
280 DEF_GM(return F(C(paint_skew),             0.0f,  0.0f, {{"SKYA"_t, 20.f}}))
281 DEF_GM(return F(C(paint_skew),             0.0f,  0.0f, {{"SKCX"_t, 200.f},{"SKCY"_t, 200.f}}))
282 DEF_GM(return F(C(paint_transform),        0.0f,  0.0f, {}))
283 DEF_GM(return F(C(paint_translate),        0.0f,  0.0f, {}))
284 DEF_GM(return F(C(paint_translate),        0.0f,  0.0f, {{"TLDX"_t, 100.f}, {"TLDY"_t, 100.f}}))
285 DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {}))
286 DEF_GM(return F(C(sweep_varsweep),        -0.5f,  0.0f, {}))
287 DEF_GM(return F(C(sweep_varsweep),        -0.5f, 20.0f, {}))
288 DEF_GM(return F(C(sweep_varsweep),         0.0f, 20.0f, {}))
289 DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {{"SWPS"_t, 0.f}}))
290 DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {{"SWPS"_t, 90.f}}))
291 DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {{"SWPE"_t, -90.f}}))
292 DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {{"SWPE"_t, -45.f}}))
293 DEF_GM(return F(C(sweep_varsweep),         0.0f,  0.0f, {{"SWPS"_t, -45.f},{"SWPE"_t, 45.f}}))
294 DEF_GM(return F(C(sweep_varsweep),
295                 0.0f,
296                 0.0f,
297                 {{"SWC1"_t, -0.25f},
298                  {"SWC2"_t, 0.083333333f},
299                  {"SWC3"_t, 0.083333333f},
300                  {"SWC4"_t, +0.25f}}))
301 DEF_GM(return F(C(variable_alpha),         0.0f,  0.0f, {}))
302 DEF_GM(return F(C(variable_alpha),         0.0f,  0.0f, {{"APH1"_t, -0.7f}}))
303 DEF_GM(return F(C(variable_alpha),         0.0f,  0.0f, {{"APH2"_t, -0.7f}, {"APH3"_t, -0.2f}}))
304 
305 }  // namespace skiagm
306