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