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