xref: /aosp_15_r20/external/skia/tools/viewer/TextBoxSlide.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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