blob: 5b69ecf0ca6d1fedb473fdf4c1d4c8e5c45fc72f [file] [log] [blame]
reed@google.comaf0fa6a2013-03-28 13:39:35 +00001/*
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"
Mike Klein33d20552017-03-22 13:47:51 -04009#include "sk_tool_utils.h"
reed@google.comaf0fa6a2013-03-28 13:39:35 +000010#include "SkCanvas.h"
Ben Wagner17bca9a2018-06-22 15:27:52 -040011#include "SkCommonFlags.h"
reed@google.comaf0fa6a2013-03-28 13:39:35 +000012#include "SkFontMgr.h"
Ben Wagner97182cc2018-02-15 10:20:04 -050013#include "SkPath.h"
reed@google.comaf0fa6a2013-03-28 13:39:35 +000014#include "SkGraphics.h"
15#include "SkTypeface.h"
16
17// limit this just so we don't take too long to draw
18#define MAX_FAMILIES 30
19
20static SkScalar drawString(SkCanvas* canvas, const SkString& text, SkScalar x,
21 SkScalar y, const SkPaint& paint) {
Cary Clark2a475ea2017-04-28 15:35:12 -040022 canvas->drawString(text, x, y, paint);
reed@google.comaf0fa6a2013-03-28 13:39:35 +000023 return x + paint.measureText(text.c_str(), text.size());
24}
25
djsollen0d393a92014-08-27 07:03:13 -070026static SkScalar drawCharacter(SkCanvas* canvas, uint32_t character, SkScalar x,
27 SkScalar y, SkPaint& paint, SkFontMgr* fm,
bungemanc9232dc2014-11-10 13:29:33 -080028 const char* fontName, const char* bcp47[], int bcp47Count,
djsollen0d393a92014-08-27 07:03:13 -070029 const SkFontStyle& fontStyle) {
30 // find typeface containing the requested character and draw it
31 SkString ch;
32 ch.appendUnichar(character);
bungeman13b9c952016-05-12 10:09:30 -070033 sk_sp<SkTypeface> typeface(fm->matchFamilyStyleCharacter(fontName, fontStyle,
34 bcp47, bcp47Count, character));
35 paint.setTypeface(typeface);
djsollen0d393a92014-08-27 07:03:13 -070036 x = drawString(canvas, ch, x, y, paint) + 20;
37
halcanary96fcdcc2015-08-27 07:41:13 -070038 if (nullptr == typeface) {
djsollen0d393a92014-08-27 07:03:13 -070039 return x;
40 }
41
42 // repeat the process, but this time use the family name of the typeface
43 // from the first pass. This emulates the behavior in Blink where it
44 // it expects to get the same glyph when following this pattern.
45 SkString familyName;
46 typeface->getFamilyName(&familyName);
Mike Reed59227392017-09-26 09:46:08 -040047 paint.setTypeface(fm->legacyMakeTypeface(familyName.c_str(), typeface->fontStyle()));
djsollen0d393a92014-08-27 07:03:13 -070048 return drawString(canvas, ch, x, y, paint) + 20;
49}
50
bungemanc9232dc2014-11-10 13:29:33 -080051static const char* zh = "zh";
52static const char* ja = "ja";
53
reed@google.comaf0fa6a2013-03-28 13:39:35 +000054class FontMgrGM : public skiagm::GM {
55public:
Mike Klein8073e792017-11-13 18:10:54 -050056 FontMgrGM() {
Ben Wagner3546ff12017-01-03 13:32:36 -050057 SkGraphics::SetFontCacheLimit(16 * 1024 * 1024);
58
59 fName.set("fontmgr_iter");
Mike Klein8073e792017-11-13 18:10:54 -050060 fFM = SkFontMgr::RefDefault();
Mike Kleinb251b722017-11-13 11:03:16 -050061 fName.append(sk_tool_utils::platform_font_manager());
Ben Wagner3546ff12017-01-03 13:32:36 -050062 }
reed@google.comaf0fa6a2013-03-28 13:39:35 +000063
64protected:
mtklein36352bf2015-03-25 18:17:31 -070065 SkString onShortName() override {
bungeman@google.combfc6cc42013-08-21 15:20:43 +000066 return fName;
reed@google.comaf0fa6a2013-03-28 13:39:35 +000067 }
68
mtklein36352bf2015-03-25 18:17:31 -070069 SkISize onISize() override {
djsollen0d393a92014-08-27 07:03:13 -070070 return SkISize::Make(1536, 768);
reed@google.comaf0fa6a2013-03-28 13:39:35 +000071 }
72
mtklein36352bf2015-03-25 18:17:31 -070073 void onDraw(SkCanvas* canvas) override {
reed@google.comaf0fa6a2013-03-28 13:39:35 +000074 SkScalar y = 20;
75 SkPaint paint;
76 paint.setAntiAlias(true);
77 paint.setLCDRenderText(true);
78 paint.setSubpixelText(true);
79 paint.setTextSize(17);
skia.committer@gmail.com6acd09e2013-03-29 07:01:22 +000080
Hal Canarycefc4312016-11-04 16:26:16 -040081 SkFontMgr* fm = fFM.get();
reed@google.comaf0fa6a2013-03-28 13:39:35 +000082 int count = SkMin32(fm->countFamilies(), MAX_FAMILIES);
83
84 for (int i = 0; i < count; ++i) {
bungemanc64239a2015-04-29 08:15:31 -070085 SkString familyName;
86 fm->getFamilyName(i, &familyName);
halcanary96fcdcc2015-08-27 07:41:13 -070087 paint.setTypeface(nullptr);
bungemanc64239a2015-04-29 08:15:31 -070088 (void)drawString(canvas, familyName, 20, y, paint);
skia.committer@gmail.com6acd09e2013-03-29 07:01:22 +000089
reed@google.comaf0fa6a2013-03-28 13:39:35 +000090 SkScalar x = 220;
reed@google.com964988f2013-03-29 14:57:22 +000091
Hal Canarycefc4312016-11-04 16:26:16 -040092 sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
reed@google.comaf0fa6a2013-03-28 13:39:35 +000093 for (int j = 0; j < set->count(); ++j) {
94 SkString sname;
95 SkFontStyle fs;
96 set->getStyle(j, &fs, &sname);
bungemanb4bb7d82016-04-27 10:21:04 -070097 sname.appendf(" [%d %d %d]", fs.weight(), fs.width(), fs.slant());
skia.committer@gmail.com6acd09e2013-03-29 07:01:22 +000098
bungeman13b9c952016-05-12 10:09:30 -070099 paint.setTypeface(sk_sp<SkTypeface>(set->createTypeface(j)));
reed@google.comaf0fa6a2013-03-28 13:39:35 +0000100 x = drawString(canvas, sname, x, y, paint) + 20;
djsollen0d393a92014-08-27 07:03:13 -0700101
102 // check to see that we get different glyphs in japanese and chinese
bungemanc64239a2015-04-29 08:15:31 -0700103 x = drawCharacter(canvas, 0x5203, x, y, paint, fm, familyName.c_str(), &zh, 1, fs);
104 x = drawCharacter(canvas, 0x5203, x, y, paint, fm, familyName.c_str(), &ja, 1, fs);
djsollen0d393a92014-08-27 07:03:13 -0700105 // check that emoji characters are found
halcanary96fcdcc2015-08-27 07:41:13 -0700106 x = drawCharacter(canvas, 0x1f601, x, y, paint, fm, familyName.c_str(), nullptr,0, fs);
skia.committer@gmail.com6acd09e2013-03-29 07:01:22 +0000107 }
reed@google.comaf0fa6a2013-03-28 13:39:35 +0000108 y += 24;
109 }
110 }
111
reed@google.comaf0fa6a2013-03-28 13:39:35 +0000112private:
Hal Canarycefc4312016-11-04 16:26:16 -0400113 sk_sp<SkFontMgr> fFM;
bungeman@google.combfc6cc42013-08-21 15:20:43 +0000114 SkString fName;
reed@google.comaf0fa6a2013-03-28 13:39:35 +0000115 typedef GM INHERITED;
116};
117
reed@google.com964988f2013-03-29 14:57:22 +0000118class FontMgrMatchGM : public skiagm::GM {
Hal Canarycefc4312016-11-04 16:26:16 -0400119 sk_sp<SkFontMgr> fFM;
reed@google.com964988f2013-03-29 14:57:22 +0000120
121public:
122 FontMgrMatchGM() : fFM(SkFontMgr::RefDefault()) {
123 SkGraphics::SetFontCacheLimit(16 * 1024 * 1024);
124 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +0000125
reed@google.com964988f2013-03-29 14:57:22 +0000126protected:
mtklein36352bf2015-03-25 18:17:31 -0700127 SkString onShortName() override {
caryclark6531c362015-07-20 13:38:56 -0700128 SkString name("fontmgr_match");
Mike Kleinb251b722017-11-13 11:03:16 -0500129 name.append(sk_tool_utils::platform_font_manager());
caryclark6531c362015-07-20 13:38:56 -0700130 return name;
reed@google.com964988f2013-03-29 14:57:22 +0000131 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +0000132
mtklein36352bf2015-03-25 18:17:31 -0700133 SkISize onISize() override {
reed@google.com964988f2013-03-29 14:57:22 +0000134 return SkISize::Make(640, 1024);
135 }
136
137 void iterateFamily(SkCanvas* canvas, const SkPaint& paint,
138 SkFontStyleSet* fset) {
139 SkPaint p(paint);
140 SkScalar y = 0;
141
142 for (int j = 0; j < fset->count(); ++j) {
143 SkString sname;
144 SkFontStyle fs;
145 fset->getStyle(j, &fs, &sname);
146
147 sname.appendf(" [%d %d]", fs.weight(), fs.width());
148
bungeman13b9c952016-05-12 10:09:30 -0700149 p.setTypeface(sk_sp<SkTypeface>(fset->createTypeface(j)));
reed@google.com964988f2013-03-29 14:57:22 +0000150 (void)drawString(canvas, sname, 0, y, p);
151 y += 24;
152 }
153 }
154
155 void exploreFamily(SkCanvas* canvas, const SkPaint& paint,
156 SkFontStyleSet* fset) {
157 SkPaint p(paint);
158 SkScalar y = 0;
159
160 for (int weight = 100; weight <= 900; weight += 200) {
161 for (int width = 1; width <= 9; width += 2) {
162 SkFontStyle fs(weight, width, SkFontStyle::kUpright_Slant);
bungeman13b9c952016-05-12 10:09:30 -0700163 sk_sp<SkTypeface> face(fset->matchStyle(fs));
reed@google.com964988f2013-03-29 14:57:22 +0000164 if (face) {
165 SkString str;
166 str.printf("request [%d %d]", fs.weight(), fs.width());
bungeman13b9c952016-05-12 10:09:30 -0700167 p.setTypeface(std::move(face));
reed@google.com964988f2013-03-29 14:57:22 +0000168 (void)drawString(canvas, str, 0, y, p);
169 y += 24;
170 }
171 }
172 }
173 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +0000174
mtklein36352bf2015-03-25 18:17:31 -0700175 void onDraw(SkCanvas* canvas) override {
reed@google.com964988f2013-03-29 14:57:22 +0000176 SkPaint paint;
177 paint.setAntiAlias(true);
178 paint.setLCDRenderText(true);
179 paint.setSubpixelText(true);
180 paint.setTextSize(17);
181
mtkleindbfd7ab2016-09-01 11:24:54 -0700182 const char* gNames[] = {
Ben Wagner83c6b962018-07-10 19:40:15 -0400183 "Helvetica Neue", "Arial", "sans"
reed@google.com964988f2013-03-29 14:57:22 +0000184 };
185
Hal Canarycefc4312016-11-04 16:26:16 -0400186 sk_sp<SkFontStyleSet> fset;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +0000187 for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) {
bungeman@google.com9fc5c682013-11-12 15:25:29 +0000188 fset.reset(fFM->matchFamily(gNames[i]));
189 if (fset->count() > 0) {
reed@google.com964988f2013-03-29 14:57:22 +0000190 break;
191 }
192 }
halcanary96fcdcc2015-08-27 07:41:13 -0700193 if (nullptr == fset.get()) {
reed@google.com964988f2013-03-29 14:57:22 +0000194 return;
195 }
reed@google.com964988f2013-03-29 14:57:22 +0000196
197 canvas->translate(20, 40);
Hal Canarycefc4312016-11-04 16:26:16 -0400198 this->exploreFamily(canvas, paint, fset.get());
reed@google.com964988f2013-03-29 14:57:22 +0000199 canvas->translate(150, 0);
Hal Canarycefc4312016-11-04 16:26:16 -0400200 this->iterateFamily(canvas, paint, fset.get());
reed@google.com964988f2013-03-29 14:57:22 +0000201 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +0000202
reed@google.com964988f2013-03-29 14:57:22 +0000203private:
204 typedef GM INHERITED;
205};
206
reeda0c814c2014-10-22 13:20:58 -0700207class FontMgrBoundsGM : public skiagm::GM {
208public:
reed8893e5f2014-12-15 13:27:26 -0800209 FontMgrBoundsGM(double scale, double skew)
210 : fScaleX(SkDoubleToScalar(scale))
211 , fSkewX(SkDoubleToScalar(skew))
212 {
reeda0c814c2014-10-22 13:20:58 -0700213 fName.set("fontmgr_bounds");
reed8893e5f2014-12-15 13:27:26 -0800214 if (scale != 1 || skew != 0) {
215 fName.appendf("_%g_%g", scale, skew);
216 }
Mike Kleinb251b722017-11-13 11:03:16 -0500217 fName.append(sk_tool_utils::platform_font_manager());
Ben Wagner3546ff12017-01-03 13:32:36 -0500218 fFM = SkFontMgr::RefDefault();
reeda0c814c2014-10-22 13:20:58 -0700219 }
220
221 static void show_bounds(SkCanvas* canvas, const SkPaint& paint, SkScalar x, SkScalar y,
Ben Wagner219f3622017-07-17 15:32:25 -0400222 SkColor boundsColor)
223 {
224 SkPaint glyphPaint(paint);
225 SkRect fontBounds = glyphPaint.getFontBounds();
226 fontBounds.offset(x, y);
227 SkPaint boundsPaint(glyphPaint);
228 boundsPaint.setColor(boundsColor);
Ben Wagner97182cc2018-02-15 10:20:04 -0500229 boundsPaint.setStyle(SkPaint::kStroke_Style);
Ben Wagner219f3622017-07-17 15:32:25 -0400230 canvas->drawRect(fontBounds, boundsPaint);
reeda0c814c2014-10-22 13:20:58 -0700231
Mike Reedcb6f53e2018-11-06 12:44:54 -0500232 SkFontMetrics fm;
Ben Wagner219f3622017-07-17 15:32:25 -0400233 glyphPaint.getFontMetrics(&fm);
234 SkPaint metricsPaint(boundsPaint);
235 metricsPaint.setStyle(SkPaint::kFill_Style);
236 metricsPaint.setAlpha(0x40);
237 if ((fm.fFlags & SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag) &&
238 (fm.fFlags & SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag))
239 {
240 SkRect underline{ fontBounds.fLeft, fm.fUnderlinePosition+y,
241 fontBounds.fRight, fm.fUnderlinePosition+y + fm.fUnderlineThickness };
242 canvas->drawRect(underline, metricsPaint);
reeda0c814c2014-10-22 13:20:58 -0700243 }
tfarinaaa458fb2015-01-05 17:18:51 -0800244
Ben Wagner219f3622017-07-17 15:32:25 -0400245 if ((fm.fFlags & SkPaint::FontMetrics::kStrikeoutPositionIsValid_Flag) &&
246 (fm.fFlags & SkPaint::FontMetrics::kStrikeoutPositionIsValid_Flag))
247 {
248 SkRect strikeout{ fontBounds.fLeft, fm.fStrikeoutPosition+y - fm.fStrikeoutThickness,
249 fontBounds.fRight, fm.fStrikeoutPosition+y };
250 canvas->drawRect(strikeout, metricsPaint);
251 }
252
253 SkGlyphID left = 0, right = 0, top = 0, bottom = 0;
254 {
255 int numGlyphs = glyphPaint.getTypeface()->countGlyphs();
256 SkRect min = {0, 0, 0, 0};
257 glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
258 for (int i = 0; i < numGlyphs; ++i) {
259 SkGlyphID glyphId = i;
260 SkRect cur;
261 glyphPaint.measureText(&glyphId, sizeof(glyphId), &cur);
262 if (cur.fLeft < min.fLeft ) { min.fLeft = cur.fLeft; left = i; }
263 if (cur.fTop < min.fTop ) { min.fTop = cur.fTop ; top = i; }
264 if (min.fRight < cur.fRight ) { min.fRight = cur.fRight; right = i; }
265 if (min.fBottom < cur.fBottom) { min.fBottom = cur.fBottom; bottom = i; }
266 }
267 }
268 SkGlyphID str[] = { left, right, top, bottom };
Ben Wagner17bca9a2018-06-22 15:27:52 -0400269 SkPoint location[] = {
270 {fontBounds.left(), fontBounds.centerY()},
271 {fontBounds.right(), fontBounds.centerY()},
272 {fontBounds.centerX(), fontBounds.top()},
273 {fontBounds.centerX(), fontBounds.bottom()}
274 };
275
276 SkPaint labelPaint;
277 labelPaint.setAntiAlias(true);
278 sk_tool_utils::set_portable_typeface(&labelPaint);
279 if (FLAGS_veryVerbose) {
280 SkString name;
281 paint.getTypeface()->getFamilyName(&name);
282 canvas->drawText(name.c_str(), name.size(),
283 fontBounds.fLeft, fontBounds.fBottom, labelPaint);
284 }
Ben Wagner219f3622017-07-17 15:32:25 -0400285 for (size_t i = 0; i < SK_ARRAY_COUNT(str); ++i) {
Ben Wagner97182cc2018-02-15 10:20:04 -0500286 SkPath path;
287 glyphPaint.getTextPath(&str[i], sizeof(str[0]), x, y, &path);
288 SkPaint::Style style = path.isEmpty() ? SkPaint::kFill_Style : SkPaint::kStroke_Style;
289 glyphPaint.setStyle(style);
Ben Wagner219f3622017-07-17 15:32:25 -0400290 canvas->drawText(&str[i], sizeof(str[0]), x, y, glyphPaint);
Ben Wagner17bca9a2018-06-22 15:27:52 -0400291
292 if (FLAGS_veryVerbose) {
293 SkString glyphStr;
294 glyphStr.appendS32(str[i]);
295 canvas->drawText(glyphStr.c_str(), glyphStr.size(),
296 location[i].fX, location[i].fY, labelPaint);
297 }
298
Ben Wagner219f3622017-07-17 15:32:25 -0400299 }
Ben Wagner17bca9a2018-06-22 15:27:52 -0400300
reeda0c814c2014-10-22 13:20:58 -0700301 }
302
303protected:
mtklein36352bf2015-03-25 18:17:31 -0700304 SkString onShortName() override {
reeda0c814c2014-10-22 13:20:58 -0700305 return fName;
306 }
tfarinaaa458fb2015-01-05 17:18:51 -0800307
mtklein36352bf2015-03-25 18:17:31 -0700308 SkISize onISize() override {
reeda0c814c2014-10-22 13:20:58 -0700309 return SkISize::Make(1024, 850);
310 }
tfarinaaa458fb2015-01-05 17:18:51 -0800311
mtklein36352bf2015-03-25 18:17:31 -0700312 void onDraw(SkCanvas* canvas) override {
reeda0c814c2014-10-22 13:20:58 -0700313 SkPaint paint;
314 paint.setAntiAlias(true);
315 paint.setSubpixelText(true);
316 paint.setTextSize(100);
reed8893e5f2014-12-15 13:27:26 -0800317 paint.setTextScaleX(fScaleX);
318 paint.setTextSkewX(fSkewX);
reeda0c814c2014-10-22 13:20:58 -0700319
320 const SkColor boundsColors[2] = { SK_ColorRED, SK_ColorBLUE };
halcanary9d524f22016-03-29 09:03:52 -0700321
Hal Canarycefc4312016-11-04 16:26:16 -0400322 SkFontMgr* fm = fFM.get();
reeda0c814c2014-10-22 13:20:58 -0700323 int count = SkMin32(fm->countFamilies(), 32);
324
325 int index = 0;
326 SkScalar x = 0, y = 0;
327
Ben Wagner219f3622017-07-17 15:32:25 -0400328 canvas->translate(10, 120);
reeda0c814c2014-10-22 13:20:58 -0700329
330 for (int i = 0; i < count; ++i) {
Hal Canarycefc4312016-11-04 16:26:16 -0400331 sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
Ben Wagner219f3622017-07-17 15:32:25 -0400332 for (int j = 0; j < set->count() && j < 3; ++j) {
bungeman13b9c952016-05-12 10:09:30 -0700333 paint.setTypeface(sk_sp<SkTypeface>(set->createTypeface(j)));
Ben Wagner2112df02017-07-24 11:04:21 -0400334 // Fonts with lots of glyphs are interesting, but can take a long time to find
335 // the glyphs which make up the maximum extent.
336 if (paint.getTypeface() && paint.getTypeface()->countGlyphs() < 1000) {
Ben Wagner219f3622017-07-17 15:32:25 -0400337 SkRect fontBounds = paint.getFontBounds();
338 x -= fontBounds.fLeft;
reeda0c814c2014-10-22 13:20:58 -0700339 show_bounds(canvas, paint, x, y, boundsColors[index & 1]);
Ben Wagner219f3622017-07-17 15:32:25 -0400340 x += fontBounds.fRight + 20;
reeda0c814c2014-10-22 13:20:58 -0700341 index += 1;
Ben Wagner219f3622017-07-17 15:32:25 -0400342 if (x > 900) {
reeda0c814c2014-10-22 13:20:58 -0700343 x = 0;
344 y += 160;
345 }
Ben Wagner219f3622017-07-17 15:32:25 -0400346 if (y >= 700) {
reeda0c814c2014-10-22 13:20:58 -0700347 return;
348 }
349 }
350 }
351 }
352 }
mtklein1c402922015-01-23 11:07:07 -0800353
reeda0c814c2014-10-22 13:20:58 -0700354private:
Hal Canarycefc4312016-11-04 16:26:16 -0400355 sk_sp<SkFontMgr> fFM;
reeda0c814c2014-10-22 13:20:58 -0700356 SkString fName;
reed8893e5f2014-12-15 13:27:26 -0800357 SkScalar fScaleX, fSkewX;
reeda0c814c2014-10-22 13:20:58 -0700358 typedef GM INHERITED;
359};
360
reed@google.comaf0fa6a2013-03-28 13:39:35 +0000361//////////////////////////////////////////////////////////////////////////////
362
halcanary385fe4d2015-08-26 13:07:48 -0700363DEF_GM(return new FontMgrGM;)
364DEF_GM(return new FontMgrMatchGM;)
365DEF_GM(return new FontMgrBoundsGM(1.0, 0);)
366DEF_GM(return new FontMgrBoundsGM(0.75, 0);)
367DEF_GM(return new FontMgrBoundsGM(1.0, -0.25);)