Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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) {
61 canvas->drawSimpleText(text, len, SkTextEncoding::kUTF8, x, y, font, paint);
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)) {
75 canvas->drawSimpleText(text, len, SkTextEncoding::kUTF8, x, y, font, paint);
76 return;
77 }
78
79
80 SkTextBlobBuilder builder;
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
95
96constexpr int gStylesCount = std::size(gStyles);
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);
412 font.setEdging(SkFont::Edging::kAntiAlias);
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]
int count
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[]
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:500
void drawSimpleText(const void *text, size_t byteLength, SkTextEncoding encoding, SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
void translate(SkScalar dx, SkScalar dy)
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)
void drawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y, const SkPaint &paint)
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
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
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
const Paint & paint
float SkScalar
Definition extension.cpp:12
const char * name
Definition fuchsia.cc:50
#define DEF_GM(CODE)
Definition gm.h:40
#define DEF_SIMPLE_GM_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H)
Definition gm.h:62
#define DEF_SIMPLE_GM(NAME, CANVAS, W, H)
Definition gm.h:50
std::u16string text
double y
double x
sk_sp< SkTypeface > DefaultPortableTypeface()
SkFont DefaultFont()
sk_sp< SkTypeface > CreateTypefaceFromResource(const char *resource, int ttcIndex)
sk_sp< SkTypeface > CreateTestTypeface(const char *name, SkFontStyle style)
int32_t width
const Scalar scale
Point offset
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
float fX
x-axis value
void set(float x, float y)
static void draw_typeface_rendering_gm(SkCanvas *canvas, sk_sp< SkTypeface > face, SkGlyphID glyph)
Definition typeface.cpp:163
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