• 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.h"
9 #include "sk_tool_utils.h"
10 #include "Resources.h"
11 #include "SkCanvas.h"
12 #include "SkString.h"
13 #include "SkSurfaceProps.h"
14 #include "SkTypeface.h"
15 #include "SkTypes.h"
16 
getGlyphPositions(const SkPaint & paint,const uint16_t glyphs[],int count,SkScalar x,SkScalar y,SkPoint pos[])17 static void getGlyphPositions(const SkPaint& paint, const uint16_t glyphs[],
18                              int count, SkScalar x, SkScalar y, SkPoint pos[]) {
19     SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
20 
21     SkAutoSTMalloc<128, SkScalar> widthStorage(count);
22     SkScalar* widths = widthStorage.get();
23     paint.getTextWidths(glyphs, count * sizeof(uint16_t), widths);
24 
25     for (int i = 0; i < count; ++i) {
26         pos[i].set(x, y);
27         x += widths[i];
28     }
29 }
30 
applyKerning(SkPoint pos[],const int32_t adjustments[],int count,const SkPaint & paint)31 static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count,
32                          const SkPaint& paint) {
33     SkScalar scale = paint.getTextSize() / paint.getTypeface()->getUnitsPerEm();
34 
35     SkScalar globalAdj = 0;
36     for (int i = 0; i < count - 1; ++i) {
37         globalAdj += adjustments[i] * scale;
38         pos[i + 1].fX += globalAdj;
39     }
40 }
41 
drawKernText(SkCanvas * canvas,const void * text,size_t len,SkScalar x,SkScalar y,const SkPaint & paint)42 static void drawKernText(SkCanvas* canvas, const void* text, size_t len,
43                          SkScalar x, SkScalar y, const SkPaint& paint) {
44     SkTypeface* face = paint.getTypeface();
45     if (!face) {
46         canvas->drawText(text, len, x, y, paint);
47         return;
48     }
49 
50     SkAutoSTMalloc<128, uint16_t> glyphStorage(len);
51     uint16_t* glyphs = glyphStorage.get();
52     int glyphCount = paint.textToGlyphs(text, len, glyphs);
53     if (glyphCount < 1) {
54         return;
55     }
56 
57     SkAutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1);
58     int32_t* adjustments = adjustmentStorage.get();
59     if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) {
60         canvas->drawText(text, len, x, y, paint);
61         return;
62     }
63 
64     SkPaint glyphPaint(paint);
65     glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
66 
67     SkAutoSTMalloc<128, SkPoint> posStorage(glyphCount);
68     SkPoint* pos = posStorage.get();
69     getGlyphPositions(glyphPaint, glyphs, glyphCount, x, y, pos);
70 
71     applyKerning(pos, adjustments, glyphCount, glyphPaint);
72     canvas->drawPosText(glyphs, glyphCount * sizeof(uint16_t), pos, glyphPaint);
73 }
74 
75 constexpr struct {
76     const char* fName;
77     SkTypeface::Style   fStyle;
78 } gFaceStyles[] = {
79     { "sans-serif", SkTypeface::kNormal },
80     { "sans-serif", SkTypeface::kBold },
81     { "sans-serif", SkTypeface::kItalic },
82     { "sans-serif", SkTypeface::kBoldItalic },
83     { "serif", SkTypeface::kNormal },
84     { "serif", SkTypeface::kBold },
85     { "serif", SkTypeface::kItalic },
86     { "serif", SkTypeface::kBoldItalic },
87     { "monospace", SkTypeface::kNormal },
88     { "monospace", SkTypeface::kBold },
89     { "monospace", SkTypeface::kItalic },
90     { "monospace", SkTypeface::kBoldItalic },
91 };
92 
93 constexpr int gFaceStylesCount = SK_ARRAY_COUNT(gFaceStyles);
94 
95 class TypefaceStylesGM : public skiagm::GM {
96     sk_sp<SkTypeface> fFaces[gFaceStylesCount];
97     bool fApplyKerning;
98 
99 public:
TypefaceStylesGM(bool applyKerning)100     TypefaceStylesGM(bool applyKerning)
101         : fApplyKerning(applyKerning) {
102         memset(fFaces, 0, sizeof(fFaces));
103     }
104 
105 protected:
onOnceBeforeDraw()106     void onOnceBeforeDraw() override {
107         for (int i = 0; i < gFaceStylesCount; i++) {
108             fFaces[i] = SkTypeface::MakeFromName(
109                     sk_tool_utils::platform_font_name(
110                         gFaceStyles[i].fName), SkFontStyle::FromOldStyle(gFaceStyles[i].fStyle));
111         }
112     }
113 
onShortName()114     SkString onShortName() override {
115         SkString name("typefacestyles");
116         if (fApplyKerning) {
117             name.append("_kerning");
118         }
119         name.append(sk_tool_utils::major_platform_os_name());
120         return name;
121     }
122 
onISize()123     SkISize onISize() override {
124         return SkISize::Make(640, 480);
125     }
126 
onDraw(SkCanvas * canvas)127     void onDraw(SkCanvas* canvas) override {
128         SkPaint paint;
129         paint.setAntiAlias(true);
130         paint.setTextSize(SkIntToScalar(30));
131 
132         const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons";
133         const size_t textLen = strlen(text);
134 
135         SkScalar x = SkIntToScalar(10);
136         SkScalar dy = paint.getFontMetrics(nullptr);
137         SkScalar y = dy;
138 
139         if (fApplyKerning) {
140             paint.setSubpixelText(true);
141         } else {
142             paint.setLinearText(true);
143         }
144         for (int i = 0; i < gFaceStylesCount; i++) {
145             paint.setTypeface(fFaces[i]);
146             canvas->drawText(text, textLen, x, y, paint);
147             if (fApplyKerning) {
148                 drawKernText(canvas, text, textLen, x + 240, y, paint);
149             }
150             y += dy;
151         }
152     }
153 
154 private:
155     typedef skiagm::GM INHERITED;
156 };
157 
158 DEF_GM( return new TypefaceStylesGM(false); )
DEF_GM(return new TypefaceStylesGM (true);)159 DEF_GM( return new TypefaceStylesGM(true); )
160 
161 ////////////////////////////////////////////////////////////////////////////////
162 
163 static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face,
164                                        char character = 'A') {
165         struct AliasType {
166             bool antiAlias;
167             bool subpixelAntitalias;
168             bool inLayer;
169         } constexpr aliasTypes[] {
170 #ifndef SK_BUILD_FOR_IOS
171             // This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering.
172             // The crash looks like
173             //   libTrueTypeScaler.dylib`<redacted> + 80
174             //   stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...)
175             //   ->  0x330b19d0 <+80>: strd   r2, r3, [r5, #36]
176             //       0x330b19d4 <+84>: movs   r3, #0x0
177             //       0x330b19d6 <+86>: add    r2, sp, #0x28
178             //       0x330b19d8 <+88>: ldr    r0, [r4, #0x4]
179             // Disable testing embedded bitmaps on iOS for now.
180             // See https://bug.skia.org/5530 .
181             { false, false, false },  // aliased
182 #endif
183             { true,  false, false },  // anti-aliased
184             { true,  true , false },  // subpixel anti-aliased
185             { true,  false, true  },  // anti-aliased in layer (flat pixel geometry)
186             { true,  true , true  },  // subpixel anti-aliased in layer (flat pixel geometry)
187         };
188 
189         // The hintgasp.ttf is designed for the following sizes to be different.
190         // GASP_DOGRAY                                      0x0002   0<=ppem<=10
191         // GASP_SYMMETRIC_SMOOTHING                         0x0008   0<=ppem<=10
192         // GASP_GRIDFIT                                     0x0001  11<=ppem<=12
193         // GASP_SYMMETRIC_GRIDFIT                           0x0004  11<=ppem<=12
194         // GASP_DOGRAY|GASP_GRIDFIT                         0x0003  13<=ppem<=14
195         // GASP_SYMMETRIC_SMOOTHING|GASP_SYMMETRIC_GRIDFIT  0x000C  13<=ppem<=14
196         // (neither)                                        0x0000  15<=ppem
197         // Odd sizes have embedded bitmaps.
198         constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 };
199 
200         constexpr SkPaint::Hinting hintingTypes[] = { SkPaint::kNo_Hinting,
201                                                       SkPaint::kSlight_Hinting,
202                                                       SkPaint::kNormal_Hinting,
203                                                       SkPaint::kFull_Hinting };
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         SkPaint paint;
218         paint.setTypeface(face);
219         paint.setEmbeddedBitmapText(true);
220 
221         SkScalar x = 0;
222         SkScalar xMax = x;
223         SkScalar xBase = 0;
224         SkScalar y = 0;  // The baseline of the previous output
225         for (const SubpixelType subpixel : subpixelTypes) {
226             y = 0;
227             paint.setSubpixelText(subpixel.requested);
228 
229             for (const AliasType& alias : aliasTypes) {
230                 paint.setAntiAlias(alias.antiAlias);
231                 paint.setLCDRenderText(alias.subpixelAntitalias);
232                 SkAutoCanvasRestore acr(canvas, false);
233                 if (alias.inLayer) {
234                     canvas->saveLayer(nullptr, &paint);
235                 }
236 
237                 for (const SkScalar& textSize : textSizes) {
238                     x = xBase + 5;
239                     paint.setTextSize(textSize);
240 
241                     SkScalar dy = SkScalarCeilToScalar(paint.getFontMetrics(nullptr));
242                     y += dy;
243                     for (const SkPaint::Hinting& hinting : hintingTypes) {
244                         paint.setHinting(hinting);
245 
246                         for (const bool& rotateABit : rotateABitTypes) {
247                             SkAutoCanvasRestore acr(canvas, true);
248                             if (rotateABit) {
249                                 canvas->rotate(2, x + subpixel.offset.x(),
250                                                   y + subpixel.offset.y());
251                             }
252                             canvas->drawText(&character, 1,
253                                              x + subpixel.offset.x(),
254                                              y + subpixel.offset.y(), paint);
255 
256                             SkScalar dx = SkScalarCeilToScalar(
257                                     paint.measureText(&character, 1)) + 5;
258                             x += dx;
259                             xMax = SkTMax(x, xMax);
260                         }
261                     }
262                 }
263                 y += 10;
264             }
265             xBase = xMax;
266         }
267 }
268 
269 DEF_SIMPLE_GM_BG_NAME(typefacerendering, canvas, 640, 680, SK_ColorWHITE,
270                       SkStringPrintf("typefacerendering%s",
271                                      sk_tool_utils::major_platform_os_name().c_str())) {
272     if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("/fonts/hintgasp.ttf")) {
273         draw_typeface_rendering_gm(canvas, std::move(face));
274     }
275 }
276 
277 // Type1 fonts don't currently work in Skia on Windows.
278 #ifndef SK_BUILD_FOR_WIN
279 
280 DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfa, canvas, 640, 680, SK_ColorWHITE,
281                       SkStringPrintf("typefacerendering_pfa%s",
282                                      sk_tool_utils::major_platform_os_name().c_str())) {
283     if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfa")) {
284         // This subsetted typeface doesn't have the character 'A'.
285         draw_typeface_rendering_gm(canvas, std::move(face), 'O');
286     }
287 }
288 
289 DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfb, canvas, 640, 680, SK_ColorWHITE,
290                       SkStringPrintf("typefacerendering_pfb%s",
291                                      sk_tool_utils::major_platform_os_name().c_str())) {
292     if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfb")) {
293         draw_typeface_rendering_gm(canvas, std::move(face), 'O');
294     }
295 }
296 
297 #endif
298