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