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
18static 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
32static 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
43static 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
76static constexpr SkFontStyle gStyles[] = {
77    SkFontStyle::Normal(),
78    SkFontStyle::Bold(),
79    SkFontStyle::Italic(),
80    SkFontStyle::BoldItalic(),
81};
82
83constexpr int gStylesCount = SK_ARRAY_COUNT(gStyles);
84
85class TypefaceStylesGM : public skiagm::GM {
86    sk_sp<SkTypeface> fFaces[gStylesCount];
87    bool fApplyKerning;
88
89public:
90    TypefaceStylesGM(bool applyKerning)
91        : fApplyKerning(applyKerning) {
92        memset(fFaces, 0, sizeof(fFaces));
93    }
94
95protected:
96    void onOnceBeforeDraw() override {
97        for (int i = 0; i < gStylesCount; i++) {
98            fFaces[i] = SkTypeface::MakeFromName(nullptr, gStyles[i]);
99        }
100    }
101
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
111    SkISize onISize() override {
112        return SkISize::Make(640, 480);
113    }
114
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
142private:
143    typedef skiagm::GM INHERITED;
144};
145
146DEF_GM( return new TypefaceStylesGM(false); )
147DEF_GM( return new TypefaceStylesGM(true); )
148
149////////////////////////////////////////////////////////////////////////////////
150
151static 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
257DEF_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
268DEF_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
277DEF_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