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
17static 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
31static 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
42static 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
75constexpr 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
93constexpr int gFaceStylesCount = SK_ARRAY_COUNT(gFaceStyles);
94
95class TypefaceStylesGM : public skiagm::GM {
96    sk_sp<SkTypeface> fFaces[gFaceStylesCount];
97    bool fApplyKerning;
98
99public:
100    TypefaceStylesGM(bool applyKerning)
101        : fApplyKerning(applyKerning) {
102        memset(fFaces, 0, sizeof(fFaces));
103    }
104
105protected:
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
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
123    SkISize onISize() override {
124        return SkISize::Make(640, 480);
125    }
126
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
154private:
155    typedef skiagm::GM INHERITED;
156};
157
158DEF_GM( return new TypefaceStylesGM(false); )
159DEF_GM( return new TypefaceStylesGM(true); )
160
161////////////////////////////////////////////////////////////////////////////////
162
163static 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
269DEF_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
280DEF_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
289DEF_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