xref: /aosp_15_r20/external/skia/gm/fontmgr.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2013 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/SkFontStyle.h"
15 #include "include/core/SkFontTypes.h"
16 #include "include/core/SkGraphics.h"
17 #include "include/core/SkPaint.h"
18 #include "include/core/SkPath.h"
19 #include "include/core/SkPathEffect.h"
20 #include "include/core/SkPoint.h"
21 #include "include/core/SkRect.h"
22 #include "include/core/SkRefCnt.h"
23 #include "include/core/SkScalar.h"
24 #include "include/core/SkSize.h"
25 #include "include/core/SkString.h"
26 #include "include/core/SkTypeface.h"
27 #include "include/core/SkTypes.h"
28 #include "include/effects/SkDashPathEffect.h"
29 #include "src/core/SkFontPriv.h"
30 #include "tools/SkMetaData.h"
31 #include "tools/ToolUtils.h"
32 #include "tools/fonts/FontToolUtils.h"
33 
34 #include <utility>
35 
36 // limit this just so we don't take too long to draw
37 #define MAX_FAMILIES    30
38 
drawString(SkCanvas * canvas,const SkString & text,SkScalar x,SkScalar y,const SkFont & font)39 static SkScalar drawString(SkCanvas* canvas, const SkString& text, SkScalar x,
40                            SkScalar y, const SkFont& font) {
41     canvas->drawString(text, x, y, font, SkPaint());
42     return x + font.measureText(text.c_str(), text.size(), SkTextEncoding::kUTF8);
43 }
44 
drawCharacter(SkCanvas * canvas,uint32_t character,SkScalar x,SkScalar y,const SkFont & origFont,SkFontMgr * fm,const char * fontName,const char * bcp47[],int bcp47Count,const SkFontStyle & fontStyle)45 static SkScalar drawCharacter(SkCanvas* canvas, uint32_t character, SkScalar x,
46                               SkScalar y, const SkFont& origFont, SkFontMgr* fm,
47                               const char* fontName, const char* bcp47[], int bcp47Count,
48                               const SkFontStyle& fontStyle) {
49     SkFont font = origFont;
50     // find typeface containing the requested character and draw it
51     SkString ch;
52     ch.appendUnichar(character);
53     sk_sp<SkTypeface> typeface(fm->matchFamilyStyleCharacter(fontName, fontStyle,
54                                                              bcp47, bcp47Count, character));
55     font.setTypeface(typeface);
56     x = drawString(canvas, ch, x, y, font) + 20;
57 
58     if (nullptr == typeface) {
59         return x;
60     }
61 
62     // repeat the process, but this time use the family name of the typeface
63     // from the first pass.  This emulates the behavior in Blink where it
64     // it expects to get the same glyph when following this pattern.
65     SkString familyName;
66     typeface->getFamilyName(&familyName);
67     font.setTypeface(fm->legacyMakeTypeface(familyName.c_str(), typeface->fontStyle()));
68     return drawString(canvas, ch, x, y, font) + 20;
69 }
70 
71 static const char* zh = "zh";
72 static const char* ja = "ja";
73 
74 class FontMgrGM : public skiagm::GM {
75     sk_sp<SkFontMgr> fFM;
76 
onOnceBeforeDraw()77     void onOnceBeforeDraw() override {
78         SkGraphics::SetFontCacheLimit(16 * 1024 * 1024);
79         fFM = ToolUtils::TestFontMgr();
80     }
81 
getName() const82     SkString getName() const override { return SkString("fontmgr_iter"); }
83 
getISize()84     SkISize getISize() override { return {1536, 768}; }
85 
onDraw(SkCanvas * canvas,SkString * errorMsg)86     DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
87         SkScalar y = 20;
88         SkFont font = ToolUtils::DefaultFont();
89         font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
90         font.setSubpixel(true);
91         font.setSize(17);
92 
93         SkFontMgr* fm = fFM.get();
94         int count = std::min(fm->countFamilies(), MAX_FAMILIES);
95         if (count == 0) {
96             *errorMsg = "No families in SkFontMgr";
97             return DrawResult::kSkip;
98         }
99 
100         for (int i = 0; i < count; ++i) {
101             SkString familyName;
102             fm->getFamilyName(i, &familyName);
103             font.setTypeface(ToolUtils::DefaultTypeface());
104             (void)drawString(canvas, familyName, 20, y, font);
105 
106             SkScalar x = 220;
107 
108             sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
109             for (int j = 0; j < set->count(); ++j) {
110                 SkString sname;
111                 SkFontStyle fs;
112                 set->getStyle(j, &fs, &sname);
113                 sname.appendf(" [%d %d %d]", fs.weight(), fs.width(), fs.slant());
114 
115                 font.setTypeface(sk_sp<SkTypeface>(set->createTypeface(j)));
116                 x = drawString(canvas, sname, x, y, font) + 20;
117 
118                 // check to see that we get different glyphs in japanese and chinese
119                 x = drawCharacter(canvas, 0x5203, x, y, font, fm, familyName.c_str(), &zh, 1, fs);
120                 x = drawCharacter(canvas, 0x5203, x, y, font, fm, familyName.c_str(), &ja, 1, fs);
121                 // check that emoji characters are found
122                 x = drawCharacter(canvas, 0x1f601, x, y, font, fm, familyName.c_str(), nullptr,0, fs);
123             }
124             y += 24;
125         }
126         return DrawResult::kOk;
127     }
128 };
129 
130 class FontMgrMatchGM : public skiagm::GM {
131     sk_sp<SkFontMgr> fFM;
132 
onOnceBeforeDraw()133     void onOnceBeforeDraw() override {
134         fFM = ToolUtils::TestFontMgr();
135         SkGraphics::SetFontCacheLimit(16 * 1024 * 1024);
136     }
137 
getName() const138     SkString getName() const override { return SkString("fontmgr_match"); }
139 
getISize()140     SkISize getISize() override { return {640, 1024}; }
141 
iterateFamily(SkCanvas * canvas,const SkFont & font,SkFontStyleSet * fset)142     void iterateFamily(SkCanvas* canvas, const SkFont& font, SkFontStyleSet* fset) {
143         SkFont f(font);
144         SkScalar y = 0;
145 
146         for (int j = 0; j < fset->count(); ++j) {
147             SkString sname;
148             SkFontStyle fs;
149             fset->getStyle(j, &fs, &sname);
150 
151             sname.appendf(" [%d %d]", fs.weight(), fs.width());
152 
153             f.setTypeface(sk_sp<SkTypeface>(fset->createTypeface(j)));
154             (void)drawString(canvas, sname, 0, y, f);
155             y += 24;
156         }
157     }
158 
exploreFamily(SkCanvas * canvas,const SkFont & font,SkFontStyleSet * fset)159     void exploreFamily(SkCanvas* canvas, const SkFont& font, SkFontStyleSet* fset) {
160         SkFont f(font);
161         SkScalar y = 0;
162 
163         for (int weight = 100; weight <= 900; weight += 200) {
164             for (int width = 1; width <= 9; width += 2) {
165                 SkFontStyle fs(weight, width, SkFontStyle::kUpright_Slant);
166                 sk_sp<SkTypeface> face(fset->matchStyle(fs));
167                 if (face) {
168                     SkString str;
169                     str.printf("request [%d %d]", fs.weight(), fs.width());
170                     f.setTypeface(std::move(face));
171                     (void)drawString(canvas, str, 0, y, f);
172                     y += 24;
173                 }
174             }
175         }
176     }
177 
onDraw(SkCanvas * canvas,SkString * errorMsg)178     DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
179         SkFont font = ToolUtils::DefaultFont();
180         font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
181         font.setSubpixel(true);
182         font.setSize(17);
183 
184         const char* gNames[] = {
185             "Helvetica Neue", "Arial", "sans", "Roboto"
186         };
187 
188         sk_sp<SkFontStyleSet> fset;
189         for (size_t i = 0; i < std::size(gNames); ++i) {
190             fset = fFM->matchFamily(gNames[i]);
191             if (fset->count() > 0) {
192                 break;
193             }
194         }
195         if (!fset || fset->count() == 0) {
196             *errorMsg = "No SkFontStyleSet";
197             return DrawResult::kSkip;
198         }
199 
200         canvas->translate(20, 40);
201         this->exploreFamily(canvas, font, fset.get());
202         canvas->translate(150, 0);
203         this->iterateFamily(canvas, font, fset.get());
204         return DrawResult::kOk;
205     }
206 };
207 
208 class FontMgrBoundsGM : public skiagm::GM {
209 public:
FontMgrBoundsGM(float scale,float skew)210     FontMgrBoundsGM(float scale, float skew) : fScaleX(scale) , fSkewX(skew) {}
211 
212 private:
getName() const213     SkString getName() const override {
214         if (fScaleX != 1 || fSkewX != 0) {
215             return SkStringPrintf("fontmgr_bounds_%g_%g", fScaleX, fSkewX);
216         }
217         return SkString("fontmgr_bounds");
218     }
219 
onOnceBeforeDraw()220     void onOnceBeforeDraw() override { fFM = ToolUtils::TestFontMgr(); }
221 
onGetControls(SkMetaData * controls)222     bool onGetControls(SkMetaData* controls) override {
223         controls->setBool("Label Bounds", fLabelBounds);
224         return true;
225     }
226 
onSetControls(const SkMetaData & controls)227     void onSetControls(const SkMetaData& controls) override {
228         controls.findBool("Label Bounds", &fLabelBounds);
229     }
230 
show_bounds(SkCanvas * canvas,const SkFont & font,SkScalar x,SkScalar y,SkColor boundsColor,bool labelBounds)231     static SkRect show_bounds(SkCanvas* canvas, const SkFont& font, SkScalar x, SkScalar y,
232                               SkColor boundsColor, bool labelBounds)
233     {
234         SkGlyphID left = 0, right = 0, top = 0, bottom = 0;
235         SkRect min = SkRect::MakeLTRB(SK_ScalarInfinity, SK_ScalarInfinity,
236                                       SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity);
237         {
238             int numGlyphs = font.getTypeface()->countGlyphs();
239             for (int i = 0; i < numGlyphs; ++i) {
240                 SkGlyphID glyphId = i;
241                 SkRect cur;
242                 font.getBounds(&glyphId, 1, &cur, nullptr);
243                 if (cur.fLeft   < min.fLeft  ) { min.fLeft   = cur.fLeft;   left   = i; }
244                 if (cur.fTop    < min.fTop   ) { min.fTop    = cur.fTop ;   top    = i; }
245                 if (min.fRight  < cur.fRight ) { min.fRight  = cur.fRight;  right  = i; }
246                 if (min.fBottom < cur.fBottom) { min.fBottom = cur.fBottom; bottom = i; }
247             }
248         }
249 
250         SkRect fontBounds = SkFontPriv::GetFontBounds(font);
251 
252         SkRect drawBounds = min;
253         drawBounds.join(fontBounds);
254 
255         SkAutoCanvasRestore acr(canvas, true);
256         canvas->translate(x - drawBounds.left(), y);
257 
258         SkPaint boundsPaint;
259         boundsPaint.setAntiAlias(true);
260         boundsPaint.setColor(boundsColor);
261         boundsPaint.setStyle(SkPaint::kStroke_Style);
262         canvas->drawRect(fontBounds, boundsPaint);
263 
264         const SkScalar intervals[] = { 10.f, 10.f };
265         boundsPaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0.f));
266         canvas->drawRect(min, boundsPaint);
267 
268         SkFontMetrics fm;
269         font.getMetrics(&fm);
270         SkPaint metricsPaint(boundsPaint);
271         metricsPaint.setStyle(SkPaint::kFill_Style);
272         metricsPaint.setAlphaf(0.25f);
273         if ((fm.fFlags & SkFontMetrics::kUnderlinePositionIsValid_Flag) &&
274             (fm.fFlags & SkFontMetrics::kUnderlineThicknessIsValid_Flag))
275         {
276             SkRect underline{ min.fLeft,  fm.fUnderlinePosition,
277                               min.fRight, fm.fUnderlinePosition + fm.fUnderlineThickness };
278             canvas->drawRect(underline, metricsPaint);
279         }
280 
281         if ((fm.fFlags & SkFontMetrics::kStrikeoutPositionIsValid_Flag) &&
282             (fm.fFlags & SkFontMetrics::kStrikeoutThicknessIsValid_Flag))
283         {
284             SkRect strikeout{ min.fLeft,  fm.fStrikeoutPosition - fm.fStrikeoutThickness,
285                               min.fRight, fm.fStrikeoutPosition };
286             canvas->drawRect(strikeout, metricsPaint);
287         }
288 
289         struct GlyphToDraw {
290             SkGlyphID id;
291             SkPoint location;
292             SkScalar rotation;
293         } glyphsToDraw [] = {
294             {left,   {min.left(),    min.centerY()}, 270},
295             {right,  {min.right(),   min.centerY()},  90},
296             {top,    {min.centerX(), min.top()    },   0},
297             {bottom, {min.centerX(), min.bottom() }, 180},
298         };
299 
300         SkFont labelFont;
301         labelFont.setEdging(SkFont::Edging::kAntiAlias);
302         labelFont.setTypeface(ToolUtils::DefaultPortableTypeface());
303 
304         if (labelBounds) {
305             SkString name;
306             font.getTypeface()->getFamilyName(&name);
307             canvas->drawString(name, min.fLeft, min.fBottom, labelFont, SkPaint());
308         }
309         for (const GlyphToDraw& glyphToDraw : glyphsToDraw) {
310             SkPath path;
311             font.getPath(glyphToDraw.id, &path);
312             SkPaint::Style style = path.isEmpty() ? SkPaint::kFill_Style : SkPaint::kStroke_Style;
313             SkPaint glyphPaint;
314             glyphPaint.setStyle(style);
315             canvas->drawSimpleText(&glyphToDraw.id, sizeof(glyphToDraw.id),
316                                    SkTextEncoding::kGlyphID, 0, 0, font, glyphPaint);
317 
318             if (labelBounds) {
319                 SkAutoCanvasRestore acr2(canvas, true);
320                 canvas->translate(glyphToDraw.location.fX, glyphToDraw.location.fY);
321                 canvas->rotate(glyphToDraw.rotation);
322                 SkString glyphStr;
323                 glyphStr.appendS32(glyphToDraw.id);
324                 canvas->drawString(glyphStr, 0, 0, labelFont, SkPaint());
325             }
326         }
327 
328         return drawBounds;
329     }
330 
getISize()331     SkISize getISize() override { return {1024, 850}; }
332 
onDraw(SkCanvas * canvas,SkString * errorMsg)333     DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
334         SkFont font = ToolUtils::DefaultFont();
335         font.setEdging(SkFont::Edging::kAntiAlias);
336         font.setSubpixel(true);
337         font.setSize(100);
338         font.setScaleX(fScaleX);
339         font.setSkewX(fSkewX);
340 
341         const SkColor boundsColors[2] = { SK_ColorRED, SK_ColorBLUE };
342 
343         SkFontMgr* fm = fFM.get();
344         int count = std::min(fm->countFamilies(), 32);
345         if (count == 0) {
346             *errorMsg = "No families in SkFontMgr under test.";
347             return DrawResult::kSkip;
348         }
349 
350         int index = 0;
351         SkScalar x = 0, y = 0;
352 
353         canvas->translate(10, 120);
354 
355         for (int i = 0; i < count; ++i) {
356             sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
357             for (int j = 0; j < set->count() && j < 3; ++j) {
358                 font.setTypeface(sk_sp<SkTypeface>(set->createTypeface(j)));
359                 // Fonts with lots of glyphs are interesting, but can take a long time to find
360                 // the glyphs which make up the maximum extent.
361                 SkTypeface* typeface = font.getTypeface();
362                 if (typeface && 0 < typeface->countGlyphs() && typeface->countGlyphs() < 1000) {
363                     SkColor color = boundsColors[index & 1];
364                     SkRect drawBounds = show_bounds(canvas, font, x, y, color, fLabelBounds);
365                     x += drawBounds.width() + 20;
366                     index += 1;
367                     if (x > 900) {
368                         x = 0;
369                         y += 160;
370                     }
371                     if (y >= 700) {
372                         return DrawResult::kOk;
373                     }
374                 }
375             }
376         }
377         return DrawResult::kOk;
378     }
379 
380     sk_sp<SkFontMgr> fFM;
381     const SkScalar fScaleX;
382     const SkScalar fSkewX;
383     bool fLabelBounds = false;
384 };
385 
386 //////////////////////////////////////////////////////////////////////////////
387 
388 DEF_GM(return new FontMgrGM;)
389 DEF_GM(return new FontMgrMatchGM;)
390 DEF_GM(return new FontMgrBoundsGM(1, 0);)
391 DEF_GM(return new FontMgrBoundsGM(0.75f, 0);)
392 DEF_GM(return new FontMgrBoundsGM(1, -0.25f);)
393