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"
9 #include "include/core/SkBlurTypes.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkFont.h"
12 #include "include/core/SkFontStyle.h"
13 #include "include/core/SkFontTypes.h"
14 #include "include/core/SkMaskFilter.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPoint.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkSize.h"
20 #include "include/core/SkString.h"
21 #include "include/core/SkTextBlob.h"
22 #include "include/core/SkTypeface.h"
23 #include "include/core/SkTypes.h"
24 #include "include/private/base/SkTemplates.h"
25 #include "tools/Resources.h"
26
27 #include <string.h>
28 #include <utility>
29
30 using namespace skia_private;
31
getGlyphPositions(const SkFont & font,const uint16_t glyphs[],int count,SkScalar x,SkScalar y,SkPoint pos[])32 static void getGlyphPositions(const SkFont& font, const uint16_t glyphs[],
33 int count, SkScalar x, SkScalar y, SkPoint pos[]) {
34 AutoSTMalloc<128, SkScalar> widthStorage(count);
35 SkScalar* widths = widthStorage.get();
36 font.getWidths(glyphs, count, widths);
37
38 for (int i = 0; i < count; ++i) {
39 pos[i].set(x, y);
40 x += widths[i];
41 }
42 }
43
applyKerning(SkPoint pos[],const int32_t adjustments[],int count,const SkFont & font)44 static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count,
45 const SkFont& font) {
46 SkScalar scale = font.getSize() / font.getTypefaceOrDefault()->getUnitsPerEm();
47
48 SkScalar globalAdj = 0;
49 for (int i = 0; i < count - 1; ++i) {
50 globalAdj += adjustments[i] * scale;
51 pos[i + 1].fX += globalAdj;
52 }
53 }
54
drawKernText(SkCanvas * canvas,const void * text,size_t len,SkScalar x,SkScalar y,const SkFont & font,const SkPaint & paint)55 static void drawKernText(SkCanvas* canvas, const void* text, size_t len,
56 SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
57 SkTypeface* face = font.getTypefaceOrDefault();
58 if (!face) {
59 canvas->drawSimpleText(text, len, SkTextEncoding::kUTF8, x, y, font, paint);
60 return;
61 }
62
63 AutoSTMalloc<128, uint16_t> glyphStorage(len);
64 uint16_t* glyphs = glyphStorage.get();
65 int glyphCount = font.textToGlyphs(text, len, SkTextEncoding::kUTF8, glyphs, len);
66 if (glyphCount < 1) {
67 return;
68 }
69
70 AutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1);
71 int32_t* adjustments = adjustmentStorage.get();
72 if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) {
73 canvas->drawSimpleText(text, len, SkTextEncoding::kUTF8, x, y, font, paint);
74 return;
75 }
76
77
78 SkTextBlobBuilder builder;
79 auto rec = builder.allocRunPos(font, glyphCount);
80 memcpy(rec.glyphs, glyphs, glyphCount * sizeof(SkGlyphID));
81 getGlyphPositions(font, glyphs, glyphCount, x, y, rec.points());
82 applyKerning(rec.points(), adjustments, glyphCount, font);
83
84 canvas->drawTextBlob(builder.make(), 0, 0, paint);
85 }
86
87 static constexpr SkFontStyle gStyles[] = {
88 SkFontStyle::Normal(),
89 SkFontStyle::Bold(),
90 SkFontStyle::Italic(),
91 SkFontStyle::BoldItalic(),
92 };
93
94 constexpr int gStylesCount = std::size(gStyles);
95
96 class TypefaceStylesGM : public skiagm::GM {
97 sk_sp<SkTypeface> fFaces[gStylesCount];
98 bool fApplyKerning;
99
100 public:
TypefaceStylesGM(bool applyKerning)101 TypefaceStylesGM(bool applyKerning) : fApplyKerning(applyKerning) {}
102
103 protected:
onOnceBeforeDraw()104 void onOnceBeforeDraw() override {
105 for (int i = 0; i < gStylesCount; i++) {
106 fFaces[i] = SkTypeface::MakeFromName(nullptr, gStyles[i]);
107 }
108 }
109
onShortName()110 SkString onShortName() override {
111 SkString name("typefacestyles");
112 if (fApplyKerning) {
113 name.append("_kerning");
114 }
115 return name;
116 }
117
onISize()118 SkISize onISize() override {
119 return SkISize::Make(640, 480);
120 }
121
onDraw(SkCanvas * canvas)122 void onDraw(SkCanvas* canvas) override {
123 SkFont font;
124 font.setSize(30);
125
126 const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons";
127 const size_t textLen = strlen(text);
128
129 SkScalar x = SkIntToScalar(10);
130 SkScalar dy = font.getMetrics(nullptr);
131 SkScalar y = dy;
132
133 if (fApplyKerning) {
134 font.setSubpixel(true);
135 } else {
136 font.setLinearMetrics(true);
137 }
138
139 SkPaint paint;
140 for (int i = 0; i < gStylesCount; i++) {
141 font.setTypeface(fFaces[i]);
142 canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint);
143 if (fApplyKerning) {
144 drawKernText(canvas, text, textLen, x + 240, y, font, paint);
145 }
146 y += dy;
147 }
148 }
149
150 private:
151 using INHERITED = skiagm::GM;
152 };
153
154 DEF_GM( return new TypefaceStylesGM(false); )
DEF_GM(return new TypefaceStylesGM (true);)155 DEF_GM( return new TypefaceStylesGM(true); )
156
157 ////////////////////////////////////////////////////////////////////////////////
158
159 static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face, SkGlyphID glyph) {
160 struct AliasType {
161 SkFont::Edging edging;
162 bool inLayer;
163 } constexpr aliasTypes[] {
164 #ifndef SK_BUILD_FOR_IOS
165 // This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering.
166 // The crash looks like
167 // libTrueTypeScaler.dylib`<redacted> + 80
168 // stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...)
169 // -> 0x330b19d0 <+80>: strd r2, r3, [r5, #36]
170 // 0x330b19d4 <+84>: movs r3, #0x0
171 // 0x330b19d6 <+86>: add r2, sp, #0x28
172 // 0x330b19d8 <+88>: ldr r0, [r4, #0x4]
173 // Disable testing embedded bitmaps on iOS for now.
174 // See https://bug.skia.org/5530 .
175 { SkFont::Edging::kAlias , false },
176 #endif
177 { SkFont::Edging::kAntiAlias , false },
178 { SkFont::Edging::kSubpixelAntiAlias, false },
179 { SkFont::Edging::kAntiAlias , true },
180 { SkFont::Edging::kSubpixelAntiAlias, true },
181 };
182
183 // The hintgasp.ttf is designed for the following sizes to be different.
184 // GASP_DOGRAY 0x0002 0<=ppem<=10
185 // GASP_SYMMETRIC_SMOOTHING 0x0008 0<=ppem<=10
186 // GASP_GRIDFIT 0x0001 11<=ppem<=12
187 // GASP_SYMMETRIC_GRIDFIT 0x0004 11<=ppem<=12
188 // GASP_DOGRAY|GASP_GRIDFIT 0x0003 13<=ppem<=14
189 // GASP_SYMMETRIC_SMOOTHING|GASP_SYMMETRIC_GRIDFIT 0x000C 13<=ppem<=14
190 // (neither) 0x0000 15<=ppem
191 // Odd sizes have embedded bitmaps.
192 constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 };
193
194 constexpr SkFontHinting hintingTypes[] = {
195 SkFontHinting::kNone,
196 SkFontHinting::kSlight,
197 SkFontHinting::kNormal,
198 SkFontHinting::kFull
199 };
200
201 struct SubpixelType {
202 bool requested;
203 SkVector offset;
204 } constexpr subpixelTypes[] = {
205 { false, { 0.00, 0.00 } },
206 { true , { 0.00, 0.00 } },
207 { true , { 0.25, 0.00 } },
208 { true , { 0.25, 0.25 } },
209 };
210
211 constexpr bool rotateABitTypes[] = { false, true };
212
213 SkScalar y = 0; // The baseline of the previous output
214 {
215 SkPaint paint;
216
217 SkFont font(face);
218 font.setEmbeddedBitmaps(true);
219
220 SkScalar x = 0;
221 SkScalar xMax = x;
222 SkScalar xBase = 0;
223 for (const SubpixelType subpixel : subpixelTypes) {
224 y = 0;
225 font.setSubpixel(subpixel.requested);
226
227 for (const AliasType& alias : aliasTypes) {
228 font.setEdging(alias.edging);
229 SkAutoCanvasRestore acr1(canvas, false);
230 if (alias.inLayer) {
231 canvas->saveLayer(nullptr, &paint);
232 }
233
234 for (const SkScalar& textSize : textSizes) {
235 x = xBase + 5;
236 font.setSize(textSize);
237
238 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr));
239 y += dy;
240 for (const SkFontHinting& hinting : hintingTypes) {
241 font.setHinting(hinting);
242
243 for (const bool& rotateABit : rotateABitTypes) {
244 SkAutoCanvasRestore acr2(canvas, true);
245 if (rotateABit) {
246 canvas->rotate(2, x + subpixel.offset.x(),
247 y + subpixel.offset.y());
248 }
249 canvas->drawSimpleText(&glyph, sizeof(glyph), SkTextEncoding::kGlyphID,
250 x + subpixel.offset.x(),
251 y + subpixel.offset.y(), font, paint);
252
253 SkScalar dx = SkScalarCeilToScalar(font.measureText(
254 &glyph, sizeof(glyph), SkTextEncoding::kGlyphID)) + 5;
255 x += dx;
256 xMax = std::max(x, xMax);
257 }
258 }
259 }
260 y += 10;
261 }
262 xBase = xMax;
263 }
264 }
265
266 constexpr struct StyleTests {
267 SkPaint::Style style;
268 SkScalar strokeWidth;
269 } styleTypes[] = {
270 { SkPaint::kFill_Style, 0.0f},
271 { SkPaint::kStroke_Style, 0.0f},
272 { SkPaint::kStroke_Style, 0.5f},
273 { SkPaint::kStrokeAndFill_Style, 1.0f},
274 };
275
276 constexpr bool fakeBoldTypes[] = { false, true };
277
278 {
279 SkPaint paint;
280
281 SkFont font(face, 16);
282
283 SkScalar x = 0;
284 for (const bool& fakeBold : fakeBoldTypes) {
285 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr));
286 y += dy;
287 x = 5;
288
289 font.setEmbolden(fakeBold);
290 for (const AliasType& alias : aliasTypes) {
291 font.setEdging(alias.edging);
292 SkAutoCanvasRestore acr(canvas, false);
293 if (alias.inLayer) {
294 canvas->saveLayer(nullptr, &paint);
295 }
296 for (const StyleTests& style : styleTypes) {
297 paint.setStyle(style.style);
298 paint.setStrokeWidth(style.strokeWidth);
299 canvas->drawSimpleText(&glyph, sizeof(glyph), SkTextEncoding::kGlyphID,
300 x, y, font, paint);
301
302 SkScalar dx = SkScalarCeilToScalar(font.measureText(
303 &glyph, sizeof(glyph), SkTextEncoding::kGlyphID)) + 5;
304 x += dx;
305 }
306 }
307 y += 10;
308 }
309 }
310
311 constexpr struct MaskTests {
312 SkBlurStyle style;
313 SkScalar sigma;
314 } maskTypes[] = {
315 { SkBlurStyle::kNormal_SkBlurStyle, 0.0f},
316 { SkBlurStyle::kSolid_SkBlurStyle, 0.0f},
317 { SkBlurStyle::kOuter_SkBlurStyle, 0.0f},
318 { SkBlurStyle::kInner_SkBlurStyle, 0.0f},
319
320 { SkBlurStyle::kNormal_SkBlurStyle, 0.5f},
321 { SkBlurStyle::kSolid_SkBlurStyle, 0.5f},
322 { SkBlurStyle::kOuter_SkBlurStyle, 0.5f},
323 { SkBlurStyle::kInner_SkBlurStyle, 0.5f},
324
325 { SkBlurStyle::kNormal_SkBlurStyle, 2.0f},
326 { SkBlurStyle::kSolid_SkBlurStyle, 2.0f},
327 { SkBlurStyle::kOuter_SkBlurStyle, 2.0f},
328 { SkBlurStyle::kInner_SkBlurStyle, 2.0f},
329 };
330
331 {
332 SkPaint paint;
333
334 SkFont font(face, 16);
335
336 SkScalar x = 0;
337 {
338 for (const AliasType& alias : aliasTypes) {
339 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr));
340 y += dy;
341 x = 5;
342
343 font.setEdging(alias.edging);
344 SkAutoCanvasRestore acr(canvas, false);
345 if (alias.inLayer) {
346 canvas->saveLayer(nullptr, &paint);
347 }
348 for (const MaskTests& mask : maskTypes) {
349 paint.setMaskFilter(SkMaskFilter::MakeBlur(mask.style, mask.sigma));
350 canvas->drawSimpleText(&glyph, sizeof(glyph), SkTextEncoding::kGlyphID,
351 x, y, font, paint);
352
353 SkScalar dx = SkScalarCeilToScalar(font.measureText(
354 &glyph, sizeof(glyph), SkTextEncoding::kGlyphID)) + 5;
355 x += dx;
356 }
357 paint.setMaskFilter(nullptr);
358 }
359 y += 10;
360 }
361 }
362 }
363
364 DEF_SIMPLE_GM_CAN_FAIL(typefacerendering, canvas, errMsg, 640, 840) {
365 sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/hintgasp.ttf");
366 if (!face) {
367 return skiagm::DrawResult::kSkip;
368 }
369 draw_typeface_rendering_gm(canvas, face, face->unicharToGlyph('A'));
370
371 // Should draw nothing and not do anything undefined.
372 draw_typeface_rendering_gm(canvas, face, 0xFFFF);
373 return skiagm::DrawResult::kOk;
374 }
375
376 // Type1 fonts don't currently work in Skia on Windows.
377 #ifndef SK_BUILD_FOR_WIN
378
379 DEF_SIMPLE_GM_CAN_FAIL(typefacerendering_pfa, canvas, errMsg, 640, 840) {
380 sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfa");
381 if (!face) {
382 return skiagm::DrawResult::kSkip;
383 }
384 draw_typeface_rendering_gm(canvas, face, face->unicharToGlyph('O'));
385 return skiagm::DrawResult::kOk;
386 }
387
388 DEF_SIMPLE_GM_CAN_FAIL(typefacerendering_pfb, canvas, errMsg, 640, 840) {
389 sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfb");
390 if (!face) {
391 return skiagm::DrawResult::kSkip;
392 }
393 draw_typeface_rendering_gm(canvas, face, face->unicharToGlyph('O'));
394 return skiagm::DrawResult::kOk;
395 }
396
397 #endif
398
399 ////////////////////////////////////////////////////////////////////////////////
400
401 #include "include/effects/SkStrokeAndFillPathEffect.h"
402
403 // Exercise different paint styles and embolden, and compare with strokeandfill patheffect
404 DEF_SIMPLE_GM(typeface_styling, canvas, 710, 360) {
405 sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto-Regular.ttf");
406 SkFont font;
407 font.setTypeface(face);
408 font.setSize(100);
409 font.setEdging(SkFont::Edging::kAntiAlias);
410
411 uint16_t glyphs[1] = { font.unicharToGlyph('A') };
412 SkPoint pos[1] = { {0, 0} };
413
__anonbcb446b20102(SkPaint::Style style, float width, sk_sp<SkPathEffect> pe) 414 auto draw = [&](SkPaint::Style style, float width, sk_sp<SkPathEffect> pe) {
415 // Draws 3 rows:
416 // 1. normal
417 // 2. emboldened
418 // 3. normal(white) on top of emboldened (to show the delta)
419
420 SkPaint paint;
421 paint.setStyle(style);
422 paint.setStrokeWidth(width);
423 paint.setPathEffect(pe);
424
425 font.setEmbolden(true);
426 canvas->drawGlyphs(1, glyphs, pos, {20, 120*2}, font, paint);
427 canvas->drawGlyphs(1, glyphs, pos, {20, 120*3}, font, paint);
428
429 font.setEmbolden(false);
430 canvas->drawGlyphs(1, glyphs, pos, {20, 120*1}, font, paint);
431 paint.setColor(SK_ColorYELLOW);
432 canvas->drawGlyphs(1, glyphs, pos, {20, 120*3}, font, paint);
433 };
434
435 const struct {
436 SkPaint::Style style;
437 float width;
438 bool usePE;
439 } recs[] = {
440 { SkPaint::kFill_Style, 0, false },
441 { SkPaint::kStroke_Style, 0, false },
442 { SkPaint::kStroke_Style, 3, false },
443 { SkPaint::kStrokeAndFill_Style, 0, false },
444 { SkPaint::kStrokeAndFill_Style, 3, false },
445 { SkPaint::kStroke_Style, 0, true },
446 { SkPaint::kStroke_Style, 3, true },
447 };
448
449 canvas->translate(0, -20);
450 auto pe = SkStrokeAndFillPathEffect::Make();
451 for (auto r : recs) {
452 draw(r.style, r.width, r.usePE ? pe : nullptr);
453 canvas->translate(100, 0);
454 }
455 }
456