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