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         return name;
105     }
106 
onISize()107     SkISize onISize() override {
108         return SkISize::Make(640, 480);
109     }
110 
onDraw(SkCanvas * canvas)111     void onDraw(SkCanvas* canvas) override {
112         SkFont font;
113         font.setSize(30);
114 
115         const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons";
116         const size_t textLen = strlen(text);
117 
118         SkScalar x = SkIntToScalar(10);
119         SkScalar dy = font.getMetrics(nullptr);
120         SkScalar y = dy;
121 
122         if (fApplyKerning) {
123             font.setSubpixel(true);
124         } else {
125             font.setLinearMetrics(true);
126         }
127 
128         SkPaint paint;
129         for (int i = 0; i < gStylesCount; i++) {
130             font.setTypeface(fFaces[i]);
131             canvas->drawSimpleText(text, textLen, kUTF8_SkTextEncoding, x, y, font, paint);
132             if (fApplyKerning) {
133                 drawKernText(canvas, text, textLen, x + 240, y, font, paint);
134             }
135             y += dy;
136         }
137     }
138 
139 private:
140     typedef skiagm::GM INHERITED;
141 };
142 
143 DEF_GM( return new TypefaceStylesGM(false); )
DEF_GM(return new TypefaceStylesGM (true);)144 DEF_GM( return new TypefaceStylesGM(true); )
145 
146 ////////////////////////////////////////////////////////////////////////////////
147 
148 static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face,
149                                        char character = 'A') {
150     struct AliasType {
151         SkFont::Edging edging;
152         bool inLayer;
153     } constexpr aliasTypes[] {
154 #ifndef SK_BUILD_FOR_IOS
155         // This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering.
156         // The crash looks like
157         //   libTrueTypeScaler.dylib`<redacted> + 80
158         //   stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...)
159         //   ->  0x330b19d0 <+80>: strd   r2, r3, [r5, #36]
160         //       0x330b19d4 <+84>: movs   r3, #0x0
161         //       0x330b19d6 <+86>: add    r2, sp, #0x28
162         //       0x330b19d8 <+88>: ldr    r0, [r4, #0x4]
163         // Disable testing embedded bitmaps on iOS for now.
164         // See https://bug.skia.org/5530 .
165         { SkFont::Edging::kAlias            , false },
166 #endif
167         { SkFont::Edging::kAntiAlias        , false },
168         { SkFont::Edging::kSubpixelAntiAlias, false },
169         { SkFont::Edging::kAntiAlias        , true  },
170         { SkFont::Edging::kSubpixelAntiAlias, true  },
171     };
172 
173     // The hintgasp.ttf is designed for the following sizes to be different.
174     // GASP_DOGRAY                                      0x0002   0<=ppem<=10
175     // GASP_SYMMETRIC_SMOOTHING                         0x0008   0<=ppem<=10
176     // GASP_GRIDFIT                                     0x0001  11<=ppem<=12
177     // GASP_SYMMETRIC_GRIDFIT                           0x0004  11<=ppem<=12
178     // GASP_DOGRAY|GASP_GRIDFIT                         0x0003  13<=ppem<=14
179     // GASP_SYMMETRIC_SMOOTHING|GASP_SYMMETRIC_GRIDFIT  0x000C  13<=ppem<=14
180     // (neither)                                        0x0000  15<=ppem
181     // Odd sizes have embedded bitmaps.
182     constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 };
183 
184     constexpr SkFontHinting hintingTypes[] = {
185         kNo_SkFontHinting,
186         kSlight_SkFontHinting,
187         kNormal_SkFontHinting,
188         kFull_SkFontHinting
189     };
190 
191     struct SubpixelType {
192         bool requested;
193         SkVector offset;
194     } constexpr subpixelTypes[] = {
195         { false, { 0.00, 0.00 } },
196         { true , { 0.00, 0.00 } },
197         { true , { 0.25, 0.00 } },
198         { true , { 0.25, 0.25 } },
199     };
200 
201     constexpr bool rotateABitTypes[] = { false, true };
202 
203     SkScalar y = 0;  // The baseline of the previous output
204     {
205         SkPaint paint;
206 
207         SkFont font(face);
208         font.setEmbeddedBitmaps(true);
209 
210         SkScalar x = 0;
211         SkScalar xMax = x;
212         SkScalar xBase = 0;
213         for (const SubpixelType subpixel : subpixelTypes) {
214             y = 0;
215             font.setSubpixel(subpixel.requested);
216 
217             for (const AliasType& alias : aliasTypes) {
218                 font.setEdging(alias.edging);
219                 SkAutoCanvasRestore acr(canvas, false);
220                 if (alias.inLayer) {
221                     canvas->saveLayer(nullptr, &paint);
222                 }
223 
224                 for (const SkScalar& textSize : textSizes) {
225                     x = xBase + 5;
226                     font.setSize(textSize);
227 
228                     SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr));
229                     y += dy;
230                     for (const SkFontHinting& hinting : hintingTypes) {
231                         font.setHinting(hinting);
232 
233                         for (const bool& rotateABit : rotateABitTypes) {
234                             SkAutoCanvasRestore acr(canvas, true);
235                             if (rotateABit) {
236                                 canvas->rotate(2, x + subpixel.offset.x(),
237                                                   y + subpixel.offset.y());
238                             }
239                             canvas->drawSimpleText(&character, 1, kUTF8_SkTextEncoding,
240                                                    x + subpixel.offset.x(),
241                                                    y + subpixel.offset.y(), font, paint);
242 
243                             SkScalar dx = SkScalarCeilToScalar(
244                                     font.measureText(&character, 1, kUTF8_SkTextEncoding)) + 5;
245                             x += dx;
246                             xMax = SkTMax(x, xMax);
247                         }
248                     }
249                 }
250                 y += 10;
251             }
252             xBase = xMax;
253         }
254     }
255 
256     constexpr struct StyleTests {
257         SkPaint::Style style;
258         SkScalar strokeWidth;
259     } styleTypes[] = {
260         { SkPaint::kFill_Style, 0.0f},
261         { SkPaint::kStroke_Style, 0.0f},
262         { SkPaint::kStroke_Style, 0.5f},
263         { SkPaint::kStrokeAndFill_Style, 1.0f},
264     };
265 
266     constexpr bool fakeBoldTypes[] = { false, true };
267 
268     {
269         SkPaint paint;
270 
271         SkFont font(face, 16);
272 
273         SkScalar x = 0;
274         for (const bool& fakeBold : fakeBoldTypes) {
275             SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr));
276             y += dy;
277             x = 5;
278 
279             font.setEmbolden(fakeBold);
280             for (const AliasType& alias : aliasTypes) {
281                 font.setEdging(alias.edging);
282                 SkAutoCanvasRestore acr(canvas, false);
283                 if (alias.inLayer) {
284                     canvas->saveLayer(nullptr, &paint);
285                 }
286                 for (const StyleTests& style : styleTypes) {
287                     paint.setStyle(style.style);
288                     paint.setStrokeWidth(style.strokeWidth);
289                     canvas->drawSimpleText(&character, 1, kUTF8_SkTextEncoding, x, y, font, paint);
290 
291                     SkScalar dx = SkScalarCeilToScalar(font.measureText(&character, 1,
292                                                                         kUTF8_SkTextEncoding)) + 5;
293                     x += dx;
294                 }
295             }
296             y += 10;
297         }
298     }
299 
300     constexpr struct MaskTests {
301         SkBlurStyle style;
302         SkScalar sigma;
303     } maskTypes[] = {
304         { SkBlurStyle::kNormal_SkBlurStyle, 0.0f},
305         { SkBlurStyle::kSolid_SkBlurStyle, 0.0f},
306         { SkBlurStyle::kOuter_SkBlurStyle, 0.0f},
307         { SkBlurStyle::kInner_SkBlurStyle, 0.0f},
308 
309         { SkBlurStyle::kNormal_SkBlurStyle, 0.5f},
310         { SkBlurStyle::kSolid_SkBlurStyle, 0.5f},
311         { SkBlurStyle::kOuter_SkBlurStyle, 0.5f},
312         { SkBlurStyle::kInner_SkBlurStyle, 0.5f},
313 
314         { SkBlurStyle::kNormal_SkBlurStyle, 2.0f},
315         { SkBlurStyle::kSolid_SkBlurStyle, 2.0f},
316         { SkBlurStyle::kOuter_SkBlurStyle, 2.0f},
317         { SkBlurStyle::kInner_SkBlurStyle, 2.0f},
318     };
319 
320     {
321         SkPaint paint;
322 
323         SkFont font(face, 16);
324 
325         SkScalar x = 0;
326         {
327             for (const AliasType& alias : aliasTypes) {
328                 SkScalar dy = SkScalarCeilToScalar(font.getMetrics(nullptr));
329                 y += dy;
330                 x = 5;
331 
332                 font.setEdging(alias.edging);
333                 SkAutoCanvasRestore acr(canvas, false);
334                 if (alias.inLayer) {
335                     canvas->saveLayer(nullptr, &paint);
336                 }
337                 for (const MaskTests& mask : maskTypes) {
338                     paint.setMaskFilter(SkMaskFilter::MakeBlur(mask.style, mask.sigma));
339                     canvas->drawSimpleText(&character, 1, kUTF8_SkTextEncoding, x, y, font, paint);
340 
341                     SkScalar dx = SkScalarCeilToScalar(font.measureText(&character, 1,
342                                                                         kUTF8_SkTextEncoding)) + 5;
343                     x += dx;
344                 }
345                 paint.setMaskFilter(nullptr);
346             }
347             y += 10;
348         }
349     }
350 }
351 
352 DEF_SIMPLE_GM(typefacerendering, canvas, 640, 840) {
353     if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/hintgasp.ttf")) {
354         draw_typeface_rendering_gm(canvas, std::move(face));
355     }
356 }
357 
358 // Type1 fonts don't currently work in Skia on Windows.
359 #ifndef SK_BUILD_FOR_WIN
360 
361 DEF_SIMPLE_GM(typefacerendering_pfa, canvas, 640, 840) {
362     if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfa")) {
363         // This subsetted typeface doesn't have the character 'A'.
364         draw_typeface_rendering_gm(canvas, std::move(face), 'O');
365     }
366 }
367 
368 DEF_SIMPLE_GM(typefacerendering_pfb, canvas, 640, 840) {
369     if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfb")) {
370         draw_typeface_rendering_gm(canvas, std::move(face), 'O');
371     }
372 }
373 
374 #endif
375