1 /*
2 * Copyright 2011 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 "include/core/SkCanvas.h"
9 #include "include/core/SkColorFilter.h"
10 #include "include/core/SkColorPriv.h"
11 #include "include/core/SkGraphics.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkRegion.h"
14 #include "include/core/SkShader.h"
15 #include "include/core/SkStream.h"
16 #include "include/core/SkTextBlob.h"
17 #include "include/core/SkTypeface.h"
18 #include "include/effects/SkGradientShader.h"
19 #include "modules/skshaper/include/SkShaper.h"
20 #include "modules/skshaper/include/SkShaper_skunicode.h"
21 #include "modules/skunicode/include/SkUnicode.h"
22 #include "src/base/SkRandom.h"
23 #include "src/base/SkTime.h"
24 #include "src/base/SkUTF.h"
25 #include "src/core/SkOSFile.h"
26 #include "tools/fonts/FontToolUtils.h"
27 #include "tools/viewer/Slide.h"
28
29 #if defined(SK_SHAPER_CORETEXT_AVAILABLE)
30 #include "modules/skshaper/include/SkShaper_coretext.h"
31 #endif
32
33 #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE)
34 #include "modules/skshaper/include/SkShaper_harfbuzz.h"
35 #endif
36
37 #if defined(SK_UNICODE_ICU_IMPLEMENTATION)
38 #include "modules/skunicode/include/SkUnicode_icu.h"
39 #endif
40
41 #if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION)
42 #include "modules/skunicode/include/SkUnicode_libgrapheme.h"
43 #endif
44
45 #if defined(SK_UNICODE_ICU4X_IMPLEMENTATION)
46 #include "modules/skunicode/include/SkUnicode_icu4x.h"
47 #endif
48
49 typedef std::unique_ptr<SkShaper> (*ShaperFactory)();
50
51 static const char gText[] =
52 "When in the Course of human events it becomes necessary for one people "
53 "to dissolve the political bands which have connected them with another "
54 "and to assume among the powers of the earth, the separate and equal "
55 "station to which the Laws of Nature and of Nature's God entitle them, "
56 "a decent respect to the opinions of mankind requires that they should "
57 "declare the causes which impel them to the separation.";
58
59 namespace {
get_unicode()60 sk_sp<SkUnicode> get_unicode() {
61 #if defined(SK_UNICODE_ICU_IMPLEMENTATION)
62 if (auto unicode = SkUnicodes::ICU::Make()) {
63 return unicode;
64 }
65 #endif // defined(SK_UNICODE_ICU_IMPLEMENTATION)
66 #if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION)
67 if (auto unicode = SkUnicodes::Libgrapheme::Make()) {
68 return unicode;
69 }
70 #endif
71 #if defined(SK_UNICODE_ICU4X_IMPLEMENTATION)
72 if (auto unicode = SkUnicodes::ICU4X::Make()) {
73 return unicode;
74 }
75 #endif
76 return nullptr;
77 }
78 }
79
80 using MakeBidiIteratorCallback = std::unique_ptr<SkShaper::BiDiRunIterator> (*)(sk_sp<SkUnicode> unicode,
81 const char* utf8,
82 size_t utf8Bytes,
83 uint8_t bidiLevel);
84 using MakeScriptRunCallback = std::unique_ptr<SkShaper::ScriptRunIterator> (*)(
85 const char* utf8, size_t utf8Bytes, SkFourByteTag script);
86
make_trivial_bidi(sk_sp<SkUnicode>,const char *,size_t utf8Bytes,uint8_t bidiLevel)87 static std::unique_ptr<SkShaper::BiDiRunIterator> make_trivial_bidi(sk_sp<SkUnicode>,
88 const char*,
89 size_t utf8Bytes,
90 uint8_t bidiLevel) {
91 return std::make_unique<SkShaper::TrivialBiDiRunIterator>(bidiLevel, utf8Bytes);
92 }
93
94 #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_SHAPER_UNICODE_AVAILABLE)
make_unicode_bidi(sk_sp<SkUnicode> unicode,const char * utf8,size_t utf8Bytes,uint8_t bidiLevel)95 static std::unique_ptr<SkShaper::BiDiRunIterator> make_unicode_bidi(sk_sp<SkUnicode> unicode,
96 const char* utf8,
97 size_t utf8Bytes,
98 uint8_t bidiLevel) {
99 if (auto bidi = SkShapers::unicode::BidiRunIterator(unicode, utf8, utf8Bytes, bidiLevel)) {
100 return bidi;
101 }
102 return make_trivial_bidi(unicode, utf8, utf8Bytes, bidiLevel);
103 }
104 #endif
105
make_trivial_script_runner(const char *,size_t utf8Bytes,SkFourByteTag scriptTag)106 static std::unique_ptr<SkShaper::ScriptRunIterator> make_trivial_script_runner(
107 const char*, size_t utf8Bytes, SkFourByteTag scriptTag) {
108 return std::make_unique<SkShaper::TrivialScriptRunIterator>(scriptTag, utf8Bytes);
109 }
110
111 #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_SHAPER_UNICODE_AVAILABLE)
make_harfbuzz_script_runner(const char * utf8,size_t utf8Bytes,SkFourByteTag scriptTag)112 static std::unique_ptr<SkShaper::ScriptRunIterator> make_harfbuzz_script_runner(
113 const char* utf8, size_t utf8Bytes, SkFourByteTag scriptTag) {
114 std::unique_ptr<SkShaper::ScriptRunIterator> script =
115 SkShapers::HB::ScriptRunIterator(utf8, utf8Bytes, scriptTag);
116 if (script) {
117 return script;
118 }
119 return std::make_unique<SkShaper::TrivialScriptRunIterator>(scriptTag, utf8Bytes);
120 }
121 #endif
122
123 class TextBoxSlide : public Slide {
124 public:
TextBoxSlide(ShaperFactory fact,MakeBidiIteratorCallback bidi,MakeScriptRunCallback script,const char suffix[])125 TextBoxSlide(ShaperFactory fact,
126 MakeBidiIteratorCallback bidi,
127 MakeScriptRunCallback script,
128 const char suffix[])
129 : fShaper(fact()), fBidiCallback(bidi), fScriptRunCallback(script) {
130 fName = SkStringPrintf("TextBox_%s", suffix);
131 }
132
load(SkScalar w,SkScalar h)133 void load(SkScalar w, SkScalar h) override { fSize = {w, h}; }
134
resize(SkScalar w,SkScalar h)135 void resize(SkScalar w, SkScalar h) override { fSize = {w, h}; }
136
draw(SkCanvas * canvas)137 void draw(SkCanvas* canvas) override {
138 SkScalar width = fSize.width() / 3;
139 drawTest(canvas, width, fSize.height(), SK_ColorBLACK, SK_ColorWHITE);
140 canvas->translate(width, 0);
141 drawTest(canvas, width, fSize.height(), SK_ColorWHITE, SK_ColorBLACK);
142 canvas->translate(width, 0);
143 drawTest(canvas, width, fSize.height()/2, SK_ColorGRAY, SK_ColorWHITE);
144 canvas->translate(0, fSize.height()/2);
145 drawTest(canvas, width, fSize.height()/2, SK_ColorGRAY, SK_ColorBLACK);
146 }
147
148 private:
drawTest(SkCanvas * canvas,SkScalar w,SkScalar h,SkColor fg,SkColor bg)149 void drawTest(SkCanvas* canvas, SkScalar w, SkScalar h, SkColor fg, SkColor bg) {
150 SkAutoCanvasRestore acr(canvas, true);
151
152 canvas->clipRect(SkRect::MakeWH(w, h));
153 canvas->drawColor(bg);
154
155 SkScalar margin = 20;
156
157 SkPaint paint;
158 paint.setColor(fg);
159
160 for (int i = 9; i < 24; i += 2) {
161 #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_SHAPER_UNICODE_AVAILABLE)
162 SkShapers::HB::PurgeCaches();
163 #endif
164 SkTextBlobBuilderRunHandler builder(gText, { margin, margin });
165 SkFont srcFont(nullptr, SkIntToScalar(i));
166 srcFont.setEdging(SkFont::Edging::kSubpixelAntiAlias);
167 srcFont.setSubpixel(true);
168
169 const char* utf8 = gText;
170 size_t utf8Bytes = sizeof(gText) - 1;
171
172 auto unicode = get_unicode();
173 std::unique_ptr<SkShaper::BiDiRunIterator> bidi =
174 fBidiCallback(unicode, utf8, utf8Bytes, 0xfe);
175 if (!bidi) {
176 return;
177 }
178
179 std::unique_ptr<SkShaper::LanguageRunIterator> language(
180 SkShaper::MakeStdLanguageRunIterator(utf8, utf8Bytes));
181 if (!language) {
182 return;
183 }
184
185 SkFourByteTag undeterminedScript = SkSetFourByteTag('Z','y','y','y');
186 std::unique_ptr<SkShaper::ScriptRunIterator> script =
187 fScriptRunCallback(utf8, utf8Bytes, undeterminedScript);
188 if (!script) {
189 return;
190 }
191
192 std::unique_ptr<SkShaper::FontRunIterator> font(
193 SkShaper::MakeFontMgrRunIterator(utf8,
194 utf8Bytes,
195 srcFont,
196 ToolUtils::TestFontMgr(),
197 "Arial",
198 SkFontStyle::Bold(),
199 &*language));
200 if (!font) {
201 return;
202 }
203
204 fShaper->shape(utf8,
205 utf8Bytes,
206 *font,
207 *bidi,
208 *script,
209 *language,
210 nullptr,
211 0,
212 w - margin,
213 &builder);
214 canvas->drawTextBlob(builder.makeBlob(), 0, 0, paint);
215
216 canvas->translate(0, builder.endPoint().y());
217 }
218 }
219
220 SkSize fSize;
221 std::unique_ptr<SkShaper> fShaper;
222 MakeBidiIteratorCallback fBidiCallback;
223 MakeScriptRunCallback fScriptRunCallback;
224 };
225
226 DEF_SLIDE(return new TextBoxSlide(SkShapers::Primitive::PrimitiveText,
227 make_trivial_bidi,
228 make_trivial_script_runner,
229 "primitive"););
230
231 #if defined(SK_SHAPER_CORETEXT_AVAILABLE)
232 DEF_SLIDE(return new TextBoxSlide(SkShapers::CT::CoreText,
233 make_trivial_bidi,
234 make_trivial_script_runner,
235 "coretext"););
236 #endif
237
238 #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_SHAPER_UNICODE_AVAILABLE)
239 DEF_SLIDE(return new TextBoxSlide(
__anoncc9255cf0202() 240 []() {
241 return SkShapers::HB::ShaperDrivenWrapper(get_unicode(),
242 SkFontMgr::RefEmpty());
243 },
244 make_unicode_bidi,
245 make_harfbuzz_script_runner,
246 "harfbuzz"););
247 #endif
248
249 class ShaperSlide : public Slide {
250 public:
ShaperSlide()251 ShaperSlide() { fName = "shaper"; }
252
draw(SkCanvas * canvas)253 void draw(SkCanvas* canvas) override {
254 canvas->translate(10, 30);
255
256 const char text[] = "world";
257
258 for (SkScalar size = 30; size <= 30; size += 10) {
259 this->drawTest(canvas,
260 text,
261 size,
262 SkShapers::Primitive::PrimitiveText(),
263 make_trivial_bidi,
264 make_trivial_script_runner);
265 canvas->translate(0, size + 5);
266 #if defined(SK_SHAPER_CORETEXT_AVAILABLE)
267 this->drawTest(canvas,
268 text,
269 size,
270 SkShapers::CT::CoreText(),
271 make_trivial_bidi,
272 make_trivial_script_runner);
273 #endif
274 canvas->translate(0, size + 5);
275 #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_SHAPER_UNICODE_AVAILABLE)
276 auto unicode = get_unicode();
277 this->drawTest(
278 canvas,
279 text,
280 size,
281 SkShapers::HB::ShaperDrivenWrapper(unicode, SkFontMgr::RefEmpty()),
282 make_unicode_bidi,
283 make_harfbuzz_script_runner);
284 #endif
285 canvas->translate(0, size*2);
286 }
287 }
288
289 private:
drawTest(SkCanvas * canvas,const char str[],SkScalar size,std::unique_ptr<SkShaper> shaper,MakeBidiIteratorCallback bidiCallback,MakeScriptRunCallback scriptRunCallback)290 void drawTest(SkCanvas* canvas,
291 const char str[],
292 SkScalar size,
293 std::unique_ptr<SkShaper> shaper,
294 MakeBidiIteratorCallback bidiCallback,
295 MakeScriptRunCallback scriptRunCallback) {
296 if (!shaper) return;
297
298 SkTextBlobBuilderRunHandler builder(str, {0, 0});
299 SkFont srcFont;
300 srcFont.setSize(size);
301 srcFont.setEdging(SkFont::Edging::kSubpixelAntiAlias);
302 srcFont.setSubpixel(true);
303
304 size_t len = strlen(str);
305
306 auto unicode = get_unicode();
307 std::unique_ptr<SkShaper::BiDiRunIterator> bidi =
308 bidiCallback(unicode, str, len, 0xfe);
309 if (!bidi) {
310 return;
311 }
312
313 std::unique_ptr<SkShaper::LanguageRunIterator> language(
314 SkShaper::MakeStdLanguageRunIterator(str, len));
315 if (!language) {
316 return;
317 }
318
319 SkFourByteTag undeterminedScript = SkSetFourByteTag('Z','y','y','y');
320 std::unique_ptr<SkShaper::ScriptRunIterator> script(
321 scriptRunCallback(str, len, undeterminedScript));
322 if (!script) {
323 return;
324 }
325
326 std::unique_ptr<SkShaper::FontRunIterator> font(
327 SkShaper::MakeFontMgrRunIterator(str,
328 len,
329 srcFont,
330 ToolUtils::TestFontMgr(),
331 "Arial",
332 SkFontStyle::Bold(),
333 &*language));
334 if (!font) {
335 return;
336 }
337
338 shaper->shape(str, len, *font, *bidi, *script, *language, nullptr, 0, 2000, &builder);
339
340 canvas->drawTextBlob(builder.makeBlob(), 0, 0, SkPaint());
341 }
342 };
343
344 DEF_SLIDE( return new ShaperSlide; );
345