• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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