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