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