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