blob: 028e2a1ca3c492504ea3a74d574cee155de7f8ca [file] [log] [blame]
scroggo@google.com3cb969f2012-07-27 20:39:19 +00001/*
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"
Mike Klein33d20552017-03-22 13:47:51 -04009#include "sk_tool_utils.h"
bungeman61457a62016-07-06 11:55:05 -070010#include "Resources.h"
Ben Wagnere3f1b592018-05-10 15:24:20 -040011#include "SkBlurTypes.h"
scroggo@google.com3cb969f2012-07-27 20:39:19 +000012#include "SkCanvas.h"
Ben Wagner71319502017-07-27 10:45:29 -040013#include "SkFontStyle.h"
Ben Wagnere3f1b592018-05-10 15:24:20 -040014#include "SkMaskFilter.h"
scroggo@google.com3cb969f2012-07-27 20:39:19 +000015#include "SkString.h"
bungeman61457a62016-07-06 11:55:05 -070016#include "SkSurfaceProps.h"
scroggo@google.com3cb969f2012-07-27 20:39:19 +000017#include "SkTypeface.h"
18#include "SkTypes.h"
19
reed@google.com35fe7372013-10-30 15:07:03 +000020static void getGlyphPositions(const SkPaint& paint, const uint16_t glyphs[],
21 int count, SkScalar x, SkScalar y, SkPoint pos[]) {
22 SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
23
24 SkAutoSTMalloc<128, SkScalar> widthStorage(count);
25 SkScalar* widths = widthStorage.get();
26 paint.getTextWidths(glyphs, count * sizeof(uint16_t), widths);
skia.committer@gmail.com7ed98df2013-10-31 07:01:53 +000027
reed@google.com35fe7372013-10-30 15:07:03 +000028 for (int i = 0; i < count; ++i) {
29 pos[i].set(x, y);
30 x += widths[i];
31 }
32}
33
34static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count,
35 const SkPaint& paint) {
36 SkScalar scale = paint.getTextSize() / paint.getTypeface()->getUnitsPerEm();
37
38 SkScalar globalAdj = 0;
39 for (int i = 0; i < count - 1; ++i) {
40 globalAdj += adjustments[i] * scale;
41 pos[i + 1].fX += globalAdj;
42 }
43}
44
45static void drawKernText(SkCanvas* canvas, const void* text, size_t len,
46 SkScalar x, SkScalar y, const SkPaint& paint) {
47 SkTypeface* face = paint.getTypeface();
48 if (!face) {
49 canvas->drawText(text, len, x, y, paint);
50 return;
51 }
52
53 SkAutoSTMalloc<128, uint16_t> glyphStorage(len);
54 uint16_t* glyphs = glyphStorage.get();
55 int glyphCount = paint.textToGlyphs(text, len, glyphs);
56 if (glyphCount < 1) {
57 return;
58 }
59
60 SkAutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1);
61 int32_t* adjustments = adjustmentStorage.get();
62 if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) {
63 canvas->drawText(text, len, x, y, paint);
64 return;
65 }
66
67 SkPaint glyphPaint(paint);
68 glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
69
70 SkAutoSTMalloc<128, SkPoint> posStorage(glyphCount);
71 SkPoint* pos = posStorage.get();
72 getGlyphPositions(glyphPaint, glyphs, glyphCount, x, y, pos);
73
74 applyKerning(pos, adjustments, glyphCount, glyphPaint);
75 canvas->drawPosText(glyphs, glyphCount * sizeof(uint16_t), pos, glyphPaint);
76}
77
Mike Kleincb9fc412017-11-14 10:45:32 -050078static constexpr SkFontStyle gStyles[] = {
79 SkFontStyle::Normal(),
80 SkFontStyle::Bold(),
81 SkFontStyle::Italic(),
82 SkFontStyle::BoldItalic(),
reed@google.com92abe482013-02-26 16:57:16 +000083};
scroggo@google.com3cb969f2012-07-27 20:39:19 +000084
Mike Kleincb9fc412017-11-14 10:45:32 -050085constexpr int gStylesCount = SK_ARRAY_COUNT(gStyles);
reed@google.com92abe482013-02-26 16:57:16 +000086
87class TypefaceStylesGM : public skiagm::GM {
Mike Kleincb9fc412017-11-14 10:45:32 -050088 sk_sp<SkTypeface> fFaces[gStylesCount];
reed@google.com35fe7372013-10-30 15:07:03 +000089 bool fApplyKerning;
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +000090
reed@google.com92abe482013-02-26 16:57:16 +000091public:
kkinnunenb4a797f2015-05-21 06:15:28 -070092 TypefaceStylesGM(bool applyKerning)
93 : fApplyKerning(applyKerning) {
94 memset(fFaces, 0, sizeof(fFaces));
reed@google.com92abe482013-02-26 16:57:16 +000095 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +000096
reed@google.com92abe482013-02-26 16:57:16 +000097protected:
kkinnunenb4a797f2015-05-21 06:15:28 -070098 void onOnceBeforeDraw() override {
Mike Kleincb9fc412017-11-14 10:45:32 -050099 for (int i = 0; i < gStylesCount; i++) {
100 fFaces[i] = SkTypeface::MakeFromName(nullptr, gStyles[i]);
kkinnunenb4a797f2015-05-21 06:15:28 -0700101 }
102 }
103
mtklein36352bf2015-03-25 18:17:31 -0700104 SkString onShortName() override {
reed@google.com35fe7372013-10-30 15:07:03 +0000105 SkString name("typefacestyles");
106 if (fApplyKerning) {
107 name.append("_kerning");
108 }
Mike Kleinb251b722017-11-13 11:03:16 -0500109 name.append(sk_tool_utils::platform_font_manager());
reed@google.com35fe7372013-10-30 15:07:03 +0000110 return name;
reed@google.com92abe482013-02-26 16:57:16 +0000111 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000112
mtklein36352bf2015-03-25 18:17:31 -0700113 SkISize onISize() override {
reed@google.com92abe482013-02-26 16:57:16 +0000114 return SkISize::Make(640, 480);
115 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000116
mtklein36352bf2015-03-25 18:17:31 -0700117 void onDraw(SkCanvas* canvas) override {
reed@google.com92abe482013-02-26 16:57:16 +0000118 SkPaint paint;
119 paint.setAntiAlias(true);
120 paint.setTextSize(SkIntToScalar(30));
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000121
reed@google.com35fe7372013-10-30 15:07:03 +0000122 const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons";
reed@google.com92abe482013-02-26 16:57:16 +0000123 const size_t textLen = strlen(text);
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000124
reed@google.com92abe482013-02-26 16:57:16 +0000125 SkScalar x = SkIntToScalar(10);
halcanary96fcdcc2015-08-27 07:41:13 -0700126 SkScalar dy = paint.getFontMetrics(nullptr);
reed@google.com92abe482013-02-26 16:57:16 +0000127 SkScalar y = dy;
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000128
reed@google.com35fe7372013-10-30 15:07:03 +0000129 if (fApplyKerning) {
130 paint.setSubpixelText(true);
131 } else {
132 paint.setLinearText(true);
133 }
Mike Kleincb9fc412017-11-14 10:45:32 -0500134 for (int i = 0; i < gStylesCount; i++) {
reed@google.com92abe482013-02-26 16:57:16 +0000135 paint.setTypeface(fFaces[i]);
136 canvas->drawText(text, textLen, x, y, paint);
reed@google.com35fe7372013-10-30 15:07:03 +0000137 if (fApplyKerning) {
138 drawKernText(canvas, text, textLen, x + 240, y, paint);
139 }
reed@google.com92abe482013-02-26 16:57:16 +0000140 y += dy;
141 }
142 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000143
reed@google.com92abe482013-02-26 16:57:16 +0000144private:
145 typedef skiagm::GM INHERITED;
146};
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000147
halcanary4ecf0d42016-09-20 13:11:01 -0700148DEF_GM( return new TypefaceStylesGM(false); )
149DEF_GM( return new TypefaceStylesGM(true); )
bungeman61457a62016-07-06 11:55:05 -0700150
halcanary4ecf0d42016-09-20 13:11:01 -0700151////////////////////////////////////////////////////////////////////////////////
bungeman61457a62016-07-06 11:55:05 -0700152
halcanary4ecf0d42016-09-20 13:11:01 -0700153static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face,
154 char character = 'A') {
Ben Wagnere3f1b592018-05-10 15:24:20 -0400155 struct AliasType {
156 bool antiAlias;
157 bool subpixelAntitalias;
158 bool inLayer;
159 } constexpr aliasTypes[] {
bungemanf382b482016-07-13 14:00:39 -0700160#ifndef SK_BUILD_FOR_IOS
Ben Wagnere3f1b592018-05-10 15:24:20 -0400161 // This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering.
162 // The crash looks like
163 // libTrueTypeScaler.dylib`<redacted> + 80
164 // stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...)
165 // -> 0x330b19d0 <+80>: strd r2, r3, [r5, #36]
166 // 0x330b19d4 <+84>: movs r3, #0x0
167 // 0x330b19d6 <+86>: add r2, sp, #0x28
168 // 0x330b19d8 <+88>: ldr r0, [r4, #0x4]
169 // Disable testing embedded bitmaps on iOS for now.
170 // See https://bug.skia.org/5530 .
171 { false, false, false }, // aliased
bungemanf382b482016-07-13 14:00:39 -0700172#endif
Ben Wagnere3f1b592018-05-10 15:24:20 -0400173 { true, false, false }, // anti-aliased
174 { true, true , false }, // subpixel anti-aliased
175 { true, false, true }, // anti-aliased in layer (flat pixel geometry)
176 { true, true , true }, // subpixel anti-aliased in layer (flat pixel geometry)
177 };
bungeman61457a62016-07-06 11:55:05 -0700178
Ben Wagnere3f1b592018-05-10 15:24:20 -0400179 // The hintgasp.ttf is designed for the following sizes to be different.
180 // GASP_DOGRAY 0x0002 0<=ppem<=10
181 // GASP_SYMMETRIC_SMOOTHING 0x0008 0<=ppem<=10
182 // GASP_GRIDFIT 0x0001 11<=ppem<=12
183 // GASP_SYMMETRIC_GRIDFIT 0x0004 11<=ppem<=12
184 // GASP_DOGRAY|GASP_GRIDFIT 0x0003 13<=ppem<=14
185 // GASP_SYMMETRIC_SMOOTHING|GASP_SYMMETRIC_GRIDFIT 0x000C 13<=ppem<=14
186 // (neither) 0x0000 15<=ppem
187 // Odd sizes have embedded bitmaps.
188 constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 };
bungeman61457a62016-07-06 11:55:05 -0700189
Ben Wagnere3f1b592018-05-10 15:24:20 -0400190 constexpr SkPaint::Hinting hintingTypes[] = { SkPaint::kNo_Hinting,
191 SkPaint::kSlight_Hinting,
192 SkPaint::kNormal_Hinting,
193 SkPaint::kFull_Hinting };
bungeman61457a62016-07-06 11:55:05 -0700194
Ben Wagnere3f1b592018-05-10 15:24:20 -0400195 struct SubpixelType {
196 bool requested;
197 SkVector offset;
198 } constexpr subpixelTypes[] = {
199 { false, { 0.00, 0.00 } },
200 { true , { 0.00, 0.00 } },
201 { true , { 0.25, 0.00 } },
202 { true , { 0.25, 0.25 } },
203 };
bungeman61457a62016-07-06 11:55:05 -0700204
Ben Wagnere3f1b592018-05-10 15:24:20 -0400205 constexpr bool rotateABitTypes[] = { false, true };
bungeman61457a62016-07-06 11:55:05 -0700206
Ben Wagnere3f1b592018-05-10 15:24:20 -0400207 SkScalar y = 0; // The baseline of the previous output
208 {
bungeman61457a62016-07-06 11:55:05 -0700209 SkPaint paint;
halcanary4ecf0d42016-09-20 13:11:01 -0700210 paint.setTypeface(face);
bungeman5dba3012016-07-12 06:55:25 -0700211 paint.setEmbeddedBitmapText(true);
212
bungeman61457a62016-07-06 11:55:05 -0700213 SkScalar x = 0;
214 SkScalar xMax = x;
215 SkScalar xBase = 0;
bungeman61457a62016-07-06 11:55:05 -0700216 for (const SubpixelType subpixel : subpixelTypes) {
217 y = 0;
218 paint.setSubpixelText(subpixel.requested);
219
220 for (const AliasType& alias : aliasTypes) {
221 paint.setAntiAlias(alias.antiAlias);
222 paint.setLCDRenderText(alias.subpixelAntitalias);
223 SkAutoCanvasRestore acr(canvas, false);
224 if (alias.inLayer) {
225 canvas->saveLayer(nullptr, &paint);
226 }
227
228 for (const SkScalar& textSize : textSizes) {
229 x = xBase + 5;
230 paint.setTextSize(textSize);
231
232 SkScalar dy = SkScalarCeilToScalar(paint.getFontMetrics(nullptr));
233 y += dy;
234 for (const SkPaint::Hinting& hinting : hintingTypes) {
235 paint.setHinting(hinting);
236
237 for (const bool& rotateABit : rotateABitTypes) {
238 SkAutoCanvasRestore acr(canvas, true);
239 if (rotateABit) {
bungeman7438bfc2016-07-12 15:01:19 -0700240 canvas->rotate(2, x + subpixel.offset.x(),
241 y + subpixel.offset.y());
bungeman61457a62016-07-06 11:55:05 -0700242 }
Ben Wagnere3f1b592018-05-10 15:24:20 -0400243 canvas->drawText(&character, 1, x + subpixel.offset.x(),
244 y + subpixel.offset.y(), paint);
bungeman61457a62016-07-06 11:55:05 -0700245
halcanary4ecf0d42016-09-20 13:11:01 -0700246 SkScalar dx = SkScalarCeilToScalar(
247 paint.measureText(&character, 1)) + 5;
bungeman61457a62016-07-06 11:55:05 -0700248 x += dx;
249 xMax = SkTMax(x, xMax);
250 }
251 }
252 }
253 y += 10;
254 }
255 xBase = xMax;
256 }
Ben Wagnere3f1b592018-05-10 15:24:20 -0400257 }
258
259 constexpr struct StyleTests {
260 SkPaint::Style style;
261 SkScalar strokeWidth;
262 } styleTypes[] = {
263 { SkPaint::kFill_Style, 0.0f},
264 { SkPaint::kStroke_Style, 0.0f},
265 { SkPaint::kStroke_Style, 0.5f},
266 { SkPaint::kStrokeAndFill_Style, 1.0f},
267 };
268
269 constexpr bool fakeBoldTypes[] = { false, true };
270
271 {
272 SkPaint paint;
273 paint.setTypeface(face);
274 paint.setTextSize(16);
275
276 SkScalar x = 0;
277 for (const bool& fakeBold : fakeBoldTypes) {
278 SkScalar dy = SkScalarCeilToScalar(paint.getFontMetrics(nullptr));
279 y += dy;
280 x = 5;
281
282 paint.setFakeBoldText(fakeBold);
283 for (const AliasType& alias : aliasTypes) {
284 paint.setAntiAlias(alias.antiAlias);
285 paint.setLCDRenderText(alias.subpixelAntitalias);
286 SkAutoCanvasRestore acr(canvas, false);
287 if (alias.inLayer) {
288 canvas->saveLayer(nullptr, &paint);
289 }
290 for (const StyleTests& style : styleTypes) {
291 paint.setStyle(style.style);
292 paint.setStrokeWidth(style.strokeWidth);
293 canvas->drawText(&character, 1, x, y, paint);
294
295 SkScalar dx = SkScalarCeilToScalar(paint.measureText(&character, 1)) + 5;
296 x += dx;
297 }
298 }
299 y += 10;
300 }
301 }
302
303 constexpr struct MaskTests {
304 SkBlurStyle style;
305 SkScalar sigma;
306 } maskTypes[] = {
307 { SkBlurStyle::kNormal_SkBlurStyle, 0.0f},
308 { SkBlurStyle::kSolid_SkBlurStyle, 0.0f},
309 { SkBlurStyle::kOuter_SkBlurStyle, 0.0f},
310 { SkBlurStyle::kInner_SkBlurStyle, 0.0f},
311
312 { SkBlurStyle::kNormal_SkBlurStyle, 0.5f},
313 { SkBlurStyle::kSolid_SkBlurStyle, 0.5f},
314 { SkBlurStyle::kOuter_SkBlurStyle, 0.5f},
315 { SkBlurStyle::kInner_SkBlurStyle, 0.5f},
316
317 { SkBlurStyle::kNormal_SkBlurStyle, 2.0f},
318 { SkBlurStyle::kSolid_SkBlurStyle, 2.0f},
319 { SkBlurStyle::kOuter_SkBlurStyle, 2.0f},
320 { SkBlurStyle::kInner_SkBlurStyle, 2.0f},
321 };
322
323 {
324 SkPaint paint;
325 paint.setTypeface(face);
326 paint.setTextSize(16);
327
328 SkScalar x = 0;
329 {
330 for (const AliasType& alias : aliasTypes) {
331 SkScalar dy = SkScalarCeilToScalar(paint.getFontMetrics(nullptr));
332 y += dy;
333 x = 5;
334
335 paint.setAntiAlias(alias.antiAlias);
336 paint.setLCDRenderText(alias.subpixelAntitalias);
337 SkAutoCanvasRestore acr(canvas, false);
338 if (alias.inLayer) {
339 canvas->saveLayer(nullptr, &paint);
340 }
341 for (const MaskTests& mask : maskTypes) {
342 paint.setMaskFilter(SkMaskFilter::MakeBlur(mask.style, mask.sigma));
343 canvas->drawText(&character, 1, x, y, paint);
344
345 SkScalar dx = SkScalarCeilToScalar(paint.measureText(&character, 1)) + 5;
346 x += dx;
347 }
348 paint.setMaskFilter(nullptr);
349 }
350 y += 10;
351 }
352 }
halcanary4ecf0d42016-09-20 13:11:01 -0700353}
354
Ben Wagnere3f1b592018-05-10 15:24:20 -0400355DEF_SIMPLE_GM_BG_NAME(typefacerendering, canvas, 640, 840, SK_ColorWHITE,
halcanary4ecf0d42016-09-20 13:11:01 -0700356 SkStringPrintf("typefacerendering%s",
Mike Kleinb251b722017-11-13 11:03:16 -0500357 sk_tool_utils::platform_font_manager())) {
Hal Canary53e5e7d2017-12-08 14:25:14 -0500358 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/hintgasp.ttf")) {
halcanary4ecf0d42016-09-20 13:11:01 -0700359 draw_typeface_rendering_gm(canvas, std::move(face));
bungeman61457a62016-07-06 11:55:05 -0700360 }
halcanary4ecf0d42016-09-20 13:11:01 -0700361}
bungeman61457a62016-07-06 11:55:05 -0700362
halcanary4ecf0d42016-09-20 13:11:01 -0700363// Type1 fonts don't currently work in Skia on Windows.
364#ifndef SK_BUILD_FOR_WIN
bungeman61457a62016-07-06 11:55:05 -0700365
Ben Wagnere3f1b592018-05-10 15:24:20 -0400366DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfa, canvas, 640, 840, SK_ColorWHITE,
halcanary4ecf0d42016-09-20 13:11:01 -0700367 SkStringPrintf("typefacerendering_pfa%s",
Mike Kleinb251b722017-11-13 11:03:16 -0500368 sk_tool_utils::platform_font_manager())) {
halcanary4ecf0d42016-09-20 13:11:01 -0700369 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfa")) {
370 // This subsetted typeface doesn't have the character 'A'.
371 draw_typeface_rendering_gm(canvas, std::move(face), 'O');
372 }
373}
reed@google.com92abe482013-02-26 16:57:16 +0000374
Ben Wagnere3f1b592018-05-10 15:24:20 -0400375DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfb, canvas, 640, 840, SK_ColorWHITE,
halcanary4ecf0d42016-09-20 13:11:01 -0700376 SkStringPrintf("typefacerendering_pfb%s",
Mike Kleinb251b722017-11-13 11:03:16 -0500377 sk_tool_utils::platform_font_manager())) {
halcanary4ecf0d42016-09-20 13:11:01 -0700378 if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfb")) {
379 draw_typeface_rendering_gm(canvas, std::move(face), 'O');
380 }
381}
382
383#endif