• 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/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