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.h"
9 #include "sk_tool_utils.h"
10 #include "Resources.h"
11 #include "SkBlurTypes.h"
12 #include "SkCanvas.h"
13 #include "SkFontStyle.h"
14 #include "SkMaskFilter.h"
15 #include "SkString.h"
16 #include "SkSurfaceProps.h"
17 #include "SkTextBlob.h"
18 #include "SkTypeface.h"
19 #include "SkTypes.h"
20
getGlyphPositions(const SkFont & font,const uint16_t glyphs[],int count,SkScalar x,SkScalar y,SkPoint pos[])21 static void getGlyphPositions(const SkFont& font, const uint16_t glyphs[],
22 int count, SkScalar x, SkScalar y, SkPoint pos[]) {
23 SkAutoSTMalloc<128, SkScalar> widthStorage(count);
24 SkScalar* widths = widthStorage.get();
25 font.getWidths(glyphs, count, widths);
26
27 for (int i = 0; i < count; ++i) {
28 pos[i].set(x, y);
29 x += widths[i];
30 }
31 }
32
applyKerning(SkPoint pos[],const int32_t adjustments[],int count,const SkFont & font)33 static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count,
34 const SkFont& font) {
35 SkScalar scale = font.getSize() / font.getTypefaceOrDefault()->getUnitsPerEm();
36
37 SkScalar globalAdj = 0;
38 for (int i = 0; i < count - 1; ++i) {
39 globalAdj += adjustments[i] * scale;
40 pos[i + 1].fX += globalAdj;
41 }
42 }
43
drawKernText(SkCanvas * canvas,const void * text,size_t len,SkScalar x,SkScalar y,const SkFont & font,const SkPaint & paint)44 static void drawKernText(SkCanvas* canvas, const void* text, size_t len,
45 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
46 SkTypeface* face = font.getTypefaceOrDefault();
47 if (!face) {
48 canvas->drawSimpleText(text, len, kUTF8_SkTextEncoding, x, y, font, paint);
49 return;
50 }
51
52 SkAutoSTMalloc<128, uint16_t> glyphStorage(len);
53 uint16_t* glyphs = glyphStorage.get();
54 int glyphCount = font.textToGlyphs(text, len, kUTF8_SkTextEncoding, glyphs, len);
55 if (glyphCount < 1) {
56 return;
57 }
58
59 SkAutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1);
60 int32_t* adjustments = adjustmentStorage.get();
61 if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) {
62 canvas->drawSimpleText(text, len, kUTF8_SkTextEncoding, x, y, font, paint);
63 return;
64 }
65
66
67 SkTextBlobBuilder builder;
68 auto rec = builder.allocRunPos(font, glyphCount);
69 memcpy(rec.glyphs, glyphs, glyphCount * sizeof(SkGlyphID));
70 getGlyphPositions(font, glyphs, glyphCount, x, y, rec.points());
71 applyKerning(rec.points(), adjustments, glyphCount, font);
72
73 canvas->drawTextBlob(builder.make(), 0, 0, paint);
74 }
75
76 static constexpr SkFontStyle gStyles[] = {
77 SkFontStyle::Normal(),
78 SkFontStyle::Bold(),
79 SkFontStyle::Italic(),
80 SkFontStyle::BoldItalic(),
81 };
82
83 constexpr int gStylesCount = SK_ARRAY_COUNT(gStyles);
84
85 class TypefaceStylesGM : public skiagm::GM {
86 sk_sp<SkTypeface> fFaces[gStylesCount];
87 bool fApplyKerning;
88
89 public:
TypefaceStylesGM(bool applyKerning)90 TypefaceStylesGM(bool applyKerning) : fApplyKerning(applyKerning) {}
91
92 protected:
onOnceBeforeDraw()93 void onOnceBeforeDraw() override {
94 for (int i = 0; i < gStylesCount; i++) {
95 fFaces[i] = SkTypeface::MakeFromName(nullptr, gStyles[i]);
96 }
97 }
98
onShortName()99 SkString onShortName() override {
100 SkString name("typefacestyles");
101 if (fApplyKerning) {
102 name.append("_kerning");
103 }
104 name.append(sk_tool_utils::platform_font_manager());
105 return name;
106 }
107
onISize()108 SkISize onISize() override {
109 return SkISize::Make(640, 480);
110 }
111
onDraw(SkCanvas * canvas)112 void onDraw(SkCanvas* canvas) override {
113 SkFont font;
114 font.setSize(30);
115
116 const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons";
117 const size_t textLen = strlen(text);
118
119 SkScalar x = SkIntToScalar(10);
120 SkScalar dy = font.getMetrics(nullptr);
121 SkScalar y = dy;
122
123 if (fApplyKerning) {
124 font.setSubpixel(true);
125 } else {
126 font.setLinearMetrics(true);
127 }
128
129 SkPaint paint;
130 for (int i = 0; i < gStylesCount; i++) {
131 font.setTypeface(fFaces[i]);
132 canvas->drawSimpleText(text, textLen, kUTF8_SkTextEncoding, x, y, font, paint);
133 if (fApplyKerning) {
134 drawKernText(canvas, text, textLen, x + 240, y, font, paint);
135 }
136 y += dy;
137 }
138 }
139
140 private:
141 typedef skiagm::GM INHERITED;
142 };
143
144 DEF_GM( return new TypefaceStylesGM(false); )
DEF_GM(return new TypefaceStylesGM (true);)145 DEF_GM( return new TypefaceStylesGM(true); )
146
147 ////////////////////////////////////////////////////////////////////////////////
148
149 static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face,
150 char character = 'A') {
151 struct AliasType {
152 bool antiAlias;
153 bool subpixelAntitalias;
154 bool inLayer;
155 } constexpr aliasTypes[] {
156 #ifndef SK_BUILD_FOR_IOS
157 // This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering.
158 // The crash looks like
159 // libTrueTypeScaler.dylib`<redacted> + 80
160 // stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...)
161 // -> 0x330b19d0 <+80>: strd r2, r3, [r5, #36]
162 // 0x330b19d4 <+84>: movs r3, #0x0
163 // 0x330b19d6 <+86>: add r2, sp, #0x28
164 // 0x330b19d8 <+88>: ldr r0, [r4, #0x4]
165 // Disable testing embedded bitmaps on iOS for now.
166 // See https://bug.skia.org/5530 .
167 { false, false, false }, // aliased
168 #endif
169 { true, false, false }, // anti-aliased
170 { true, true , false }, // subpixel anti-aliased
171 { true, false, true }, // anti-aliased in layer (flat pixel geometry)
172 { true, true , true }, // subpixel anti-aliased in layer (flat pixel geometry)
173 };
174
__anon30aa69240102(AliasType at) 175 auto compute_edging = [](AliasType at) {
176 if (at.antiAlias) {
177 if (at.subpixelAntitalias) {
178 return SkFont::Edging::kSubpixelAntiAlias;
179 } else {
180 return SkFont::Edging::kAntiAlias;
181 }
182 } else {
183 return SkFont::Edging::kAlias;
184 }
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[] = {
199 kNo_SkFontHinting,
200 kSlight_SkFontHinting,
201 kNormal_SkFontHinting,
202 kFull_SkFontHinting
203 };
204
205 struct SubpixelType {
206 bool requested;
207 SkVector offset;
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 {
219 SkPaint paint;
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(compute_edging(alias));
233 SkAutoCanvasRestore acr(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 acr(canvas, true);
249 if (rotateABit) {
250 canvas->rotate(2, x + subpixel.offset.x(),
251 y + subpixel.offset.y());
252 }
253 canvas->drawSimpleText(&character, 1, kUTF8_SkTextEncoding,
254 x + subpixel.offset.x(),
255 y + subpixel.offset.y(), font, paint);
256
257 SkScalar dx = SkScalarCeilToScalar(
258 font.measureText(&character, 1, kUTF8_SkTextEncoding)) + 5;
259 x += dx;
260 xMax = SkTMax(x, xMax);
261 }
262 }
263 }
264 y += 10;
265 }
266 xBase = xMax;
267 }
268 }
269
270 constexpr struct StyleTests {
271 SkPaint::Style style;
272 SkScalar strokeWidth;
273 } styleTypes[] = {
274 { SkPaint::kFill_Style, 0.0f},
275 { SkPaint::kStroke_Style, 0.0f},
276 { SkPaint::kStroke_Style, 0.5f},
277 { SkPaint::kStrokeAndFill_Style, 1.0f},
278 };
279
280 constexpr bool fakeBoldTypes[] = { false, true };
281
282 {
283 SkPaint paint;
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(compute_edging(alias));
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(&character, 1, kUTF8_SkTextEncoding, x, y, font, paint);
304
305 SkScalar dx = SkScalarCeilToScalar(font.measureText(&character, 1,
306 kUTF8_SkTextEncoding)) + 5;
307 x += dx;
308 }
309 }
310 y += 10;
311 }
312 }
313
314 constexpr struct MaskTests {
315 SkBlurStyle style;
316 SkScalar sigma;
317 } maskTypes[] = {
318 { SkBlurStyle::kNormal_SkBlurStyle, 0.0f},
319 { SkBlurStyle::kSolid_SkBlurStyle, 0.0f},
320 { SkBlurStyle::kOuter_SkBlurStyle, 0.0f},
321 { SkBlurStyle::kInner_SkBlurStyle, 0.0f},
322
323 { SkBlurStyle::kNormal_SkBlurStyle, 0.5f},
324 { SkBlurStyle::kSolid_SkBlurStyle, 0.5f},
325 { SkBlurStyle::kOuter_SkBlurStyle, 0.5f},
326 { SkBlurStyle::kInner_SkBlurStyle, 0.5f},
327
328 { SkBlurStyle::kNormal_SkBlurStyle, 2.0f},
329 { SkBlurStyle::kSolid_SkBlurStyle, 2.0f},
330 { SkBlurStyle::kOuter_SkBlurStyle, 2.0f},
331 { SkBlurStyle::kInner_SkBlurStyle, 2.0f},
332 };
333
334 {
335 SkPaint paint;
336
337 SkFont font(face, 16);
338
339 SkScalar x = 0;
340 {
341 for (const AliasType& alias : aliasTypes) {
342 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr));
343 y += dy;
344 x = 5;
345
346 font.setEdging(compute_edging(alias));
347 SkAutoCanvasRestore acr(canvas, false);
348 if (alias.inLayer) {
349 canvas->saveLayer(nullptr, &paint);
350 }
351 for (const MaskTests& mask : maskTypes) {
352 paint.setMaskFilter(SkMaskFilter::MakeBlur(mask.style, mask.sigma));
353 canvas->drawSimpleText(&character, 1, kUTF8_SkTextEncoding, x, y, font, paint);
354
355 SkScalar dx = SkScalarCeilToScalar(font.measureText(&character, 1,
356 kUTF8_SkTextEncoding)) + 5;
357 x += dx;
358 }
359 paint.setMaskFilter(nullptr);
360 }
361 y += 10;
362 }
363 }
364 }
365
366 DEF_SIMPLE_GM_BG_NAME(typefacerendering, canvas, 640, 840, SK_ColorWHITE,
367 SkStringPrintf("typefacerendering%s",
368 sk_tool_utils::platform_font_manager())) {
369 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/hintgasp.ttf")) {
370 draw_typeface_rendering_gm(canvas, std::move(face));
371 }
372 }
373
374 // Type1 fonts don't currently work in Skia on Windows.
375 #ifndef SK_BUILD_FOR_WIN
376
377 DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfa, canvas, 640, 840, SK_ColorWHITE,
378 SkStringPrintf("typefacerendering_pfa%s",
379 sk_tool_utils::platform_font_manager())) {
380 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfa")) {
381 // This subsetted typeface doesn't have the character 'A'.
382 draw_typeface_rendering_gm(canvas, std::move(face), 'O');
383 }
384 }
385
386 DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfb, canvas, 640, 840, SK_ColorWHITE,
387 SkStringPrintf("typefacerendering_pfb%s",
388 sk_tool_utils::platform_font_manager())) {
389 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfb")) {
390 draw_typeface_rendering_gm(canvas, std::move(face), 'O');
391 }
392 }
393
394 #endif
395