xref: /aosp_15_r20/external/skia/tests/FontMgrFontConfigTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2014 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 "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkFont.h"
12 #include "include/core/SkFontArguments.h"
13 #include "include/core/SkFontMgr.h"
14 #include "include/core/SkFontStyle.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPoint.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkStream.h"
20 #include "include/core/SkString.h"
21 #include "include/core/SkTypeface.h"
22 #include "include/core/SkTypes.h"
23 #include "include/encode/SkPngEncoder.h"
24 #include "include/ports/SkFontMgr_fontconfig.h"
25 
26 #ifdef SK_TYPEFACE_FACTORY_FONTATIONS
27 #include "include/ports/SkFontScanner_Fontations.h"
28 #endif
29 #ifdef SK_TYPEFACE_FACTORY_FREETYPE
30 #include "include/ports/SkFontScanner_FreeType.h"
31 #endif
32 #include "tests/Test.h"
33 #include "tools/Resources.h"
34 
35 #include <fontconfig/fontconfig.h>
36 
37 #include <array>
38 #include <memory>
39 
40 namespace {
41 
bitmap_compare(const SkBitmap & ref,const SkBitmap & test)42 bool bitmap_compare(const SkBitmap& ref, const SkBitmap& test) {
43     auto count = 0;
44     for (int y = 0; y < test.height(); ++y) {
45         for (int x = 0; x < test.width(); ++x) {
46             SkColor testColor = test.getColor(x, y);
47             SkColor refColor = ref.getColor(x, y);
48             if (refColor != testColor) {
49                 ++count;
50                 if ((false)) {
51                     SkDebugf("%d: (%d,%d) ", count, x, y);
52                 }
53             }
54         }
55     }
56     return (count == 0);
57 }
58 
build_fontconfig_with_fontfile(const char * fontFilename)59 FcConfig* build_fontconfig_with_fontfile(const char* fontFilename) {
60     FcConfig* config = FcConfigCreate();
61 
62     // FontConfig may modify the passed path (make absolute or other).
63     FcConfigSetSysRoot(config, reinterpret_cast<const FcChar8*>(GetResourcePath("").c_str()));
64     // FontConfig will lexically compare paths against its version of the sysroot.
65     SkString fontFilePath(reinterpret_cast<const char*>(FcConfigGetSysRoot(config)));
66     fontFilePath += fontFilename;
67     FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(fontFilePath.c_str()));
68 
69     FcConfigBuildFonts(config);
70     return config;
71 }
72 
build_fontconfig_from_resources()73 FcConfig* build_fontconfig_from_resources() {
74 
75     SkString path = GetResourcePath("");
76     SkString content;
77     content.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
78                    "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">"
79                    "<fontconfig>\n"
80                    "<dir>/fonts</dir>\n");
81     content.append("<match target=\"font\">\n"
82                    "   <edit name=\"embolden\" mode=\"assign\">\n"
83                    "       <bool>true</bool>\n"
84                    "   </edit>\n"
85                    "</match>");
86     content.append("</fontconfig>\n");
87     FcConfig* fc_config = FcConfigCreate();
88     FcConfigSetSysRoot(fc_config, reinterpret_cast<const FcChar8*>(path.c_str()));
89     if (FcConfigParseAndLoadFromMemory(
90                 fc_config, reinterpret_cast<const FcChar8*>(content.c_str()),
91                 FcTrue) != FcTrue) {
92         SkDebugf("FcConfigParseAndLoadFromMemory\n");
93     }
94     if (FcConfigBuildFonts(fc_config) != FcTrue) {
95         SkDebugf("!FcConfigBuildFonts\n");
96     }
97     return fc_config;
98 }
99 
100 [[maybe_unused]]
write_bitmap(const SkBitmap * bm,const char fileName[])101 static void write_bitmap(const SkBitmap* bm, const char fileName[]) {
102     SkFILEWStream file(fileName);
103     SkAssertResult(file.isValid());
104     SkAssertResult(SkPngEncoder::Encode(&file, bm->pixmap(), {}));
105 }
106 
107 }  // namespace
108 
DEF_TEST(FontMgrFontConfig,reporter)109 DEF_TEST(FontMgrFontConfig, reporter) {
110     FcConfig* config = build_fontconfig_with_fontfile("/fonts/Distortable.ttf");
111 
112     sk_sp<SkFontMgr> fontMgr(SkFontMgr_New_FontConfig(config, SkFontScanner_Make_FreeType()));
113     sk_sp<SkTypeface> typeface(fontMgr->legacyMakeTypeface("Distortable", SkFontStyle()));
114     if (!typeface) {
115         ERRORF(reporter, "Could not find typeface. FcVersion: %d", FcGetVersion());
116         return;
117     }
118 
119     SkBitmap bitmapStream;
120     bitmapStream.allocN32Pixels(64, 64);
121     SkCanvas canvasStream(bitmapStream);
122     canvasStream.drawColor(SK_ColorWHITE);
123 
124     SkBitmap bitmapClone;
125     bitmapClone.allocN32Pixels(64, 64);
126     SkCanvas canvasClone(bitmapClone);
127     canvasStream.drawColor(SK_ColorWHITE);
128 
129     SkPaint paint;
130     paint.setColor(SK_ColorGRAY);
131 
132     constexpr float kTextSize = 20;
133 
134     std::unique_ptr<SkStreamAsset> distortableStream(
135             GetResourceAsStream("fonts/Distortable.ttf"));
136     if (!distortableStream) {
137         return;
138     }
139 
140     SkPoint point = SkPoint::Make(20.0f, 20.0f);
141     SkFourByteTag tag = SkSetFourByteTag('w', 'g', 'h', 't');
142 
143     for (int i = 0; i < 10; ++i) {
144         SkScalar styleValue =
145             SkDoubleToScalar(0.5 + i * ((2.0 - 0.5) / 10));
146         SkFontArguments::VariationPosition::Coordinate
147             coordinates[] = {{tag, styleValue}};
148         SkFontArguments::VariationPosition
149             position = {coordinates, std::size(coordinates)};
150 
151         SkFont fontStream(
152             fontMgr->makeFromStream(distortableStream->duplicate(),
153                                     SkFontArguments().setVariationDesignPosition(position)),
154             kTextSize);
155         fontStream.setEdging(SkFont::Edging::kSubpixelAntiAlias);
156 
157         SkFont fontClone(
158             typeface->makeClone(SkFontArguments().setVariationDesignPosition(position)), kTextSize);
159         fontClone.setEdging(SkFont::Edging::kSubpixelAntiAlias);
160 
161         constexpr char text[] = "abc";
162 
163         canvasStream.drawColor(SK_ColorWHITE);
164         canvasStream.drawString(text, point.fX, point.fY, fontStream, paint);
165 
166         canvasClone.drawColor(SK_ColorWHITE);
167         canvasClone.drawString(text, point.fX, point.fY, fontClone, paint);
168 
169         bool success = bitmap_compare(bitmapStream, bitmapClone);
170         REPORTER_ASSERT(reporter, success);
171     }
172 }
173 
testAllBold(sk_sp<SkFontMgr> fontMgr,skiatest::Reporter * reporter)174 void testAllBold(sk_sp<SkFontMgr> fontMgr, skiatest::Reporter* reporter) {
175     constexpr float kTextSize = 20;
176     constexpr char text[] = "abc";
177 
178     SkString filePath = GetResourcePath("fonts/Roboto-Regular.ttf");
179     sk_sp<SkTypeface> dataTypeface(fontMgr->makeFromFile(filePath.c_str(), 0));
180     if (!dataTypeface) {
181         ERRORF(reporter, "Could not find data typeface. FcVersion: %d", FcGetVersion());
182         return;
183     }
184 
185     SkFont dataFont(dataTypeface, kTextSize);
186     dataFont.setEmbolden(true);
187 
188     sk_sp<SkTypeface> matchTypeface(fontMgr->matchFamilyStyle("Roboto", SkFontStyle()));
189     if (!matchTypeface) {
190         ERRORF(reporter, "Could not find match typeface. FcVersion: %d", FcGetVersion());
191         return;
192     }
193     SkFont matchFont(matchTypeface, kTextSize);
194 
195     SkBitmap bitmapData;
196     bitmapData.allocN32Pixels(64, 64);
197     SkCanvas canvasData(bitmapData);
198 
199     SkBitmap bitmapMatch;
200     bitmapMatch.allocN32Pixels(64, 64);
201     SkCanvas canvasMatch(bitmapMatch);
202 
203     SkPaint paint;
204     paint.setColor(SK_ColorBLACK);
205 
206     canvasData.drawColor(SK_ColorGRAY);
207     canvasData.drawString(text, 20.0f, 20.0f, dataFont, paint);
208     if ((false)) {
209         // In case we wonder what's been painted
210         SkString dataPath = GetResourcePath("/fonts/data.png");
211         write_bitmap(&bitmapData, dataPath.c_str());
212     }
213 
214     canvasMatch.drawColor(SK_ColorGRAY);
215     canvasMatch.drawString(text, 20.0f, 20.0f, matchFont, paint);
216     if ((false)) {
217         SkString matchPath = GetResourcePath("/fonts/match.png");
218         write_bitmap(&bitmapMatch, matchPath.c_str());
219     }
220 
221     bool success = bitmap_compare(bitmapData, bitmapMatch);
222     REPORTER_ASSERT(reporter, success);
223 }
224 
225 #if defined(SK_TYPEFACE_FACTORY_FREETYPE)
DEF_TEST(FontMgrFontConfig_FreeType_AllBold,reporter)226 DEF_TEST(FontMgrFontConfig_FreeType_AllBold, reporter) {
227 
228     FcConfig* config = build_fontconfig_from_resources();
229     sk_sp<SkFontMgr> fontMgr(SkFontMgr_New_FontConfig(config, SkFontScanner_Make_FreeType()));
230 
231     testAllBold(fontMgr, reporter);
232 }
233 #endif
234 
235 #if defined(SK_TYPEFACE_FACTORY_FONTATIONS)
DEF_TEST(FontMgrFontConfig_Fontations_AllBold,reporter)236 DEF_TEST(FontMgrFontConfig_Fontations_AllBold, reporter) {
237 
238     FcConfig* config = build_fontconfig_from_resources();
239     sk_sp<SkFontMgr> fontMgr(SkFontMgr_New_FontConfig(config, SkFontScanner_Make_FreeType()));
240 
241     testAllBold(fontMgr, reporter);
242 }
243 #endif
244 
245 #if defined(SK_TYPEFACE_FACTORY_FREETYPE) && defined(SK_TYPEFACE_FACTORY_FONTATIONS)
246 // The results may not match but it's still interesting to run sometimes
DEF_TEST_DISABLED(FontMgrFontConfig_MatchFonts,reporter)247 DEF_TEST_DISABLED(FontMgrFontConfig_MatchFonts, reporter) {
248     FcConfig* config = build_fontconfig_from_resources();
249     sk_sp<SkFontMgr> freeTypeFontMgr(
250             SkFontMgr_New_FontConfig(config, SkFontScanner_Make_FreeType()));
251     sk_sp<SkFontMgr> fontationsFontMgr(
252             SkFontMgr_New_FontConfig(config, SkFontScanner_Make_Fontations()));
253 
254     constexpr float kTextSize = 20;
255     constexpr char text[] = "abc";
256 
257     sk_sp<SkTypeface> dataTypeface(freeTypeFontMgr->matchFamilyStyle("Roboto", SkFontStyle()));
258     if (!dataTypeface) {
259         ERRORF(reporter, "Could not find data typeface. FcVersion: %d", FcGetVersion());
260         return;
261     }
262     SkFont dataFont(dataTypeface, kTextSize);
263 
264     sk_sp<SkTypeface> matchTypeface(fontationsFontMgr->matchFamilyStyle("Roboto", SkFontStyle()));
265     if (!matchTypeface) {
266         ERRORF(reporter, "Could not find match typeface. FcVersion: %d", FcGetVersion());
267         return;
268     }
269     SkFont matchFont(matchTypeface, kTextSize);
270 
271     SkBitmap bitmapData;
272     bitmapData.allocN32Pixels(64, 64);
273     SkCanvas canvasData(bitmapData);
274 
275     SkBitmap bitmapMatch;
276     bitmapMatch.allocN32Pixels(64, 64);
277     SkCanvas canvasMatch(bitmapMatch);
278 
279     SkPaint paint;
280     paint.setColor(SK_ColorBLACK);
281 
282     canvasData.drawColor(SK_ColorGRAY);
283     canvasData.drawString(text, 20.0f, 20.0f, dataFont, paint);
284     if ((false)) {
285         // In case we wonder what's been painted
286         SkString dataPath = GetResourcePath("/fonts/data.png");
287         write_bitmap(&bitmapData, dataPath.c_str());
288     }
289 
290     canvasMatch.drawColor(SK_ColorGRAY);
291     canvasMatch.drawString(text, 20.0f, 20.0f, matchFont, paint);
292     if ((false)) {
293         SkString matchPath = GetResourcePath("/fonts/match.png");
294         write_bitmap(&bitmapMatch, matchPath.c_str());
295     }
296 
297     bool success = bitmap_compare(bitmapData, bitmapMatch);
298     REPORTER_ASSERT(reporter, success);
299 }
300 #endif
301