Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkShaper_harfbuzz.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2016 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
9
10#include "include/core/SkData.h"
11#include "include/core/SkFont.h"
18#include "include/core/SkRect.h"
21#include "include/core/SkSpan.h"
35#include "src/base/SkTDPQueue.h"
36#include "src/base/SkUTF.h"
37#include "src/core/SkLRUCache.h"
38
39#if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
41#endif
42
43#include <hb-ot.h>
44#include <hb.h>
45
46#include <cstdint>
47#include <cstring>
48#include <memory>
49#include <type_traits>
50#include <utility>
51
52using namespace skia_private;
53
54// HB_FEATURE_GLOBAL_START and HB_FEATURE_GLOBAL_END were not added until HarfBuzz 2.0
55// They would have always worked, they just hadn't been named yet.
56#if !defined(HB_FEATURE_GLOBAL_START)
57# define HB_FEATURE_GLOBAL_START 0
58#endif
59#if !defined(HB_FEATURE_GLOBAL_END)
60# define HB_FEATURE_GLOBAL_END ((unsigned int) -1)
61#endif
62
63namespace {
64using HBBlob = std::unique_ptr<hb_blob_t , SkFunctionObject<hb_blob_destroy> >;
65using HBFace = std::unique_ptr<hb_face_t , SkFunctionObject<hb_face_destroy> >;
66using HBFont = std::unique_ptr<hb_font_t , SkFunctionObject<hb_font_destroy> >;
67using HBBuffer = std::unique_ptr<hb_buffer_t, SkFunctionObject<hb_buffer_destroy>>;
68
69using SkUnicodeBreak = std::unique_ptr<SkBreakIterator>;
70
71hb_position_t skhb_position(SkScalar value) {
72 // Treat HarfBuzz hb_position_t as 16.16 fixed-point.
73 constexpr int kHbPosition1 = 1 << 16;
74 return SkScalarRoundToInt(value * kHbPosition1);
75}
76
77hb_bool_t skhb_glyph(hb_font_t* hb_font,
78 void* font_data,
79 hb_codepoint_t unicode,
80 hb_codepoint_t variation_selector,
81 hb_codepoint_t* glyph,
82 void* user_data) {
83 SkFont& font = *reinterpret_cast<SkFont*>(font_data);
84
85 *glyph = font.unicharToGlyph(unicode);
86 return *glyph != 0;
87}
88
89hb_bool_t skhb_nominal_glyph(hb_font_t* hb_font,
90 void* font_data,
91 hb_codepoint_t unicode,
92 hb_codepoint_t* glyph,
93 void* user_data) {
94 return skhb_glyph(hb_font, font_data, unicode, 0, glyph, user_data);
95}
96
97unsigned skhb_nominal_glyphs(hb_font_t *hb_font, void *font_data,
98 unsigned int count,
99 const hb_codepoint_t *unicodes,
100 unsigned int unicode_stride,
101 hb_codepoint_t *glyphs,
102 unsigned int glyph_stride,
103 void *user_data) {
104 SkFont& font = *reinterpret_cast<SkFont*>(font_data);
105
106 // Batch call textToGlyphs since entry cost is not cheap.
107 // Copy requred because textToGlyphs is dense and hb is strided.
109 for (unsigned i = 0; i < count; i++) {
110 unicode[i] = *unicodes;
111 unicodes = SkTAddOffset<const hb_codepoint_t>(unicodes, unicode_stride);
112 }
114 font.textToGlyphs(unicode.get(), count * sizeof(SkUnichar), SkTextEncoding::kUTF32,
115 glyph.get(), count);
116
117 // Copy the results back to the sparse array.
118 unsigned int done;
119 for (done = 0; done < count && glyph[done] != 0; done++) {
120 *glyphs = glyph[done];
121 glyphs = SkTAddOffset<hb_codepoint_t>(glyphs, glyph_stride);
122 }
123 // return 'done' to allow HarfBuzz to synthesize with NFC and spaces, return 'count' to avoid
124 return done;
125}
126
127hb_position_t skhb_glyph_h_advance(hb_font_t* hb_font,
128 void* font_data,
129 hb_codepoint_t hbGlyph,
130 void* user_data) {
131 SkFont& font = *reinterpret_cast<SkFont*>(font_data);
132
133 SkScalar advance;
134 SkGlyphID skGlyph = SkTo<SkGlyphID>(hbGlyph);
135
136 font.getWidths(&skGlyph, 1, &advance);
137 if (!font.isSubpixel()) {
138 advance = SkScalarRoundToInt(advance);
139 }
140 return skhb_position(advance);
141}
142
143void skhb_glyph_h_advances(hb_font_t* hb_font,
144 void* font_data,
145 unsigned count,
146 const hb_codepoint_t* glyphs,
147 unsigned int glyph_stride,
148 hb_position_t* advances,
149 unsigned int advance_stride,
150 void* user_data) {
151 SkFont& font = *reinterpret_cast<SkFont*>(font_data);
152
153 // Batch call getWidths since entry cost is not cheap.
154 // Copy requred because getWidths is dense and hb is strided.
156 for (unsigned i = 0; i < count; i++) {
157 glyph[i] = *glyphs;
158 glyphs = SkTAddOffset<const hb_codepoint_t>(glyphs, glyph_stride);
159 }
161 font.getWidths(glyph.get(), count, advance.get());
162
163 if (!font.isSubpixel()) {
164 for (unsigned i = 0; i < count; i++) {
165 advance[i] = SkScalarRoundToInt(advance[i]);
166 }
167 }
168
169 // Copy the results back to the sparse array.
170 for (unsigned i = 0; i < count; i++) {
171 *advances = skhb_position(advance[i]);
172 advances = SkTAddOffset<hb_position_t>(advances, advance_stride);
173 }
174}
175
176// HarfBuzz callback to retrieve glyph extents, mainly used by HarfBuzz for
177// fallback mark positioning, i.e. the situation when the font does not have
178// mark anchors or other mark positioning rules, but instead HarfBuzz is
179// supposed to heuristically place combining marks around base glyphs. HarfBuzz
180// does this by measuring "ink boxes" of glyphs, and placing them according to
181// Unicode mark classes. Above, below, centered or left or right, etc.
182hb_bool_t skhb_glyph_extents(hb_font_t* hb_font,
183 void* font_data,
184 hb_codepoint_t hbGlyph,
185 hb_glyph_extents_t* extents,
186 void* user_data) {
187 SkFont& font = *reinterpret_cast<SkFont*>(font_data);
188 SkASSERT(extents);
189
190 SkRect sk_bounds;
191 SkGlyphID skGlyph = SkTo<SkGlyphID>(hbGlyph);
192
193 font.getWidths(&skGlyph, 1, nullptr, &sk_bounds);
194 if (!font.isSubpixel()) {
195 sk_bounds.set(sk_bounds.roundOut());
196 }
197
198 // Skia is y-down but HarfBuzz is y-up.
199 extents->x_bearing = skhb_position(sk_bounds.fLeft);
200 extents->y_bearing = skhb_position(-sk_bounds.fTop);
201 extents->width = skhb_position(sk_bounds.width());
202 extents->height = skhb_position(-sk_bounds.height());
203 return true;
204}
205
206#define SK_HB_VERSION_CHECK(x, y, z) \
207 (HB_VERSION_MAJOR > (x)) || \
208 (HB_VERSION_MAJOR == (x) && HB_VERSION_MINOR > (y)) || \
209 (HB_VERSION_MAJOR == (x) && HB_VERSION_MINOR == (y) && HB_VERSION_MICRO >= (z))
210
211hb_font_funcs_t* skhb_get_font_funcs() {
212 static hb_font_funcs_t* const funcs = []{
213 // HarfBuzz will use the default (parent) implementation if they aren't set.
214 hb_font_funcs_t* const funcs = hb_font_funcs_create();
215 hb_font_funcs_set_variation_glyph_func(funcs, skhb_glyph, nullptr, nullptr);
216 hb_font_funcs_set_nominal_glyph_func(funcs, skhb_nominal_glyph, nullptr, nullptr);
217#if SK_HB_VERSION_CHECK(2, 0, 0)
218 hb_font_funcs_set_nominal_glyphs_func(funcs, skhb_nominal_glyphs, nullptr, nullptr);
219#else
220 sk_ignore_unused_variable(skhb_nominal_glyphs);
221#endif
222 hb_font_funcs_set_glyph_h_advance_func(funcs, skhb_glyph_h_advance, nullptr, nullptr);
223#if SK_HB_VERSION_CHECK(1, 8, 6)
224 hb_font_funcs_set_glyph_h_advances_func(funcs, skhb_glyph_h_advances, nullptr, nullptr);
225#else
226 sk_ignore_unused_variable(skhb_glyph_h_advances);
227#endif
228 hb_font_funcs_set_glyph_extents_func(funcs, skhb_glyph_extents, nullptr, nullptr);
229 hb_font_funcs_make_immutable(funcs);
230 return funcs;
231 }();
232 SkASSERT(funcs);
233 return funcs;
234}
235
236hb_blob_t* skhb_get_table(hb_face_t* face, hb_tag_t tag, void* user_data) {
237 SkTypeface& typeface = *reinterpret_cast<SkTypeface*>(user_data);
238
239 auto data = typeface.copyTableData(tag);
240 if (!data) {
241 return nullptr;
242 }
243 SkData* rawData = data.release();
244 return hb_blob_create(reinterpret_cast<char*>(rawData->writable_data()), rawData->size(),
245 HB_MEMORY_MODE_READONLY, rawData, [](void* ctx) {
246 SkSafeUnref(((SkData*)ctx));
247 });
248}
249
250HBBlob stream_to_blob(std::unique_ptr<SkStreamAsset> asset) {
251 size_t size = asset->getLength();
252 HBBlob blob;
253 if (const void* base = asset->getMemoryBase()) {
254 blob.reset(hb_blob_create((const char*)base, SkToUInt(size),
255 HB_MEMORY_MODE_READONLY, asset.release(),
256 [](void* p) { delete (SkStreamAsset*)p; }));
257 } else {
258 // SkDebugf("Extra SkStreamAsset copy\n");
259 void* ptr = size ? sk_malloc_throw(size) : nullptr;
260 asset->read(ptr, size);
261 blob.reset(hb_blob_create((char*)ptr, SkToUInt(size),
262 HB_MEMORY_MODE_READONLY, ptr, sk_free));
263 }
264 SkASSERT(blob);
265 hb_blob_make_immutable(blob.get());
266 return blob;
267}
268
269SkDEBUGCODE(static hb_user_data_key_t gDataIdKey;)
270
271HBFace create_hb_face(const SkTypeface& typeface) {
272 int index = 0;
273 std::unique_ptr<SkStreamAsset> typefaceAsset = typeface.openExistingStream(&index);
274 HBFace face;
275 if (typefaceAsset && typefaceAsset->getMemoryBase()) {
276 HBBlob blob(stream_to_blob(std::move(typefaceAsset)));
277 // hb_face_create always succeeds. Check that the format is minimally recognized first.
278 // hb_face_create_for_tables may still create a working hb_face.
279 // See https://github.com/harfbuzz/harfbuzz/issues/248 .
280 unsigned int num_hb_faces = hb_face_count(blob.get());
281 if (0 < num_hb_faces && (unsigned)index < num_hb_faces) {
282 face.reset(hb_face_create(blob.get(), (unsigned)index));
283 // Check the number of glyphs as a basic sanitization step.
284 if (face && hb_face_get_glyph_count(face.get()) == 0) {
285 face.reset();
286 }
287 }
288 }
289 if (!face) {
290 face.reset(hb_face_create_for_tables(
291 skhb_get_table,
292 const_cast<SkTypeface*>(SkRef(&typeface)),
293 [](void* user_data){ SkSafeUnref(reinterpret_cast<SkTypeface*>(user_data)); }));
294 hb_face_set_index(face.get(), (unsigned)index);
295 }
296 SkASSERT(face);
297 if (!face) {
298 return nullptr;
299 }
300 hb_face_set_upem(face.get(), typeface.getUnitsPerEm());
301
303 hb_face_set_user_data(face.get(), &gDataIdKey, const_cast<SkTypeface*>(&typeface),
304 nullptr, false);
305 )
306
307 return face;
308}
309
310HBFont create_typeface_hb_font(const SkTypeface& typeface) {
311 HBFace face(create_hb_face(typeface));
312 if (!face) {
313 return nullptr;
314 }
315
316 HBFont otFont(hb_font_create(face.get()));
317 SkASSERT(otFont);
318 if (!otFont) {
319 return nullptr;
320 }
321 hb_ot_font_set_funcs(otFont.get());
322 int axis_count = typeface.getVariationDesignPosition(nullptr, 0);
323 if (axis_count > 0) {
325 if (typeface.getVariationDesignPosition(axis_values, axis_count) == axis_count) {
326 hb_font_set_variations(otFont.get(),
327 reinterpret_cast<hb_variation_t*>(axis_values.get()),
328 axis_count);
329 }
330 }
331
332 return otFont;
333}
334
335HBFont create_sub_hb_font(const SkFont& font, const HBFont& typefaceFont) {
337 hb_face_t* face = hb_font_get_face(typefaceFont.get());
338 void* dataId = hb_face_get_user_data(face, &gDataIdKey);
339 SkASSERT(dataId == font.getTypeface());
340 )
341
342 // Creating a sub font means that non-available functions
343 // are found from the parent.
344 HBFont skFont(hb_font_create_sub_font(typefaceFont.get()));
345 hb_font_set_funcs(skFont.get(), skhb_get_font_funcs(),
346 reinterpret_cast<void *>(new SkFont(font)),
347 [](void* user_data){ delete reinterpret_cast<SkFont*>(user_data); });
348 int scale = skhb_position(font.getSize());
349 hb_font_set_scale(skFont.get(), scale, scale);
350
351 return skFont;
352}
353
354/** Replaces invalid utf-8 sequences with REPLACEMENT CHARACTER U+FFFD. */
355static inline SkUnichar utf8_next(const char** ptr, const char* end) {
356 SkUnichar val = SkUTF::NextUTF8(ptr, end);
357 return val < 0 ? 0xFFFD : val;
358}
359
360class SkUnicodeHbScriptRunIterator final: public SkShaper::ScriptRunIterator {
361public:
362 SkUnicodeHbScriptRunIterator(const char* utf8,
363 size_t utf8Bytes,
364 hb_script_t defaultScript)
365 : fCurrent(utf8)
366 , fBegin(utf8)
367 , fEnd(fCurrent + utf8Bytes)
368 , fCurrentScript(defaultScript) {}
369 hb_script_t hb_script_for_unichar(SkUnichar u) {
370 return hb_unicode_script(hb_unicode_funcs_get_default(), u);
371 }
372 void consume() override {
373 SkASSERT(fCurrent < fEnd);
374 SkUnichar u = utf8_next(&fCurrent, fEnd);
375 fCurrentScript = hb_script_for_unichar(u);
376 while (fCurrent < fEnd) {
377 const char* prev = fCurrent;
378 u = utf8_next(&fCurrent, fEnd);
379 const hb_script_t script = hb_script_for_unichar(u);
380 if (script != fCurrentScript) {
381 if (fCurrentScript == HB_SCRIPT_INHERITED || fCurrentScript == HB_SCRIPT_COMMON) {
382 fCurrentScript = script;
383 } else if (script == HB_SCRIPT_INHERITED || script == HB_SCRIPT_COMMON) {
384 continue;
385 } else {
386 fCurrent = prev;
387 break;
388 }
389 }
390 }
391 if (fCurrentScript == HB_SCRIPT_INHERITED) {
392 fCurrentScript = HB_SCRIPT_COMMON;
393 }
394 }
395 size_t endOfCurrentRun() const override {
396 return fCurrent - fBegin;
397 }
398 bool atEnd() const override {
399 return fCurrent == fEnd;
400 }
401
402 SkFourByteTag currentScript() const override {
403 return SkSetFourByteTag(HB_UNTAG(fCurrentScript));
404 }
405private:
406 char const * fCurrent;
407 char const * const fBegin;
408 char const * const fEnd;
409 hb_script_t fCurrentScript;
410};
411
412class RunIteratorQueue {
413public:
414 void insert(SkShaper::RunIterator* runIterator, int priority) {
415 fEntries.insert({runIterator, priority});
416 }
417
418 bool advanceRuns() {
419 const SkShaper::RunIterator* leastRun = fEntries.peek().runIterator;
420 if (leastRun->atEnd()) {
421 SkASSERT(this->allRunsAreAtEnd());
422 return false;
423 }
424 const size_t leastEnd = leastRun->endOfCurrentRun();
425 SkShaper::RunIterator* currentRun = nullptr;
426 SkDEBUGCODE(size_t previousEndOfCurrentRun);
427 while ((currentRun = fEntries.peek().runIterator)->endOfCurrentRun() <= leastEnd) {
428 int priority = fEntries.peek().priority;
429 fEntries.pop();
430 SkDEBUGCODE(previousEndOfCurrentRun = currentRun->endOfCurrentRun());
431 currentRun->consume();
432 SkASSERT(previousEndOfCurrentRun < currentRun->endOfCurrentRun());
433 fEntries.insert({currentRun, priority});
434 }
435 return true;
436 }
437
438 size_t endOfCurrentRun() const {
439 return fEntries.peek().runIterator->endOfCurrentRun();
440 }
441
442private:
443 bool allRunsAreAtEnd() const {
444 for (int i = 0; i < fEntries.count(); ++i) {
445 if (!fEntries.at(i).runIterator->atEnd()) {
446 return false;
447 }
448 }
449 return true;
450 }
451
452 struct Entry {
453 SkShaper::RunIterator* runIterator;
454 int priority;
455 };
456 static bool CompareEntry(Entry const& a, Entry const& b) {
457 size_t aEnd = a.runIterator->endOfCurrentRun();
458 size_t bEnd = b.runIterator->endOfCurrentRun();
459 return aEnd < bEnd || (aEnd == bEnd && a.priority < b.priority);
460 }
462};
463
464struct ShapedGlyph {
465 SkGlyphID fID;
466 uint32_t fCluster;
467 SkPoint fOffset;
468 SkVector fAdvance;
469 bool fMayLineBreakBefore;
470 bool fMustLineBreakBefore;
471 bool fHasVisual;
472 bool fGraphemeBreakBefore;
473 bool fUnsafeToBreak;
474};
475struct ShapedRun {
476 ShapedRun(SkShaper::RunHandler::Range utf8Range, const SkFont& font, SkBidiIterator::Level level,
477 std::unique_ptr<ShapedGlyph[]> glyphs, size_t numGlyphs, SkVector advance = {0, 0})
478 : fUtf8Range(utf8Range), fFont(font), fLevel(level)
479 , fGlyphs(std::move(glyphs)), fNumGlyphs(numGlyphs), fAdvance(advance)
480 {}
481
483 SkFont fFont;
485 std::unique_ptr<ShapedGlyph[]> fGlyphs;
486 size_t fNumGlyphs;
487 SkVector fAdvance;
488
489 static_assert(::sk_is_trivially_relocatable<decltype(fUtf8Range)>::value);
490 static_assert(::sk_is_trivially_relocatable<decltype(fFont)>::value);
491 static_assert(::sk_is_trivially_relocatable<decltype(fLevel)>::value);
492 static_assert(::sk_is_trivially_relocatable<decltype(fGlyphs)>::value);
493 static_assert(::sk_is_trivially_relocatable<decltype(fAdvance)>::value);
494
495 using sk_is_trivially_relocatable = std::true_type;
496};
497struct ShapedLine {
499 SkVector fAdvance = { 0, 0 };
500};
501
502constexpr bool is_LTR(SkBidiIterator::Level level) {
503 return (level & 1) == 0;
504}
505
506void append(SkShaper::RunHandler* handler, const SkShaper::RunHandler::RunInfo& runInfo,
507 const ShapedRun& run, size_t startGlyphIndex, size_t endGlyphIndex) {
508 SkASSERT(startGlyphIndex <= endGlyphIndex);
509 const size_t glyphLen = endGlyphIndex - startGlyphIndex;
510
511 const auto buffer = handler->runBuffer(runInfo);
512 SkASSERT(buffer.glyphs);
513 SkASSERT(buffer.positions);
514
515 SkVector advance = {0,0};
516 for (size_t i = 0; i < glyphLen; i++) {
517 // Glyphs are in logical order, but output ltr since PDF readers seem to expect that.
518 const ShapedGlyph& glyph = run.fGlyphs[is_LTR(run.fLevel) ? startGlyphIndex + i
519 : endGlyphIndex - 1 - i];
520 buffer.glyphs[i] = glyph.fID;
521 if (buffer.offsets) {
522 buffer.positions[i] = advance + buffer.point;
523 buffer.offsets[i] = glyph.fOffset;
524 } else {
525 buffer.positions[i] = advance + buffer.point + glyph.fOffset;
526 }
527 if (buffer.clusters) {
528 buffer.clusters[i] = glyph.fCluster;
529 }
530 advance += glyph.fAdvance;
531 }
532 handler->commitRunBuffer(runInfo);
533}
534
535void emit(SkUnicode* unicode, const ShapedLine& line, SkShaper::RunHandler* handler) {
536 // Reorder the runs and glyphs per line and write them out.
537 handler->beginLine();
538
539 int numRuns = line.runs.size();
541 for (int i = 0; i < numRuns; ++i) {
542 runLevels[i] = line.runs[i].fLevel;
543 }
544 AutoSTMalloc<4, int32_t> logicalFromVisual(numRuns);
545 unicode->reorderVisual(runLevels, numRuns, logicalFromVisual);
546
547 for (int i = 0; i < numRuns; ++i) {
548 int logicalIndex = logicalFromVisual[i];
549
550 const auto& run = line.runs[logicalIndex];
552 run.fFont,
553 run.fLevel,
554 run.fAdvance,
555 run.fNumGlyphs,
556 run.fUtf8Range
557 };
558 handler->runInfo(info);
559 }
560 handler->commitRunInfo();
561 for (int i = 0; i < numRuns; ++i) {
562 int logicalIndex = logicalFromVisual[i];
563
564 const auto& run = line.runs[logicalIndex];
566 run.fFont,
567 run.fLevel,
568 run.fAdvance,
569 run.fNumGlyphs,
570 run.fUtf8Range
571 };
572 append(handler, info, run, 0, run.fNumGlyphs);
573 }
574
575 handler->commitLine();
576}
577
578struct ShapedRunGlyphIterator {
579 ShapedRunGlyphIterator(const TArray<ShapedRun>& origRuns)
580 : fRuns(&origRuns), fRunIndex(0), fGlyphIndex(0)
581 { }
582
583 ShapedRunGlyphIterator(const ShapedRunGlyphIterator& that) = default;
584 ShapedRunGlyphIterator& operator=(const ShapedRunGlyphIterator& that) = default;
585 bool operator==(const ShapedRunGlyphIterator& that) const {
586 return fRuns == that.fRuns &&
587 fRunIndex == that.fRunIndex &&
588 fGlyphIndex == that.fGlyphIndex;
589 }
590 bool operator!=(const ShapedRunGlyphIterator& that) const {
591 return fRuns != that.fRuns ||
592 fRunIndex != that.fRunIndex ||
593 fGlyphIndex != that.fGlyphIndex;
594 }
595
596 ShapedGlyph* next() {
597 const TArray<ShapedRun>& runs = *fRuns;
598 SkASSERT(fRunIndex < runs.size());
599 SkASSERT(fGlyphIndex < runs[fRunIndex].fNumGlyphs);
600
601 ++fGlyphIndex;
602 if (fGlyphIndex == runs[fRunIndex].fNumGlyphs) {
603 fGlyphIndex = 0;
604 ++fRunIndex;
605 if (fRunIndex >= runs.size()) {
606 return nullptr;
607 }
608 }
609 return &runs[fRunIndex].fGlyphs[fGlyphIndex];
610 }
611
612 ShapedGlyph* current() {
613 const TArray<ShapedRun>& runs = *fRuns;
614 if (fRunIndex >= runs.size()) {
615 return nullptr;
616 }
617 return &runs[fRunIndex].fGlyphs[fGlyphIndex];
618 }
619
620 const TArray<ShapedRun>* fRuns;
621 int fRunIndex;
622 size_t fGlyphIndex;
623};
624
625class ShaperHarfBuzz : public SkShaper {
626public:
627 ShaperHarfBuzz(sk_sp<SkUnicode>,
628 HBBuffer,
630
631protected:
632 sk_sp<SkUnicode> fUnicode;
633
634 ShapedRun shape(const char* utf8, size_t utf8Bytes,
635 const char* utf8Start,
636 const char* utf8End,
637 const BiDiRunIterator&,
638 const LanguageRunIterator&,
639 const ScriptRunIterator&,
640 const FontRunIterator&,
641 const Feature*, size_t featuresSize) const;
642private:
643 const sk_sp<SkFontMgr> fFontMgr; // for fallback
644 HBBuffer fBuffer;
645 hb_language_t fUndefinedLanguage;
646
647#if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
648 void shape(const char* utf8, size_t utf8Bytes,
649 const SkFont&,
650 bool leftToRight,
652 RunHandler*) const override;
653
654 void shape(const char* utf8Text, size_t textBytes,
655 FontRunIterator&,
656 BiDiRunIterator&,
657 ScriptRunIterator&,
658 LanguageRunIterator&,
660 RunHandler*) const override;
661#endif
662
663 void shape(const char* utf8Text, size_t textBytes,
664 FontRunIterator&,
665 BiDiRunIterator&,
666 ScriptRunIterator&,
667 LanguageRunIterator&,
668 const Feature*, size_t featuresSize,
670 RunHandler*) const override;
671
672 virtual void wrap(char const * const utf8, size_t utf8Bytes,
673 const BiDiRunIterator&,
674 const LanguageRunIterator&,
675 const ScriptRunIterator&,
676 const FontRunIterator&,
677 RunIteratorQueue& runSegmenter,
678 const Feature*, size_t featuresSize,
680 RunHandler*) const = 0;
681};
682
683class ShaperDrivenWrapper : public ShaperHarfBuzz {
684public:
685 using ShaperHarfBuzz::ShaperHarfBuzz;
686private:
687 void wrap(char const * const utf8, size_t utf8Bytes,
688 const BiDiRunIterator&,
689 const LanguageRunIterator&,
690 const ScriptRunIterator&,
691 const FontRunIterator&,
692 RunIteratorQueue& runSegmenter,
693 const Feature*, size_t featuresSize,
695 RunHandler*) const override;
696};
697
698class ShapeThenWrap : public ShaperHarfBuzz {
699public:
700 using ShaperHarfBuzz::ShaperHarfBuzz;
701private:
702 void wrap(char const * const utf8, size_t utf8Bytes,
703 const BiDiRunIterator&,
704 const LanguageRunIterator&,
705 const ScriptRunIterator&,
706 const FontRunIterator&,
707 RunIteratorQueue& runSegmenter,
708 const Feature*, size_t featuresSize,
710 RunHandler*) const override;
711};
712
713class ShapeDontWrapOrReorder : public ShaperHarfBuzz {
714public:
715 using ShaperHarfBuzz::ShaperHarfBuzz;
716private:
717 void wrap(char const * const utf8, size_t utf8Bytes,
718 const BiDiRunIterator&,
719 const LanguageRunIterator&,
720 const ScriptRunIterator&,
721 const FontRunIterator&,
722 RunIteratorQueue& runSegmenter,
723 const Feature*, size_t featuresSize,
725 RunHandler*) const override;
726};
727
728ShaperHarfBuzz::ShaperHarfBuzz(sk_sp<SkUnicode> unicode,
729 HBBuffer buffer,
730 sk_sp<SkFontMgr> fallback)
731 : fUnicode(unicode)
732 , fFontMgr(fallback ? std::move(fallback) : SkFontMgr::RefEmpty())
733 , fBuffer(std::move(buffer))
734 , fUndefinedLanguage(hb_language_from_string("und", -1)) {
735#if defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
736 SkASSERT(fUnicode);
737#endif
738}
739
740#if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
741void ShaperHarfBuzz::shape(const char* utf8,
742 size_t utf8Bytes,
743 const SkFont& srcFont,
744 bool leftToRight,
746 RunHandler* handler) const {
748 std::unique_ptr<BiDiRunIterator> bidi(
749 SkShapers::unicode::BidiRunIterator(fUnicode, utf8, utf8Bytes, defaultLevel));
750
751 if (!bidi) {
752 return;
753 }
754
755 std::unique_ptr<LanguageRunIterator> language(MakeStdLanguageRunIterator(utf8, utf8Bytes));
756 if (!language) {
757 return;
758 }
759
760 std::unique_ptr<ScriptRunIterator> script(SkShapers::HB::ScriptRunIterator(utf8, utf8Bytes));
761 if (!script) {
762 return;
763 }
764
765 std::unique_ptr<FontRunIterator> font(
766 MakeFontMgrRunIterator(utf8, utf8Bytes, srcFont, fFontMgr));
767 if (!font) {
768 return;
769 }
770
771 this->shape(utf8, utf8Bytes, *font, *bidi, *script, *language, width, handler);
772}
773
774void ShaperHarfBuzz::shape(const char* utf8,
775 size_t utf8Bytes,
776 FontRunIterator& font,
777 BiDiRunIterator& bidi,
778 ScriptRunIterator& script,
779 LanguageRunIterator& language,
781 RunHandler* handler) const {
782 this->shape(utf8, utf8Bytes, font, bidi, script, language, nullptr, 0, width, handler);
783}
784#endif // !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
785
786void ShaperHarfBuzz::shape(const char* utf8,
787 size_t utf8Bytes,
788 FontRunIterator& font,
789 BiDiRunIterator& bidi,
790 ScriptRunIterator& script,
791 LanguageRunIterator& language,
792 const Feature* features,
793 size_t featuresSize,
795 RunHandler* handler) const {
796 SkASSERT(handler);
797 RunIteratorQueue runSegmenter;
798 runSegmenter.insert(&font, 3); // The font iterator is always run last in case of tie.
799 runSegmenter.insert(&bidi, 2);
800 runSegmenter.insert(&script, 1);
801 runSegmenter.insert(&language, 0);
802
803 this->wrap(utf8, utf8Bytes, bidi, language, script, font, runSegmenter,
804 features, featuresSize, width, handler);
805}
806
807void ShaperDrivenWrapper::wrap(char const * const utf8, size_t utf8Bytes,
808 const BiDiRunIterator& bidi,
809 const LanguageRunIterator& language,
810 const ScriptRunIterator& script,
811 const FontRunIterator& font,
812 RunIteratorQueue& runSegmenter,
813 const Feature* features, size_t featuresSize,
815 RunHandler* handler) const
816{
817 ShapedLine line;
818
819 const char* utf8Start = nullptr;
820 const char* utf8End = utf8;
821 SkUnicodeBreak lineBreakIterator;
822 SkString currentLanguage;
823 while (runSegmenter.advanceRuns()) { // For each item
824 utf8Start = utf8End;
825 utf8End = utf8 + runSegmenter.endOfCurrentRun();
826
827 ShapedRun model(RunHandler::Range(), SkFont(), 0, nullptr, 0);
828 bool modelNeedsRegenerated = true;
829 int modelGlyphOffset = 0;
830
831 struct TextProps {
832 int glyphLen = 0;
833 SkVector advance = {0, 0};
834 };
835 // map from character position to [safe to break, glyph position, advance]
836 std::unique_ptr<TextProps[]> modelText;
837 int modelTextOffset = 0;
838 SkVector modelAdvanceOffset = {0, 0};
839
840 while (utf8Start < utf8End) { // While there are still code points left in this item
841 size_t utf8runLength = utf8End - utf8Start;
842 if (modelNeedsRegenerated) {
843 model = shape(utf8, utf8Bytes,
844 utf8Start, utf8End,
845 bidi, language, script, font,
846 features, featuresSize);
847 modelGlyphOffset = 0;
848
849 SkVector advance = {0, 0};
850 modelText = std::make_unique<TextProps[]>(utf8runLength + 1);
851 size_t modelStartCluster = utf8Start - utf8;
852 size_t previousCluster = 0;
853 for (size_t i = 0; i < model.fNumGlyphs; ++i) {
854 SkASSERT(modelStartCluster <= model.fGlyphs[i].fCluster);
855 SkASSERT( model.fGlyphs[i].fCluster < (size_t)(utf8End - utf8));
856 if (!model.fGlyphs[i].fUnsafeToBreak) {
857 // Store up to the first glyph in the cluster.
858 size_t currentCluster = model.fGlyphs[i].fCluster - modelStartCluster;
859 if (previousCluster != currentCluster) {
860 previousCluster = currentCluster;
861 modelText[currentCluster].glyphLen = i;
862 modelText[currentCluster].advance = advance;
863 }
864 }
865 advance += model.fGlyphs[i].fAdvance;
866 }
867 // Assume it is always safe to break after the end of an item
868 modelText[utf8runLength].glyphLen = model.fNumGlyphs;
869 modelText[utf8runLength].advance = model.fAdvance;
870 modelTextOffset = 0;
871 modelAdvanceOffset = {0, 0};
872 modelNeedsRegenerated = false;
873 }
874
875 // TODO: break iterator per item, but just reset position if needed?
876 // Maybe break iterator with model?
877 if (!lineBreakIterator || !currentLanguage.equals(language.currentLanguage())) {
878 currentLanguage = language.currentLanguage();
879 lineBreakIterator = fUnicode->makeBreakIterator(currentLanguage.c_str(),
881 if (!lineBreakIterator) {
882 return;
883 }
884 }
885 if (!lineBreakIterator->setText(utf8Start, utf8runLength)) {
886 return;
887 }
888 SkBreakIterator& breakIterator = *lineBreakIterator;
889
890 ShapedRun best(RunHandler::Range(), SkFont(), 0, nullptr, 0,
892 bool bestIsInvalid = true;
893 bool bestUsesModelForGlyphs = false;
894 SkScalar widthLeft = width - line.fAdvance.fX;
895
896 for (int32_t breakIteratorCurrent = breakIterator.next();
897 !breakIterator.isDone();
898 breakIteratorCurrent = breakIterator.next())
899 {
900 // TODO: if past a safe to break, future safe to break will be at least as long
901
902 // TODO: adjust breakIteratorCurrent by ignorable whitespace
903 bool candidateUsesModelForGlyphs = false;
904 ShapedRun candidate = [&](const TextProps& props){
905 if (props.glyphLen) {
906 candidateUsesModelForGlyphs = true;
907 return ShapedRun(RunHandler::Range(utf8Start - utf8, breakIteratorCurrent),
908 font.currentFont(), bidi.currentLevel(),
909 std::unique_ptr<ShapedGlyph[]>(),
910 props.glyphLen - modelGlyphOffset,
911 props.advance - modelAdvanceOffset);
912 } else {
913 return shape(utf8, utf8Bytes,
914 utf8Start, utf8Start + breakIteratorCurrent,
915 bidi, language, script, font,
916 features, featuresSize);
917 }
918 }(modelText[breakIteratorCurrent + modelTextOffset]);
919 auto score = [widthLeft](const ShapedRun& run) -> SkScalar {
920 if (run.fAdvance.fX < widthLeft) {
921 return run.fUtf8Range.size();
922 } else {
923 return widthLeft - run.fAdvance.fX;
924 }
925 };
926 if (bestIsInvalid || score(best) < score(candidate)) {
927 best = std::move(candidate);
928 bestIsInvalid = false;
929 bestUsesModelForGlyphs = candidateUsesModelForGlyphs;
930 }
931 }
932
933 // If nothing fit (best score is negative) and the line is not empty
934 if (width < line.fAdvance.fX + best.fAdvance.fX && !line.runs.empty()) {
935 emit(fUnicode.get(), line, handler);
936 line.runs.clear();
937 line.fAdvance = {0, 0};
938 } else {
939 if (bestUsesModelForGlyphs) {
940 best.fGlyphs = std::make_unique<ShapedGlyph[]>(best.fNumGlyphs);
941 memcpy(best.fGlyphs.get(), model.fGlyphs.get() + modelGlyphOffset,
942 best.fNumGlyphs * sizeof(ShapedGlyph));
943 modelGlyphOffset += best.fNumGlyphs;
944 modelTextOffset += best.fUtf8Range.size();
945 modelAdvanceOffset += best.fAdvance;
946 } else {
947 modelNeedsRegenerated = true;
948 }
949 utf8Start += best.fUtf8Range.size();
950 line.fAdvance += best.fAdvance;
951 line.runs.emplace_back(std::move(best));
952
953 // If item broken, emit line (prevent remainder from accidentally fitting)
954 if (utf8Start != utf8End) {
955 emit(fUnicode.get(), line, handler);
956 line.runs.clear();
957 line.fAdvance = {0, 0};
958 }
959 }
960 }
961 }
962 emit(fUnicode.get(), line, handler);
963}
964
965void ShapeThenWrap::wrap(char const * const utf8, size_t utf8Bytes,
966 const BiDiRunIterator& bidi,
967 const LanguageRunIterator& language,
968 const ScriptRunIterator& script,
969 const FontRunIterator& font,
970 RunIteratorQueue& runSegmenter,
971 const Feature* features, size_t featuresSize,
973 RunHandler* handler) const
974{
976{
977 SkString currentLanguage;
978 SkUnicodeBreak lineBreakIterator;
979 SkUnicodeBreak graphemeBreakIterator;
980 bool needIteratorInit = true;
981 const char* utf8Start = nullptr;
982 const char* utf8End = utf8;
983 while (runSegmenter.advanceRuns()) {
984 utf8Start = utf8End;
985 utf8End = utf8 + runSegmenter.endOfCurrentRun();
986
987 runs.emplace_back(shape(utf8, utf8Bytes,
988 utf8Start, utf8End,
989 bidi, language, script, font,
990 features, featuresSize));
991 ShapedRun& run = runs.back();
992
993 if (needIteratorInit || !currentLanguage.equals(language.currentLanguage())) {
994 currentLanguage = language.currentLanguage();
995 lineBreakIterator = fUnicode->makeBreakIterator(currentLanguage.c_str(),
997 if (!lineBreakIterator) {
998 return;
999 }
1000 graphemeBreakIterator = fUnicode->makeBreakIterator(currentLanguage.c_str(),
1002 if (!graphemeBreakIterator) {
1003 return;
1004 }
1005 needIteratorInit = false;
1006 }
1007 size_t utf8runLength = utf8End - utf8Start;
1008 if (!lineBreakIterator->setText(utf8Start, utf8runLength)) {
1009 return;
1010 }
1011 if (!graphemeBreakIterator->setText(utf8Start, utf8runLength)) {
1012 return;
1013 }
1014
1015 uint32_t previousCluster = 0xFFFFFFFF;
1016 for (size_t i = 0; i < run.fNumGlyphs; ++i) {
1017 ShapedGlyph& glyph = run.fGlyphs[i];
1018 int32_t glyphCluster = glyph.fCluster;
1019
1020 int32_t lineBreakIteratorCurrent = lineBreakIterator->current();
1021 while (!lineBreakIterator->isDone() && lineBreakIteratorCurrent < glyphCluster)
1022 {
1023 lineBreakIteratorCurrent = lineBreakIterator->next();
1024 }
1025 glyph.fMayLineBreakBefore = glyph.fCluster != previousCluster &&
1026 lineBreakIteratorCurrent == glyphCluster;
1027
1028 int32_t graphemeBreakIteratorCurrent = graphemeBreakIterator->current();
1029 while (!graphemeBreakIterator->isDone() && graphemeBreakIteratorCurrent < glyphCluster)
1030 {
1031 graphemeBreakIteratorCurrent = graphemeBreakIterator->next();
1032 }
1033 glyph.fGraphemeBreakBefore = glyph.fCluster != previousCluster &&
1034 graphemeBreakIteratorCurrent == glyphCluster;
1035
1036 previousCluster = glyph.fCluster;
1037 }
1038 }
1039}
1040
1041// Iterate over the glyphs in logical order to find potential line lengths.
1042{
1043 /** The position of the beginning of the line. */
1044 ShapedRunGlyphIterator beginning(runs);
1045
1046 /** The position of the candidate line break. */
1047 ShapedRunGlyphIterator candidateLineBreak(runs);
1048 SkScalar candidateLineBreakWidth = 0;
1049
1050 /** The position of the candidate grapheme break. */
1051 ShapedRunGlyphIterator candidateGraphemeBreak(runs);
1052 SkScalar candidateGraphemeBreakWidth = 0;
1053
1054 /** The position of the current location. */
1055 ShapedRunGlyphIterator current(runs);
1056 SkScalar currentWidth = 0;
1057 while (ShapedGlyph* glyph = current.current()) {
1058 // 'Break' at graphemes until a line boundary, then only at line boundaries.
1059 // Only break at graphemes if no line boundary is valid.
1060 if (current != beginning) {
1061 if (glyph->fGraphemeBreakBefore || glyph->fMayLineBreakBefore) {
1062 // TODO: preserve line breaks <= grapheme breaks
1063 // and prevent line breaks inside graphemes
1064 candidateGraphemeBreak = current;
1065 candidateGraphemeBreakWidth = currentWidth;
1066 if (glyph->fMayLineBreakBefore) {
1067 candidateLineBreak = current;
1068 candidateLineBreakWidth = currentWidth;
1069 }
1070 }
1071 }
1072
1073 SkScalar glyphWidth = glyph->fAdvance.fX;
1074 // Break when overwidth, the glyph has a visual representation, and some space is used.
1075 if (width < currentWidth + glyphWidth && glyph->fHasVisual && candidateGraphemeBreakWidth > 0){
1076 if (candidateLineBreak != beginning) {
1077 beginning = candidateLineBreak;
1078 currentWidth -= candidateLineBreakWidth;
1079 candidateGraphemeBreakWidth -= candidateLineBreakWidth;
1080 candidateLineBreakWidth = 0;
1081 } else if (candidateGraphemeBreak != beginning) {
1082 beginning = candidateGraphemeBreak;
1083 candidateLineBreak = beginning;
1084 currentWidth -= candidateGraphemeBreakWidth;
1085 candidateGraphemeBreakWidth = 0;
1086 candidateLineBreakWidth = 0;
1087 } else {
1088 SK_ABORT("");
1089 }
1090
1091 if (width < currentWidth) {
1092 if (width < candidateGraphemeBreakWidth) {
1093 candidateGraphemeBreak = candidateLineBreak;
1094 candidateGraphemeBreakWidth = candidateLineBreakWidth;
1095 }
1096 current = candidateGraphemeBreak;
1097 currentWidth = candidateGraphemeBreakWidth;
1098 }
1099
1100 glyph = beginning.current();
1101 if (glyph) {
1102 glyph->fMustLineBreakBefore = true;
1103 }
1104
1105 } else {
1106 current.next();
1107 currentWidth += glyphWidth;
1108 }
1109 }
1110}
1111
1112// Reorder the runs and glyphs per line and write them out.
1113{
1114 ShapedRunGlyphIterator previousBreak(runs);
1115 ShapedRunGlyphIterator glyphIterator(runs);
1116 int previousRunIndex = -1;
1117 while (glyphIterator.current()) {
1118 const ShapedRunGlyphIterator current = glyphIterator;
1119 ShapedGlyph* nextGlyph = glyphIterator.next();
1120
1121 if (previousRunIndex != current.fRunIndex) {
1122 SkFontMetrics metrics;
1123 runs[current.fRunIndex].fFont.getMetrics(&metrics);
1124 previousRunIndex = current.fRunIndex;
1125 }
1126
1127 // Nothing can be written until the baseline is known.
1128 if (!(nextGlyph == nullptr || nextGlyph->fMustLineBreakBefore)) {
1129 continue;
1130 }
1131
1132 int numRuns = current.fRunIndex - previousBreak.fRunIndex + 1;
1133 AutoSTMalloc<4, SkBidiIterator::Level> runLevels(numRuns);
1134 for (int i = 0; i < numRuns; ++i) {
1135 runLevels[i] = runs[previousBreak.fRunIndex + i].fLevel;
1136 }
1137 AutoSTMalloc<4, int32_t> logicalFromVisual(numRuns);
1138 fUnicode->reorderVisual(runLevels, numRuns, logicalFromVisual);
1139
1140 // step through the runs in reverse visual order and the glyphs in reverse logical order
1141 // until a visible glyph is found and force them to the end of the visual line.
1142
1143 handler->beginLine();
1144
1145 struct SubRun { const ShapedRun& run; size_t startGlyphIndex; size_t endGlyphIndex; };
1146 auto makeSubRun = [&runs, &previousBreak, &current, &logicalFromVisual](size_t visualIndex){
1147 int logicalIndex = previousBreak.fRunIndex + logicalFromVisual[visualIndex];
1148 const auto& run = runs[logicalIndex];
1149 size_t startGlyphIndex = (logicalIndex == previousBreak.fRunIndex)
1150 ? previousBreak.fGlyphIndex
1151 : 0;
1152 size_t endGlyphIndex = (logicalIndex == current.fRunIndex)
1153 ? current.fGlyphIndex + 1
1154 : run.fNumGlyphs;
1155 return SubRun{ run, startGlyphIndex, endGlyphIndex };
1156 };
1157 auto makeRunInfo = [](const SubRun& sub) {
1158 uint32_t startUtf8 = sub.run.fGlyphs[sub.startGlyphIndex].fCluster;
1159 uint32_t endUtf8 = (sub.endGlyphIndex < sub.run.fNumGlyphs)
1160 ? sub.run.fGlyphs[sub.endGlyphIndex].fCluster
1161 : sub.run.fUtf8Range.end();
1162
1163 SkVector advance = SkVector::Make(0, 0);
1164 for (size_t i = sub.startGlyphIndex; i < sub.endGlyphIndex; ++i) {
1165 advance += sub.run.fGlyphs[i].fAdvance;
1166 }
1167
1168 return RunHandler::RunInfo{
1169 sub.run.fFont,
1170 sub.run.fLevel,
1171 advance,
1172 sub.endGlyphIndex - sub.startGlyphIndex,
1173 RunHandler::Range(startUtf8, endUtf8 - startUtf8)
1174 };
1175 };
1176
1177 for (int i = 0; i < numRuns; ++i) {
1178 handler->runInfo(makeRunInfo(makeSubRun(i)));
1179 }
1180 handler->commitRunInfo();
1181 for (int i = 0; i < numRuns; ++i) {
1182 SubRun sub = makeSubRun(i);
1183 append(handler, makeRunInfo(sub), sub.run, sub.startGlyphIndex, sub.endGlyphIndex);
1184 }
1185
1186 handler->commitLine();
1187
1188 previousRunIndex = -1;
1189 previousBreak = glyphIterator;
1190 }
1191}
1192}
1193
1194void ShapeDontWrapOrReorder::wrap(char const * const utf8, size_t utf8Bytes,
1195 const BiDiRunIterator& bidi,
1196 const LanguageRunIterator& language,
1197 const ScriptRunIterator& script,
1198 const FontRunIterator& font,
1199 RunIteratorQueue& runSegmenter,
1200 const Feature* features, size_t featuresSize,
1202 RunHandler* handler) const
1203{
1205 TArray<ShapedRun> runs;
1206
1207 const char* utf8Start = nullptr;
1208 const char* utf8End = utf8;
1209 while (runSegmenter.advanceRuns()) {
1210 utf8Start = utf8End;
1211 utf8End = utf8 + runSegmenter.endOfCurrentRun();
1212
1213 runs.emplace_back(shape(utf8, utf8Bytes,
1214 utf8Start, utf8End,
1215 bidi, language, script, font,
1216 features, featuresSize));
1217 }
1218
1219 handler->beginLine();
1220 for (const auto& run : runs) {
1221 const RunHandler::RunInfo info = {
1222 run.fFont,
1223 run.fLevel,
1224 run.fAdvance,
1225 run.fNumGlyphs,
1226 run.fUtf8Range
1227 };
1228 handler->runInfo(info);
1229 }
1230 handler->commitRunInfo();
1231 for (const auto& run : runs) {
1232 const RunHandler::RunInfo info = {
1233 run.fFont,
1234 run.fLevel,
1235 run.fAdvance,
1236 run.fNumGlyphs,
1237 run.fUtf8Range
1238 };
1239 append(handler, info, run, 0, run.fNumGlyphs);
1240 }
1241 handler->commitLine();
1242}
1243
1244class HBLockedFaceCache {
1245public:
1246 HBLockedFaceCache(SkLRUCache<SkTypefaceID, HBFont>& lruCache, SkMutex& mutex)
1247 : fLRUCache(lruCache), fMutex(mutex)
1248 {
1249 fMutex.acquire();
1250 }
1251 HBLockedFaceCache(const HBLockedFaceCache&) = delete;
1252 HBLockedFaceCache& operator=(const HBLockedFaceCache&) = delete;
1253 HBLockedFaceCache& operator=(HBLockedFaceCache&&) = delete;
1254
1255 ~HBLockedFaceCache() {
1256 fMutex.release();
1257 }
1258
1259 HBFont* find(SkTypefaceID fontId) {
1260 return fLRUCache.find(fontId);
1261 }
1262 HBFont* insert(SkTypefaceID fontId, HBFont hbFont) {
1263 return fLRUCache.insert(fontId, std::move(hbFont));
1264 }
1265 void reset() {
1266 fLRUCache.reset();
1267 }
1268private:
1270 SkMutex& fMutex;
1271};
1272static HBLockedFaceCache get_hbFace_cache() {
1273 static SkMutex gHBFaceCacheMutex;
1274 static SkLRUCache<SkTypefaceID, HBFont> gHBFaceCache(100);
1275 return HBLockedFaceCache(gHBFaceCache, gHBFaceCacheMutex);
1276}
1277
1278ShapedRun ShaperHarfBuzz::shape(char const * const utf8,
1279 size_t const utf8Bytes,
1280 char const * const utf8Start,
1281 char const * const utf8End,
1282 const BiDiRunIterator& bidi,
1283 const LanguageRunIterator& language,
1284 const ScriptRunIterator& script,
1285 const FontRunIterator& font,
1286 Feature const * const features, size_t const featuresSize) const
1287{
1288 size_t utf8runLength = utf8End - utf8Start;
1289 ShapedRun run(RunHandler::Range(utf8Start - utf8, utf8runLength),
1290 font.currentFont(), bidi.currentLevel(), nullptr, 0);
1291
1292 hb_buffer_t* buffer = fBuffer.get();
1294 hb_buffer_set_content_type(buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
1295 hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
1296
1297 // Documentation for HB_BUFFER_FLAG_BOT/EOT at 763e5466c0a03a7c27020e1e2598e488612529a7.
1298 // Currently BOT forces a dotted circle when first codepoint is a mark; EOT has no effect.
1299 // Avoid adding dotted circle, re-evaluate if BOT/EOT change. See https://skbug.com/9618.
1300 // hb_buffer_set_flags(buffer, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
1301
1302 // Add precontext.
1303 hb_buffer_add_utf8(buffer, utf8, utf8Start - utf8, utf8Start - utf8, 0);
1304
1305 // Populate the hb_buffer directly with utf8 cluster indexes.
1306 const char* utf8Current = utf8Start;
1307 while (utf8Current < utf8End) {
1308 unsigned int cluster = utf8Current - utf8;
1309 hb_codepoint_t u = utf8_next(&utf8Current, utf8End);
1310 hb_buffer_add(buffer, u, cluster);
1311 }
1312
1313 // Add postcontext.
1314 hb_buffer_add_utf8(buffer, utf8Current, utf8 + utf8Bytes - utf8Current, 0, 0);
1315
1316 hb_direction_t direction = is_LTR(bidi.currentLevel()) ? HB_DIRECTION_LTR:HB_DIRECTION_RTL;
1317 hb_buffer_set_direction(buffer, direction);
1318 hb_buffer_set_script(buffer, hb_script_from_iso15924_tag((hb_tag_t)script.currentScript()));
1319 // Buffers with HB_LANGUAGE_INVALID race since hb_language_get_default is not thread safe.
1320 // The user must provide a language, but may provide data hb_language_from_string cannot use.
1321 // Use "und" for the undefined language in this case (RFC5646 4.1 5).
1322 hb_language_t hbLanguage = hb_language_from_string(language.currentLanguage(), -1);
1323 if (hbLanguage == HB_LANGUAGE_INVALID) {
1324 hbLanguage = fUndefinedLanguage;
1325 }
1326 hb_buffer_set_language(buffer, hbLanguage);
1327 hb_buffer_guess_segment_properties(buffer);
1328
1329 // TODO: better cache HBFace (data) / hbfont (typeface)
1330 // An HBFace is expensive (it sanitizes the bits).
1331 // An HBFont is fairly inexpensive.
1332 // An HBFace is actually tied to the data, not the typeface.
1333 // The size of 100 here is completely arbitrary and used to match libtxt.
1334 HBFont hbFont;
1335 {
1336 HBLockedFaceCache cache = get_hbFace_cache();
1337 SkTypefaceID dataId = font.currentFont().getTypeface()->uniqueID();
1338 HBFont* typefaceFontCached = cache.find(dataId);
1339 if (!typefaceFontCached) {
1340 HBFont typefaceFont(create_typeface_hb_font(*font.currentFont().getTypeface()));
1341 typefaceFontCached = cache.insert(dataId, std::move(typefaceFont));
1342 }
1343 hbFont = create_sub_hb_font(font.currentFont(), *typefaceFontCached);
1344 }
1345 if (!hbFont) {
1346 return run;
1347 }
1348
1349 STArray<32, hb_feature_t> hbFeatures;
1350 for (const auto& feature : SkSpan(features, featuresSize)) {
1351 if (feature.end < SkTo<size_t>(utf8Start - utf8) ||
1352 SkTo<size_t>(utf8End - utf8) <= feature.start)
1353 {
1354 continue;
1355 }
1356 if (feature.start <= SkTo<size_t>(utf8Start - utf8) &&
1357 SkTo<size_t>(utf8End - utf8) <= feature.end)
1358 {
1359 hbFeatures.push_back({ (hb_tag_t)feature.tag, feature.value,
1361 } else {
1362 hbFeatures.push_back({ (hb_tag_t)feature.tag, feature.value,
1363 SkTo<unsigned>(feature.start), SkTo<unsigned>(feature.end)});
1364 }
1365 }
1366
1367 hb_shape(hbFont.get(), buffer, hbFeatures.data(), hbFeatures.size());
1368 unsigned len = hb_buffer_get_length(buffer);
1369 if (len == 0) {
1370 return run;
1371 }
1372
1373 if (direction == HB_DIRECTION_RTL) {
1374 // Put the clusters back in logical order.
1375 // Note that the advances remain ltr.
1376 hb_buffer_reverse(buffer);
1377 }
1378 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, nullptr);
1379 hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(buffer, nullptr);
1380
1381 run = ShapedRun(RunHandler::Range(utf8Start - utf8, utf8runLength),
1382 font.currentFont(), bidi.currentLevel(),
1383 std::unique_ptr<ShapedGlyph[]>(new ShapedGlyph[len]), len);
1384
1385 // Undo skhb_position with (1.0/(1<<16)) and scale as needed.
1386 AutoSTArray<32, SkGlyphID> glyphIDs(len);
1387 for (unsigned i = 0; i < len; i++) {
1388 glyphIDs[i] = info[i].codepoint;
1389 }
1390 AutoSTArray<32, SkRect> glyphBounds(len);
1391 SkPaint p;
1392 run.fFont.getBounds(glyphIDs.get(), len, glyphBounds.get(), &p);
1393
1394 double SkScalarFromHBPosX = +(1.52587890625e-5) * run.fFont.getScaleX();
1395 double SkScalarFromHBPosY = -(1.52587890625e-5); // HarfBuzz y-up, Skia y-down
1396 SkVector runAdvance = { 0, 0 };
1397 for (unsigned i = 0; i < len; i++) {
1398 ShapedGlyph& glyph = run.fGlyphs[i];
1399 glyph.fID = info[i].codepoint;
1400 glyph.fCluster = info[i].cluster;
1401 glyph.fOffset.fX = pos[i].x_offset * SkScalarFromHBPosX;
1402 glyph.fOffset.fY = pos[i].y_offset * SkScalarFromHBPosY;
1403 glyph.fAdvance.fX = pos[i].x_advance * SkScalarFromHBPosX;
1404 glyph.fAdvance.fY = pos[i].y_advance * SkScalarFromHBPosY;
1405
1406 glyph.fHasVisual = !glyphBounds[i].isEmpty(); //!font->currentTypeface()->glyphBoundsAreZero(glyph.fID);
1407#if SK_HB_VERSION_CHECK(1, 5, 0)
1408 glyph.fUnsafeToBreak = info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
1409#else
1410 glyph.fUnsafeToBreak = false;
1411#endif
1412 glyph.fMustLineBreakBefore = false;
1413
1414 runAdvance += glyph.fAdvance;
1415 }
1416 run.fAdvance = runAdvance;
1417
1418 return run;
1419}
1420} // namespace
1421
1422#if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
1423
1424#if defined(SK_UNICODE_ICU_IMPLEMENTATION)
1426#endif
1427
1428#if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION)
1430#endif
1431
1432#if defined(SK_UNICODE_ICU4X_IMPLEMENTATION)
1434#endif
1435
1437#if defined(SK_UNICODE_ICU_IMPLEMENTATION)
1438 if (auto unicode = SkUnicodes::ICU::Make()) {
1439 return unicode;
1440 }
1441#endif // defined(SK_UNICODE_ICU_IMPLEMENTATION)
1442#if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION)
1443 if (auto unicode = SkUnicodes::Libgrapheme::Make()) {
1444 return unicode;
1445 }
1446#endif
1447#if defined(SK_UNICODE_ICU4X_IMPLEMENTATION)
1448 if (auto unicode = SkUnicodes::ICU4X::Make()) {
1449 return unicode;
1450 }
1451#endif
1452 return nullptr;
1453}
1454
1455std::unique_ptr<SkShaper::ScriptRunIterator>
1456SkShaper::MakeHbIcuScriptRunIterator(const char* utf8, size_t utf8Bytes) {
1457 return SkShapers::HB::ScriptRunIterator(utf8, utf8Bytes);
1458}
1459
1460std::unique_ptr<SkShaper::ScriptRunIterator>
1461SkShaper::MakeSkUnicodeHbScriptRunIterator(const char* utf8, size_t utf8Bytes) {
1462 return SkShapers::HB::ScriptRunIterator(utf8, utf8Bytes);
1463}
1464
1465std::unique_ptr<SkShaper::ScriptRunIterator> SkShaper::MakeSkUnicodeHbScriptRunIterator(
1466 const char* utf8, size_t utf8Bytes, SkFourByteTag script) {
1467 return SkShapers::HB::ScriptRunIterator(utf8, utf8Bytes, script);
1468}
1469
1470std::unique_ptr<SkShaper> SkShaper::MakeShaperDrivenWrapper(sk_sp<SkFontMgr> fontmgr) {
1472}
1473
1474std::unique_ptr<SkShaper> SkShaper::MakeShapeThenWrap(sk_sp<SkFontMgr> fontmgr) {
1475 return SkShapers::HB::ShapeThenWrap(get_unicode(), fontmgr);
1476}
1477
1478void SkShaper::PurgeHarfBuzzCache() { SkShapers::HB::PurgeCaches(); }
1479#endif // !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
1480
1481namespace SkShapers::HB {
1482std::unique_ptr<SkShaper> ShaperDrivenWrapper(sk_sp<SkUnicode> unicode,
1483 sk_sp<SkFontMgr> fallback) {
1484 if (!unicode) {
1485 return nullptr;
1486 }
1487 HBBuffer buffer(hb_buffer_create());
1488 if (!buffer) {
1489 SkDEBUGF("Could not create hb_buffer");
1490 return nullptr;
1491 }
1492 return std::make_unique<::ShaperDrivenWrapper>(
1493 unicode, std::move(buffer), std::move(fallback));
1494}
1495
1496std::unique_ptr<SkShaper> ShapeThenWrap(sk_sp<SkUnicode> unicode,
1497 sk_sp<SkFontMgr> fallback) {
1498 if (!unicode) {
1499 return nullptr;
1500 }
1501 HBBuffer buffer(hb_buffer_create());
1502 if (!buffer) {
1503 SkDEBUGF("Could not create hb_buffer");
1504 return nullptr;
1505 }
1506 return std::make_unique<::ShapeThenWrap>(
1507 unicode, std::move(buffer), std::move(fallback));
1508}
1509
1510std::unique_ptr<SkShaper> ShapeDontWrapOrReorder(sk_sp<SkUnicode> unicode,
1511 sk_sp<SkFontMgr> fallback) {
1512 if (!unicode) {
1513 return nullptr;
1514 }
1515 HBBuffer buffer(hb_buffer_create());
1516 if (!buffer) {
1517 SkDEBUGF("Could not create hb_buffer");
1518 return nullptr;
1519 }
1520 return std::make_unique<::ShapeDontWrapOrReorder>(
1521 unicode, std::move(buffer), std::move(fallback));
1522}
1523
1524std::unique_ptr<SkShaper::ScriptRunIterator> ScriptRunIterator(const char* utf8, size_t utf8Bytes) {
1525 return std::make_unique<SkUnicodeHbScriptRunIterator>(utf8, utf8Bytes, HB_SCRIPT_UNKNOWN);
1526}
1527std::unique_ptr<SkShaper::ScriptRunIterator> ScriptRunIterator(const char* utf8,
1528 size_t utf8Bytes,
1529 SkFourByteTag script) {
1530 return std::make_unique<SkUnicodeHbScriptRunIterator>(
1531 utf8, utf8Bytes, hb_script_from_iso15924_tag((hb_tag_t)script));
1532}
1533
1535 HBLockedFaceCache cache = get_hbFace_cache();
1536 cache.reset();
1537}
1538} // namespace SkShapers::HB
static void done(const char *config, const char *src, const char *srcOptions, const char *name)
Definition DM.cpp:263
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
m reset()
uint16_t glyphs[5]
int count
SkPoint pos
static float next(float f)
static float prev(float f)
#define SK_ABORT(message,...)
Definition SkAssert.h:70
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkDEBUGF(...)
Definition SkDebug.h:24
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
@ kUTF32
uses four byte words to represent all of Unicode
SK_API void sk_free(void *)
static void * sk_malloc_throw(size_t size)
Definition SkMalloc.h:67
bool operator!=(const sk_sp< T > &a, const sk_sp< U > &b)
Definition SkRefCnt.h:355
static void SkSafeUnref(T *obj)
Definition SkRefCnt.h:149
static T * SkRef(T *obj)
Definition SkRefCnt.h:132
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SK_ScalarNegativeInfinity
Definition SkScalar.h:27
static SkUnichar utf8_next(const char **ptr, const char *end)
Definition SkShaper.cpp:84
#define HB_FEATURE_GLOBAL_END
static sk_sp< SkUnicode > get_unicode()
#define HB_FEATURE_GLOBAL_START
void sk_ignore_unused_variable(const T &)
Definition SkTemplates.h:37
constexpr unsigned SkToUInt(S x)
Definition SkTo.h:30
uint32_t SkTypefaceID
Definition SkTypeface.h:38
int32_t SkUnichar
Definition SkTypes.h:175
uint16_t SkGlyphID
Definition SkTypes.h:179
uint32_t SkFourByteTag
Definition SkTypes.h:166
static constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d)
Definition SkTypes.h:167
int find(T *array, int N, T item)
uint8_t Level
Definition SkUnicode.h:46
virtual Position next()=0
virtual bool isDone()=0
void * writable_data()
Definition SkData.h:52
size_t size() const
Definition SkData.h:30
virtual void commitLine()=0
Definition shape.cpp:201
virtual void commitRunBuffer(const RunInfo &)=0
Definition shape.cpp:176
virtual void commitRunInfo()=0
Definition shape.cpp:150
virtual void runInfo(const RunInfo &)=0
Definition shape.cpp:142
virtual void beginLine()=0
Definition shape.cpp:135
virtual Buffer runBuffer(const RunInfo &)=0
Definition shape.cpp:154
virtual bool atEnd() const =0
virtual void consume()=0
virtual size_t endOfCurrentRun() const =0
virtual SkFourByteTag currentScript() const =0
virtual void shape(const char *utf8, size_t utf8Bytes, const SkFont &srcFont, bool leftToRight, SkScalar width, RunHandler *) const =0
bool equals(const SkString &) const
Definition SkString.cpp:324
const char * c_str() const
Definition SkString.h:133
std::unique_ptr< SkStreamAsset > openExistingStream(int *ttcIndex) const
sk_sp< SkData > copyTableData(SkFontTableTag tag) const
int getUnitsPerEm() const
int getVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
int size() const
Definition SkTArray.h:416
T & emplace_back(Args &&... args)
Definition SkTArray.h:243
static void append(char **dst, size_t *count, const char *src, size_t n)
Definition editor.cpp:211
bool operator==(const FlutterPoint &a, const FlutterPoint &b)
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct a[10]
glong glong end
static const uint8_t buffer[]
uint8_t value
SKSHAPER_API std::unique_ptr< SkShaper > ShapeDontWrapOrReorder(sk_sp< SkUnicode > unicode, sk_sp< SkFontMgr > fallback)
SKSHAPER_API std::unique_ptr< SkShaper > ShapeThenWrap(sk_sp< SkUnicode > unicode, sk_sp< SkFontMgr > fallback)
SKSHAPER_API std::unique_ptr< SkShaper > ShaperDrivenWrapper(sk_sp< SkUnicode > unicode, sk_sp< SkFontMgr > fallback)
SKSHAPER_API void PurgeCaches()
SKSHAPER_API std::unique_ptr< SkShaper::ScriptRunIterator > ScriptRunIterator(const char *utf8, size_t utf8Bytes)
SKSHAPER_API std::unique_ptr< SkShaper::BiDiRunIterator > BidiRunIterator(sk_sp< SkUnicode > unicode, const char *utf8, size_t utf8Bytes, uint8_t bidiLevel)
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
Definition SkUTF.cpp:118
SKUNICODE_API sk_sp< SkUnicode > Make()
SKUNICODE_API sk_sp< SkUnicode > Make()
SKUNICODE_API sk_sp< SkUnicode > Make()
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition switches.h:191
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port fallback
Definition switches.h:154
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
font
Font Metadata and Metrics.
Definition run.py:1
Definition ref_ptr.h:256
static sk_sp< SkUnicode > get_unicode()
int32_t width
const Scalar scale
static constexpr SkPoint Make(float x, float y)
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
void roundOut(SkIRect *dst) const
Definition SkRect.h:1241
constexpr float height() const
Definition SkRect.h:769
constexpr float width() const
Definition SkRect.h:762
void set(const SkIRect &src)
Definition SkRect.h:849
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15