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