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