blob: 8303b373582ec7d1bc9b7a31116ee7fdff742141 [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"
bungeman61457a62016-07-06 11:55:05 -07009#include "Resources.h"
scroggo@google.com3cb969f2012-07-27 20:39:19 +000010#include "SkCanvas.h"
11#include "SkString.h"
bungeman61457a62016-07-06 11:55:05 -070012#include "SkSurfaceProps.h"
scroggo@google.com3cb969f2012-07-27 20:39:19 +000013#include "SkTypeface.h"
14#include "SkTypes.h"
15
reed@google.com35fe7372013-10-30 15:07:03 +000016static void getGlyphPositions(const SkPaint& paint, const uint16_t glyphs[],
17 int count, SkScalar x, SkScalar y, SkPoint pos[]) {
18 SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
19
20 SkAutoSTMalloc<128, SkScalar> widthStorage(count);
21 SkScalar* widths = widthStorage.get();
22 paint.getTextWidths(glyphs, count * sizeof(uint16_t), widths);
skia.committer@gmail.com7ed98df2013-10-31 07:01:53 +000023
reed@google.com35fe7372013-10-30 15:07:03 +000024 for (int i = 0; i < count; ++i) {
25 pos[i].set(x, y);
26 x += widths[i];
27 }
28}
29
30static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count,
31 const SkPaint& paint) {
32 SkScalar scale = paint.getTextSize() / paint.getTypeface()->getUnitsPerEm();
33
34 SkScalar globalAdj = 0;
35 for (int i = 0; i < count - 1; ++i) {
36 globalAdj += adjustments[i] * scale;
37 pos[i + 1].fX += globalAdj;
38 }
39}
40
41static void drawKernText(SkCanvas* canvas, const void* text, size_t len,
42 SkScalar x, SkScalar y, const SkPaint& paint) {
43 SkTypeface* face = paint.getTypeface();
44 if (!face) {
45 canvas->drawText(text, len, x, y, paint);
46 return;
47 }
48
49 SkAutoSTMalloc<128, uint16_t> glyphStorage(len);
50 uint16_t* glyphs = glyphStorage.get();
51 int glyphCount = paint.textToGlyphs(text, len, glyphs);
52 if (glyphCount < 1) {
53 return;
54 }
55
56 SkAutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1);
57 int32_t* adjustments = adjustmentStorage.get();
58 if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) {
59 canvas->drawText(text, len, x, y, paint);
60 return;
61 }
62
63 SkPaint glyphPaint(paint);
64 glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
65
66 SkAutoSTMalloc<128, SkPoint> posStorage(glyphCount);
67 SkPoint* pos = posStorage.get();
68 getGlyphPositions(glyphPaint, glyphs, glyphCount, x, y, pos);
69
70 applyKerning(pos, adjustments, glyphCount, glyphPaint);
71 canvas->drawPosText(glyphs, glyphCount * sizeof(uint16_t), pos, glyphPaint);
72}
73
mtkleindbfd7ab2016-09-01 11:24:54 -070074constexpr struct {
reed@google.com92abe482013-02-26 16:57:16 +000075 const char* fName;
76 SkTypeface::Style fStyle;
77} gFaceStyles[] = {
78 { "sans-serif", SkTypeface::kNormal },
79 { "sans-serif", SkTypeface::kBold },
80 { "sans-serif", SkTypeface::kItalic },
81 { "sans-serif", SkTypeface::kBoldItalic },
82 { "serif", SkTypeface::kNormal },
83 { "serif", SkTypeface::kBold },
84 { "serif", SkTypeface::kItalic },
85 { "serif", SkTypeface::kBoldItalic },
86 { "monospace", SkTypeface::kNormal },
87 { "monospace", SkTypeface::kBold },
88 { "monospace", SkTypeface::kItalic },
89 { "monospace", SkTypeface::kBoldItalic },
90};
scroggo@google.com3cb969f2012-07-27 20:39:19 +000091
mtkleindbfd7ab2016-09-01 11:24:54 -070092constexpr int gFaceStylesCount = SK_ARRAY_COUNT(gFaceStyles);
reed@google.com92abe482013-02-26 16:57:16 +000093
94class TypefaceStylesGM : public skiagm::GM {
bungeman13b9c952016-05-12 10:09:30 -070095 sk_sp<SkTypeface> fFaces[gFaceStylesCount];
reed@google.com35fe7372013-10-30 15:07:03 +000096 bool fApplyKerning;
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +000097
reed@google.com92abe482013-02-26 16:57:16 +000098public:
kkinnunenb4a797f2015-05-21 06:15:28 -070099 TypefaceStylesGM(bool applyKerning)
100 : fApplyKerning(applyKerning) {
101 memset(fFaces, 0, sizeof(fFaces));
reed@google.com92abe482013-02-26 16:57:16 +0000102 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000103
reed@google.com92abe482013-02-26 16:57:16 +0000104protected:
kkinnunenb4a797f2015-05-21 06:15:28 -0700105 void onOnceBeforeDraw() override {
106 for (int i = 0; i < gFaceStylesCount; i++) {
bungeman13b9c952016-05-12 10:09:30 -0700107 fFaces[i] = SkTypeface::MakeFromName(
mbocee6a9912016-05-31 11:42:36 -0700108 sk_tool_utils::platform_font_name(
109 gFaceStyles[i].fName), SkFontStyle::FromOldStyle(gFaceStyles[i].fStyle));
kkinnunenb4a797f2015-05-21 06:15:28 -0700110 }
111 }
112
mtklein36352bf2015-03-25 18:17:31 -0700113 SkString onShortName() override {
reed@google.com35fe7372013-10-30 15:07:03 +0000114 SkString name("typefacestyles");
115 if (fApplyKerning) {
116 name.append("_kerning");
117 }
caryclark37213552015-07-24 11:08:01 -0700118 name.append(sk_tool_utils::major_platform_os_name());
reed@google.com35fe7372013-10-30 15:07:03 +0000119 return name;
reed@google.com92abe482013-02-26 16:57:16 +0000120 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000121
mtklein36352bf2015-03-25 18:17:31 -0700122 SkISize onISize() override {
reed@google.com92abe482013-02-26 16:57:16 +0000123 return SkISize::Make(640, 480);
124 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000125
mtklein36352bf2015-03-25 18:17:31 -0700126 void onDraw(SkCanvas* canvas) override {
reed@google.com92abe482013-02-26 16:57:16 +0000127 SkPaint paint;
128 paint.setAntiAlias(true);
129 paint.setTextSize(SkIntToScalar(30));
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000130
reed@google.com35fe7372013-10-30 15:07:03 +0000131 const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons";
reed@google.com92abe482013-02-26 16:57:16 +0000132 const size_t textLen = strlen(text);
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000133
reed@google.com92abe482013-02-26 16:57:16 +0000134 SkScalar x = SkIntToScalar(10);
halcanary96fcdcc2015-08-27 07:41:13 -0700135 SkScalar dy = paint.getFontMetrics(nullptr);
reed@google.com92abe482013-02-26 16:57:16 +0000136 SkScalar y = dy;
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000137
reed@google.com35fe7372013-10-30 15:07:03 +0000138 if (fApplyKerning) {
139 paint.setSubpixelText(true);
140 } else {
141 paint.setLinearText(true);
142 }
reed@google.com92abe482013-02-26 16:57:16 +0000143 for (int i = 0; i < gFaceStylesCount; i++) {
144 paint.setTypeface(fFaces[i]);
145 canvas->drawText(text, textLen, x, y, paint);
reed@google.com35fe7372013-10-30 15:07:03 +0000146 if (fApplyKerning) {
147 drawKernText(canvas, text, textLen, x + 240, y, paint);
148 }
reed@google.com92abe482013-02-26 16:57:16 +0000149 y += dy;
150 }
151 }
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000152
reed@google.com92abe482013-02-26 16:57:16 +0000153private:
154 typedef skiagm::GM INHERITED;
155};
skia.committer@gmail.com12eea2b2013-02-27 07:10:10 +0000156
bungeman61457a62016-07-06 11:55:05 -0700157class TypefaceRenderingGM : public skiagm::GM {
158 sk_sp<SkTypeface> fFace;
159
bungeman61457a62016-07-06 11:55:05 -0700160protected:
161 void onOnceBeforeDraw() override {
162 fFace = MakeResourceAsTypeface("/fonts/hintgasp.ttf");
163 }
164
165 SkString onShortName() override {
166 SkString name("typefacerendering");
167 name.append(sk_tool_utils::major_platform_os_name());
168 return name;
169 }
170
171 SkISize onISize() override {
bungeman5dba3012016-07-12 06:55:25 -0700172 return SkISize::Make(640, 680);
bungeman61457a62016-07-06 11:55:05 -0700173 }
174
175 void onDraw(SkCanvas* canvas) override {
176 struct AliasType {
177 bool antiAlias;
178 bool subpixelAntitalias;
179 bool inLayer;
180 } constexpr aliasTypes[] {
bungemanf382b482016-07-13 14:00:39 -0700181#ifndef SK_BUILD_FOR_IOS
182 // This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering.
183 // The crash looks like
184 // libTrueTypeScaler.dylib`<redacted> + 80
185 // stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...)
186 // -> 0x330b19d0 <+80>: strd r2, r3, [r5, #36]
187 // 0x330b19d4 <+84>: movs r3, #0x0
188 // 0x330b19d6 <+86>: add r2, sp, #0x28
189 // 0x330b19d8 <+88>: ldr r0, [r4, #0x4]
190 // Disable testing embedded bitmaps on iOS for now.
191 // See https://bug.skia.org/5530 .
bungeman61457a62016-07-06 11:55:05 -0700192 { false, false, false }, // aliased
bungemanf382b482016-07-13 14:00:39 -0700193#endif
bungeman61457a62016-07-06 11:55:05 -0700194 { true, false, false }, // anti-aliased
195 { true, true , false }, // subpixel anti-aliased
196 { true, false, true }, // anti-aliased in layer (flat pixel geometry)
197 { true, true , true }, // subpixel anti-aliased in layer (flat pixel geometry)
198 };
199
200 // The hintgasp.ttf is designed for the following sizes to be different.
201 // GASP_DOGRAY 0x0002 0<=ppem<=10
202 // GASP_SYMMETRIC_SMOOTHING 0x0008 0<=ppem<=10
203 // GASP_GRIDFIT 0x0001 11<=ppem<=12
204 // GASP_SYMMETRIC_GRIDFIT 0x0004 11<=ppem<=12
205 // GASP_DOGRAY|GASP_GRIDFIT 0x0003 13<=ppem<=14
206 // GASP_SYMMETRIC_SMOOTHING|GASP_SYMMETRIC_GRIDFIT 0x000C 13<=ppem<=14
207 // (neither) 0x0000 15<=ppem
bungeman5dba3012016-07-12 06:55:25 -0700208 // Odd sizes have embedded bitmaps.
209 constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 };
bungeman61457a62016-07-06 11:55:05 -0700210
211 constexpr SkPaint::Hinting hintingTypes[] = { SkPaint::kNo_Hinting,
212 SkPaint::kSlight_Hinting,
213 SkPaint::kNormal_Hinting,
214 SkPaint::kFull_Hinting };
215
216 struct SubpixelType {
217 bool requested;
218 SkVector offset;
219 } constexpr subpixelTypes[] = {
220 { false, { 0.00, 0.00 } },
221 { true , { 0.00, 0.00 } },
222 { true , { 0.25, 0.00 } },
223 { true , { 0.25, 0.25 } },
224 };
225
226 constexpr bool rotateABitTypes[] = { false, true };
227
228 SkPaint paint;
bungeman5dba3012016-07-12 06:55:25 -0700229 paint.setTypeface(fFace);
230 paint.setEmbeddedBitmapText(true);
231
bungeman61457a62016-07-06 11:55:05 -0700232 SkScalar x = 0;
233 SkScalar xMax = x;
234 SkScalar xBase = 0;
235 SkScalar y = 0; // The baseline of the previous output
236 for (const SubpixelType subpixel : subpixelTypes) {
237 y = 0;
238 paint.setSubpixelText(subpixel.requested);
239
240 for (const AliasType& alias : aliasTypes) {
241 paint.setAntiAlias(alias.antiAlias);
242 paint.setLCDRenderText(alias.subpixelAntitalias);
243 SkAutoCanvasRestore acr(canvas, false);
244 if (alias.inLayer) {
245 canvas->saveLayer(nullptr, &paint);
246 }
247
248 for (const SkScalar& textSize : textSizes) {
249 x = xBase + 5;
250 paint.setTextSize(textSize);
251
252 SkScalar dy = SkScalarCeilToScalar(paint.getFontMetrics(nullptr));
253 y += dy;
254 for (const SkPaint::Hinting& hinting : hintingTypes) {
255 paint.setHinting(hinting);
256
257 for (const bool& rotateABit : rotateABitTypes) {
258 SkAutoCanvasRestore acr(canvas, true);
259 if (rotateABit) {
bungeman7438bfc2016-07-12 15:01:19 -0700260 canvas->rotate(2, x + subpixel.offset.x(),
261 y + subpixel.offset.y());
bungeman61457a62016-07-06 11:55:05 -0700262 }
263 canvas->drawText("A", 1, x + subpixel.offset.x(),
264 y + subpixel.offset.y(), paint);
265
266 SkScalar dx = SkScalarCeilToScalar(paint.measureText("A", 1)) + 5;
267 x += dx;
268 xMax = SkTMax(x, xMax);
269 }
270 }
271 }
272 y += 10;
273 }
274 xBase = xMax;
275 }
276 }
277
278private:
279 typedef skiagm::GM INHERITED;
280};
281
reed@google.com92abe482013-02-26 16:57:16 +0000282///////////////////////////////////////////////////////////////////////////////
283
reed@google.com35fe7372013-10-30 15:07:03 +0000284DEF_GM( return new TypefaceStylesGM(false); )
285DEF_GM( return new TypefaceStylesGM(true); )
bungeman61457a62016-07-06 11:55:05 -0700286DEF_GM( return new TypefaceRenderingGM(); )