Flutter Engine
The Flutter Engine
typeface.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2012 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"
11#include "include/core/SkFont.h"
19#include "include/core/SkSize.h"
25#include "src/core/SkFontPriv.h"
26#include "tools/Resources.h"
28
29#include <string.h>
30#include <utility>
31
32using namespace skia_private;
33
34static void getGlyphPositions(const SkFont& font, const uint16_t glyphs[],
35 int count, SkScalar x, SkScalar y, SkPoint pos[]) {
37 SkScalar* widths = widthStorage.get();
38 font.getWidths(glyphs, count, widths);
39
40 for (int i = 0; i < count; ++i) {
41 pos[i].set(x, y);
42 x += widths[i];
43 }
44}
45
46static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count,
47 const SkFont& font) {
48 SkScalar scale = font.getSize() / font.getTypeface()->getUnitsPerEm();
49
50 SkScalar globalAdj = 0;
51 for (int i = 0; i < count - 1; ++i) {
52 globalAdj += adjustments[i] * scale;
53 pos[i + 1].fX += globalAdj;
54 }
55}
56
57static void drawKernText(SkCanvas* canvas, const void* text, size_t len,
58 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
59 SkTypeface* face = font.getTypeface();
60 if (!face) {
62 return;
63 }
64
65 AutoSTMalloc<128, uint16_t> glyphStorage(len);
66 uint16_t* glyphs = glyphStorage.get();
67 int glyphCount = font.textToGlyphs(text, len, SkTextEncoding::kUTF8, glyphs, len);
68 if (glyphCount < 1) {
69 return;
70 }
71
72 AutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1);
73 int32_t* adjustments = adjustmentStorage.get();
74 if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) {
76 return;
77 }
78
79
81 auto rec = builder.allocRunPos(font, glyphCount);
82 memcpy(rec.glyphs, glyphs, glyphCount * sizeof(SkGlyphID));
83 getGlyphPositions(font, glyphs, glyphCount, x, y, rec.points());
84 applyKerning(rec.points(), adjustments, glyphCount, font);
85
86 canvas->drawTextBlob(builder.make(), 0, 0, paint);
87}
88
89static constexpr SkFontStyle gStyles[] = {
94};
95
97
98// TODO(bungeman) delete this GM, as it is no longer useful.
101 bool fApplyKerning;
102
103public:
105
106protected:
107 void onOnceBeforeDraw() override {
108 for (int i = 0; i < gStylesCount; i++) {
109 fFaces[i] = ToolUtils::CreateTestTypeface(nullptr, gStyles[i]);
110 }
111 }
112
113 SkString getName() const override {
114 SkString name("typefacestyles");
115 if (fApplyKerning) {
116 name.append("_kerning");
117 }
118 return name;
119 }
120
121 SkISize getISize() override { return SkISize::Make(640, 480); }
122
123 void onDraw(SkCanvas* canvas) override {
124 // Need to use a font to get dy below.
126 font.setSize(30);
127
128 const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons";
129 const size_t textLen = strlen(text);
130
132 SkScalar dy = font.getMetrics(nullptr);
133 SkASSERT(dy > 0);
134 SkScalar y = dy;
135
136 if (fApplyKerning) {
137 font.setSubpixel(true);
138 } else {
139 font.setLinearMetrics(true);
140 }
141
143 for (int i = 0; i < gStylesCount; i++) {
144 SkASSERT(fFaces[i]);
145 font.setTypeface(fFaces[i]);
146 canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint);
147 if (fApplyKerning) {
148 drawKernText(canvas, text, textLen, x + 240, y, font, paint);
149 }
150 y += dy;
151 }
152 }
153
154private:
155 using INHERITED = skiagm::GM;
156};
157
158DEF_GM( return new TypefaceStylesGM(false); )
159DEF_GM( return new TypefaceStylesGM(true); )
160
161////////////////////////////////////////////////////////////////////////////////
162
164 struct AliasType {
165 SkFont::Edging edging;
166 bool inLayer;
167 } constexpr aliasTypes[] {
168#ifndef SK_BUILD_FOR_IOS
169 // This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering.
170 // The crash looks like
171 // libTrueTypeScaler.dylib`<redacted> + 80
172 // stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...)
173 // -> 0x330b19d0 <+80>: strd r2, r3, [r5, #36]
174 // 0x330b19d4 <+84>: movs r3, #0x0
175 // 0x330b19d6 <+86>: add r2, sp, #0x28
176 // 0x330b19d8 <+88>: ldr r0, [r4, #0x4]
177 // Disable testing embedded bitmaps on iOS for now.
178 // See https://bug.skia.org/5530 .
179 { SkFont::Edging::kAlias , false },
180#endif
181 { SkFont::Edging::kAntiAlias , false },
185 };
186
187 // The hintgasp.ttf is designed for the following sizes to be different.
188 // GASP_DOGRAY 0x0002 0<=ppem<=10
189 // GASP_SYMMETRIC_SMOOTHING 0x0008 0<=ppem<=10
190 // GASP_GRIDFIT 0x0001 11<=ppem<=12
191 // GASP_SYMMETRIC_GRIDFIT 0x0004 11<=ppem<=12
192 // GASP_DOGRAY|GASP_GRIDFIT 0x0003 13<=ppem<=14
193 // GASP_SYMMETRIC_SMOOTHING|GASP_SYMMETRIC_GRIDFIT 0x000C 13<=ppem<=14
194 // (neither) 0x0000 15<=ppem
195 // Odd sizes have embedded bitmaps.
196 constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 };
197
198 constexpr SkFontHinting hintingTypes[] = {
203 };
204
205 struct SubpixelType {
206 bool requested;
208 } constexpr subpixelTypes[] = {
209 { false, { 0.00, 0.00 } },
210 { true , { 0.00, 0.00 } },
211 { true , { 0.25, 0.00 } },
212 { true , { 0.25, 0.25 } },
213 };
214
215 constexpr bool rotateABitTypes[] = { false, true };
216
217 SkScalar y = 0; // The baseline of the previous output
218 {
220
221 SkFont font(face);
222 font.setEmbeddedBitmaps(true);
223
224 SkScalar x = 0;
225 SkScalar xMax = x;
226 SkScalar xBase = 0;
227 for (const SubpixelType subpixel : subpixelTypes) {
228 y = 0;
229 font.setSubpixel(subpixel.requested);
230
231 for (const AliasType& alias : aliasTypes) {
232 font.setEdging(alias.edging);
233 SkAutoCanvasRestore acr1(canvas, false);
234 if (alias.inLayer) {
235 canvas->saveLayer(nullptr, &paint);
236 }
237
238 for (const SkScalar& textSize : textSizes) {
239 x = xBase + 5;
240 font.setSize(textSize);
241
242 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr));
243 y += dy;
244 for (const SkFontHinting& hinting : hintingTypes) {
245 font.setHinting(hinting);
246
247 for (const bool& rotateABit : rotateABitTypes) {
248 SkAutoCanvasRestore acr2(canvas, true);
249 if (rotateABit) {
250 canvas->rotate(2, x + subpixel.offset.x(),
251 y + subpixel.offset.y());
252 }
253 canvas->drawSimpleText(&glyph, sizeof(glyph), SkTextEncoding::kGlyphID,
254 x + subpixel.offset.x(),
255 y + subpixel.offset.y(), font, paint);
256
257 SkScalar dx = SkScalarCeilToScalar(font.measureText(
258 &glyph, sizeof(glyph), SkTextEncoding::kGlyphID)) + 5;
259 x += dx;
260 xMax = std::max(x, xMax);
261 }
262 }
263 }
264 y += 10;
265 }
266 xBase = xMax;
267 }
268 }
269
270 constexpr struct StyleTests {
271 SkPaint::Style style;
273 } styleTypes[] = {
274 { SkPaint::kFill_Style, 0.0f},
275 { SkPaint::kStroke_Style, 0.0f},
276 { SkPaint::kStroke_Style, 0.5f},
278 };
279
280 constexpr bool fakeBoldTypes[] = { false, true };
281
282 {
284
285 SkFont font(face, 16);
286
287 SkScalar x = 0;
288 for (const bool& fakeBold : fakeBoldTypes) {
289 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr));
290 y += dy;
291 x = 5;
292
293 font.setEmbolden(fakeBold);
294 for (const AliasType& alias : aliasTypes) {
295 font.setEdging(alias.edging);
296 SkAutoCanvasRestore acr(canvas, false);
297 if (alias.inLayer) {
298 canvas->saveLayer(nullptr, &paint);
299 }
300 for (const StyleTests& style : styleTypes) {
301 paint.setStyle(style.style);
302 paint.setStrokeWidth(style.strokeWidth);
303 canvas->drawSimpleText(&glyph, sizeof(glyph), SkTextEncoding::kGlyphID,
304 x, y, font, paint);
305
306 SkScalar dx = SkScalarCeilToScalar(font.measureText(
307 &glyph, sizeof(glyph), SkTextEncoding::kGlyphID)) + 5;
308 x += dx;
309 }
310 }
311 y += 10;
312 }
313 }
314
315 constexpr struct MaskTests {
316 SkBlurStyle style;
317 SkScalar sigma;
318 } maskTypes[] = {
323
328
333 };
334
335 {
337
338 SkFont font(face, 16);
339
340 SkScalar x = 0;
341 {
342 for (const AliasType& alias : aliasTypes) {
343 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr));
344 y += dy;
345 x = 5;
346
347 font.setEdging(alias.edging);
348 SkAutoCanvasRestore acr(canvas, false);
349 if (alias.inLayer) {
350 canvas->saveLayer(nullptr, &paint);
351 }
352 for (const MaskTests& mask : maskTypes) {
353 paint.setMaskFilter(SkMaskFilter::MakeBlur(mask.style, mask.sigma));
354 canvas->drawSimpleText(&glyph, sizeof(glyph), SkTextEncoding::kGlyphID,
355 x, y, font, paint);
356
357 SkScalar dx = SkScalarCeilToScalar(font.measureText(
358 &glyph, sizeof(glyph), SkTextEncoding::kGlyphID)) + 5;
359 x += dx;
360 }
361 paint.setMaskFilter(nullptr);
362 }
363 y += 10;
364 }
365 }
366}
367
368DEF_SIMPLE_GM_CAN_FAIL(typefacerendering, canvas, errMsg, 640, 840) {
369 sk_sp<SkTypeface> face = ToolUtils::CreateTypefaceFromResource("fonts/hintgasp.ttf");
370 if (!face) {
372 }
373 draw_typeface_rendering_gm(canvas, face, face->unicharToGlyph('A'));
374
375 // Should draw nothing and not do anything undefined.
376 draw_typeface_rendering_gm(canvas, face, 0xFFFF);
378}
379
380// Type1 fonts don't currently work in Skia on Windows.
381#ifndef SK_BUILD_FOR_WIN
382
383DEF_SIMPLE_GM_CAN_FAIL(typefacerendering_pfa, canvas, errMsg, 640, 840) {
384 sk_sp<SkTypeface> face = ToolUtils::CreateTypefaceFromResource("fonts/Roboto2-Regular.pfa");
385 if (!face) {
387 }
388 draw_typeface_rendering_gm(canvas, face, face->unicharToGlyph('O'));
390}
391
392DEF_SIMPLE_GM_CAN_FAIL(typefacerendering_pfb, canvas, errMsg, 640, 840) {
393 sk_sp<SkTypeface> face = ToolUtils::CreateTypefaceFromResource("fonts/Roboto2-Regular.pfb");
394 if (!face) {
396 }
397 draw_typeface_rendering_gm(canvas, face, face->unicharToGlyph('O'));
399}
400
401#endif
402
403////////////////////////////////////////////////////////////////////////////////
404
405// Exercise different paint styles and embolden, and compare with strokeandfill patheffect
406DEF_SIMPLE_GM(typeface_styling, canvas, 710, 360) {
407 sk_sp<SkTypeface> face = ToolUtils::CreateTypefaceFromResource("fonts/Roboto-Regular.ttf");
408 if (!face) {
410 }
411 SkFont font(face, 100);
413
414 uint16_t glyphs[1] = { font.unicharToGlyph('A') };
415 SkPoint pos[1] = { {0, 0} };
416
417 auto draw = [&](SkPaint::Style style, float width) {
418 // Draws 3 rows:
419 // 1. normal
420 // 2. emboldened
421 // 3. normal(white) on top of emboldened (to show the delta)
422
424 paint.setStyle(style);
425 paint.setStrokeWidth(width);
426
427 font.setEmbolden(true);
428 canvas->drawGlyphs(1, glyphs, pos, {20, 120*2}, font, paint);
429 canvas->drawGlyphs(1, glyphs, pos, {20, 120*3}, font, paint);
430
431 font.setEmbolden(false);
432 canvas->drawGlyphs(1, glyphs, pos, {20, 120*1}, font, paint);
433 paint.setColor(SK_ColorYELLOW);
434 canvas->drawGlyphs(1, glyphs, pos, {20, 120*3}, font, paint);
435 };
436
437 const struct {
438 SkPaint::Style style;
439 float width;
440 } recs[] = {
446 };
447
448 canvas->translate(0, -20);
449 for (auto r : recs) {
450 draw(r.style, r.width);
451 canvas->translate(100, 0);
452 }
453}
static const int strokeWidth
Definition: BlurTest.cpp:60
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
int count
Definition: FontMgrTest.cpp:50
SkPoint pos
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBlurStyle
Definition: SkBlurTypes.h:11
@ kOuter_SkBlurStyle
nothing inside, fuzzy outside
Definition: SkBlurTypes.h:14
@ kSolid_SkBlurStyle
solid inside, fuzzy outside
Definition: SkBlurTypes.h:13
@ kInner_SkBlurStyle
fuzzy inside, nothing outside
Definition: SkBlurTypes.h:15
@ kNormal_SkBlurStyle
fuzzy inside and outside
Definition: SkBlurTypes.h:12
constexpr SkColor SK_ColorYELLOW
Definition: SkColor.h:139
SkFontHinting
Definition: SkFontTypes.h:18
@ kNormal
glyph outlines modified to improve constrast
@ kNone
glyph outlines unchanged
@ kSlight
minimal modification to improve constrast
@ kFull
modifies glyph outlines for maximum constrast
@ kUTF8
uses bytes to represent UTF-8 or ASCII
@ kGlyphID
uses two byte words to represent glyph indices
#define SkIntToScalar(x)
Definition: SkScalar.h:57
#define SkScalarCeilToScalar(x)
Definition: SkScalar.h:31
uint16_t SkGlyphID
Definition: SkTypes.h:179
const SkScalar widths[]
Definition: StrokerTest.cpp:39
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition: aaclip.cpp:27
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void drawSimpleText(const void *text, size_t byteLength, SkTextEncoding encoding, SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition: SkCanvas.cpp:2413
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[], const uint32_t clusters[], int textByteCount, const char utf8text[], SkPoint origin, const SkFont &font, const SkPaint &paint)
void rotate(SkScalar degrees)
Definition: SkCanvas.cpp:1300
void drawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y, const SkPaint &paint)
Definition: SkCanvas.cpp:2484
static constexpr SkFontStyle Italic()
Definition: SkFontStyle.h:72
static constexpr SkFontStyle BoldItalic()
Definition: SkFontStyle.h:75
static constexpr SkFontStyle Bold()
Definition: SkFontStyle.h:69
static constexpr SkFontStyle Normal()
Definition: SkFontStyle.h:66
Definition: SkFont.h:35
Edging
Definition: SkFont.h:39
@ kAntiAlias
may have transparent pixels on glyph edges
@ kAlias
no transparent pixels on glyph edges
@ kSubpixelAntiAlias
glyph positioned in pixel using transparency
static sk_sp< SkMaskFilter > MakeBlur(SkBlurStyle style, SkScalar sigma, bool respectCTM=true)
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
@ kFill_Style
set to fill geometry
Definition: SkPaint.h:193
@ kStrokeAndFill_Style
sets to stroke and fill geometry
Definition: SkPaint.h:195
bool getKerningPairAdjustments(const SkGlyphID glyphs[], int count, int32_t adjustments[]) const
Definition: SkTypeface.cpp:441
SkGlyphID unicharToGlyph(SkUnichar unichar) const
Definition: SkTypeface.cpp:363
void onOnceBeforeDraw() override
Definition: typeface.cpp:107
SkISize getISize() override
Definition: typeface.cpp:121
void onDraw(SkCanvas *canvas) override
Definition: typeface.cpp:123
SkString getName() const override
Definition: typeface.cpp:113
TypefaceStylesGM(bool applyKerning)
Definition: typeface.cpp:104
Definition: gm.h:110
const Paint & paint
Definition: color_source.cc:38
float SkScalar
Definition: extension.cpp:12
#define DEF_GM(CODE)
Definition: gm.h:40
static float max(float r, float g, float b)
Definition: hsl.cpp:49
std::u16string text
double y
double x
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
sk_sp< SkTypeface > DefaultPortableTypeface()
SkFont DefaultFont()
sk_sp< SkTypeface > CreateTypefaceFromResource(const char *resource, int ttcIndex)
sk_sp< SkTypeface > CreateTestTypeface(const char *name, SkFontStyle style)
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
font
Font Metadata and Metrics.
int32_t width
const Scalar scale
SeparatedVector2 offset
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
float fX
x-axis value
Definition: SkPoint_impl.h:164
void set(float x, float y)
Definition: SkPoint_impl.h:200
DEF_SIMPLE_GM(typeface_styling, canvas, 710, 360)
Definition: typeface.cpp:406
static void draw_typeface_rendering_gm(SkCanvas *canvas, sk_sp< SkTypeface > face, SkGlyphID glyph)
Definition: typeface.cpp:163
DEF_SIMPLE_GM_CAN_FAIL(typefacerendering, canvas, errMsg, 640, 840)
Definition: typeface.cpp:368
static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count, const SkFont &font)
Definition: typeface.cpp:46
static void getGlyphPositions(const SkFont &font, const uint16_t glyphs[], int count, SkScalar x, SkScalar y, SkPoint pos[])
Definition: typeface.cpp:34
constexpr int gStylesCount
Definition: typeface.cpp:96
static void drawKernText(SkCanvas *canvas, const void *text, size_t len, SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition: typeface.cpp:57
static constexpr SkFontStyle gStyles[]
Definition: typeface.cpp:89