blob: 807b49c8834bf277cf5564866ad0004ef2e186af [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"
scroggo@google.com3cb969f2012-07-27 20:39:19 +000011#include "SkCanvas.h"
12#include "SkString.h"
bungeman61457a62016-07-06 11:55:05 -070013#include "SkSurfaceProps.h"
scroggo@google.com3cb969f2012-07-27 20:39:19 +000014#include "SkTypeface.h"
15#include "SkTypes.h"
16
reed@google.com35fe7372013-10-30 15:07:03 +000017static 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);
skia.committer@gmail.com7ed98df2013-10-31 07:01:53 +000024
reed@google.com35fe7372013-10-30 15:07:03 +000025 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
mtkleindbfd7ab2016-09-01 11:24:54 -070075constexpr struct {
reed@google.com92abe482013-02-26 16:57:16 +000076 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};
scroggo@google.com3cb969f2012-07-27 20:39:19 +000092
mtkleindbfd7ab2016-09-01 11:24:54 -070093constexpr int gFaceStylesCount = SK_ARRAY_COUNT(gFaceStyles);
reed@google.com92abe482013-02-26 16:57:16 +000094
95class TypefaceStylesGM : public skiagm::GM {
bungeman13b9c952016-05-12 10:09:30 -070096 sk_sp<SkTypeface> fFaces[gFaceStylesCount];
reed@google.com35fe7372013-10-30 15:07:03 +000097 bool fApplyKerning;
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +000098
reed@google.com92abe482013-02-26 16:57:16 +000099public:
kkinnunenb4a797f2015-05-21 06:15:28 -0700100 TypefaceStylesGM(bool applyKerning)
101 : fApplyKerning(applyKerning) {
102 memset(fFaces, 0, sizeof(fFaces));
reed@google.com92abe482013-02-26 16:57:16 +0000103 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000104
reed@google.com92abe482013-02-26 16:57:16 +0000105protected:
kkinnunenb4a797f2015-05-21 06:15:28 -0700106 void onOnceBeforeDraw() override {
107 for (int i = 0; i < gFaceStylesCount; i++) {
bungeman13b9c952016-05-12 10:09:30 -0700108 fFaces[i] = SkTypeface::MakeFromName(
mbocee6a9912016-05-31 11:42:36 -0700109 sk_tool_utils::platform_font_name(
110 gFaceStyles[i].fName), SkFontStyle::FromOldStyle(gFaceStyles[i].fStyle));
kkinnunenb4a797f2015-05-21 06:15:28 -0700111 }
112 }
113
mtklein36352bf2015-03-25 18:17:31 -0700114 SkString onShortName() override {
reed@google.com35fe7372013-10-30 15:07:03 +0000115 SkString name("typefacestyles");
116 if (fApplyKerning) {
117 name.append("_kerning");
118 }
caryclark37213552015-07-24 11:08:01 -0700119 name.append(sk_tool_utils::major_platform_os_name());
reed@google.com35fe7372013-10-30 15:07:03 +0000120 return name;
reed@google.com92abe482013-02-26 16:57:16 +0000121 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000122
mtklein36352bf2015-03-25 18:17:31 -0700123 SkISize onISize() override {
reed@google.com92abe482013-02-26 16:57:16 +0000124 return SkISize::Make(640, 480);
125 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000126
mtklein36352bf2015-03-25 18:17:31 -0700127 void onDraw(SkCanvas* canvas) override {
reed@google.com92abe482013-02-26 16:57:16 +0000128 SkPaint paint;
129 paint.setAntiAlias(true);
130 paint.setTextSize(SkIntToScalar(30));
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000131
reed@google.com35fe7372013-10-30 15:07:03 +0000132 const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons";
reed@google.com92abe482013-02-26 16:57:16 +0000133 const size_t textLen = strlen(text);
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000134
reed@google.com92abe482013-02-26 16:57:16 +0000135 SkScalar x = SkIntToScalar(10);
halcanary96fcdcc2015-08-27 07:41:13 -0700136 SkScalar dy = paint.getFontMetrics(nullptr);
reed@google.com92abe482013-02-26 16:57:16 +0000137 SkScalar y = dy;
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000138
reed@google.com35fe7372013-10-30 15:07:03 +0000139 if (fApplyKerning) {
140 paint.setSubpixelText(true);
141 } else {
142 paint.setLinearText(true);
143 }
reed@google.com92abe482013-02-26 16:57:16 +0000144 for (int i = 0; i < gFaceStylesCount; i++) {
145 paint.setTypeface(fFaces[i]);
146 canvas->drawText(text, textLen, x, y, paint);
reed@google.com35fe7372013-10-30 15:07:03 +0000147 if (fApplyKerning) {
148 drawKernText(canvas, text, textLen, x + 240, y, paint);
149 }
reed@google.com92abe482013-02-26 16:57:16 +0000150 y += dy;
151 }
152 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000153
reed@google.com92abe482013-02-26 16:57:16 +0000154private:
155 typedef skiagm::GM INHERITED;
156};
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000157
halcanary4ecf0d42016-09-20 13:11:01 -0700158DEF_GM( return new TypefaceStylesGM(false); )
159DEF_GM( return new TypefaceStylesGM(true); )
bungeman61457a62016-07-06 11:55:05 -0700160
halcanary4ecf0d42016-09-20 13:11:01 -0700161////////////////////////////////////////////////////////////////////////////////
bungeman61457a62016-07-06 11:55:05 -0700162
halcanary4ecf0d42016-09-20 13:11:01 -0700163static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face,
164 char character = 'A') {
bungeman61457a62016-07-06 11:55:05 -0700165 struct AliasType {
166 bool antiAlias;
167 bool subpixelAntitalias;
168 bool inLayer;
169 } constexpr aliasTypes[] {
bungemanf382b482016-07-13 14:00:39 -0700170#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 .
bungeman61457a62016-07-06 11:55:05 -0700181 { false, false, false }, // aliased
bungemanf382b482016-07-13 14:00:39 -0700182#endif
bungeman61457a62016-07-06 11:55:05 -0700183 { 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
bungeman5dba3012016-07-12 06:55:25 -0700197 // Odd sizes have embedded bitmaps.
198 constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 };
bungeman61457a62016-07-06 11:55:05 -0700199
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;
halcanary4ecf0d42016-09-20 13:11:01 -0700218 paint.setTypeface(face);
bungeman5dba3012016-07-12 06:55:25 -0700219 paint.setEmbeddedBitmapText(true);
220
bungeman61457a62016-07-06 11:55:05 -0700221 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) {
bungeman7438bfc2016-07-12 15:01:19 -0700249 canvas->rotate(2, x + subpixel.offset.x(),
250 y + subpixel.offset.y());
bungeman61457a62016-07-06 11:55:05 -0700251 }
halcanary4ecf0d42016-09-20 13:11:01 -0700252 canvas->drawText(&character, 1,
253 x + subpixel.offset.x(),
254 y + subpixel.offset.y(), paint);
bungeman61457a62016-07-06 11:55:05 -0700255
halcanary4ecf0d42016-09-20 13:11:01 -0700256 SkScalar dx = SkScalarCeilToScalar(
257 paint.measureText(&character, 1)) + 5;
bungeman61457a62016-07-06 11:55:05 -0700258 x += dx;
259 xMax = SkTMax(x, xMax);
260 }
261 }
262 }
263 y += 10;
264 }
265 xBase = xMax;
266 }
halcanary4ecf0d42016-09-20 13:11:01 -0700267}
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));
bungeman61457a62016-07-06 11:55:05 -0700274 }
halcanary4ecf0d42016-09-20 13:11:01 -0700275}
bungeman61457a62016-07-06 11:55:05 -0700276
halcanary4ecf0d42016-09-20 13:11:01 -0700277// Type1 fonts don't currently work in Skia on Windows.
278#ifndef SK_BUILD_FOR_WIN
bungeman61457a62016-07-06 11:55:05 -0700279
halcanary4ecf0d42016-09-20 13:11:01 -0700280DEF_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}
reed@google.com92abe482013-02-26 16:57:16 +0000288
halcanary4ecf0d42016-09-20 13:11:01 -0700289DEF_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