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