10#include <unordered_set>
17void OneLineShaper::commitRunBuffer(
const RunInfo&) {
19 fCurrentRun->commit();
21 auto oldUnresolvedCount = fUnresolvedBlocks.size();
30 if (block.width() == 0) {
33 addUnresolvedWithRun(block);
37 if (oldUnresolvedCount == fUnresolvedBlocks.size()) {
41 }
else if (oldUnresolvedCount == fUnresolvedBlocks.size() - 1) {
42 auto& unresolved = fUnresolvedBlocks.back();
43 if (fCurrentRun->textRange() == unresolved.fText) {
45 auto& front = fUnresolvedBlocks.front();
46 if (front.fRun !=
nullptr) {
47 unresolved.fRun = front.fRun;
48 unresolved.fGlyphs = front.fGlyphs;
54 fillGaps(oldUnresolvedCount);
58void OneLineShaper::printState() {
59 SkDebugf(
"Resolved: %zu\n", fResolvedBlocks.size());
60 for (
auto& resolved : fResolvedBlocks) {
61 if (resolved.fRun ==
nullptr) {
63 resolved.fText.start, resolved.fText.end);
67 if (resolved.fRun->fFont.getTypeface() !=
nullptr) {
68 resolved.fRun->fFont.getTypeface()->getFamilyName(&
name);
70 SkDebugf(
"[%zu:%zu) ", resolved.fGlyphs.start, resolved.fGlyphs.end);
72 resolved.fText.start, resolved.fText.end,
76 auto size = fUnresolvedBlocks.size();
78 for (
const auto& unresolved : fUnresolvedBlocks) {
79 SkDebugf(
"[%zu:%zu)\n", unresolved.fText.start, unresolved.fText.end);
84void OneLineShaper::fillGaps(
size_t startingCount) {
86 TextRange resolvedTextLimits = fCurrentRun->fTextRange;
87 if (!fCurrentRun->leftToRight()) {
88 std::swap(resolvedTextLimits.start, resolvedTextLimits.end);
90 TextIndex resolvedTextStart = resolvedTextLimits.start;
93 auto begin = fUnresolvedBlocks.begin();
94 auto end = fUnresolvedBlocks.end();
95 begin += startingCount;
98 auto& unresolved = *
begin;
100 if (unresolved.fText == prevText) {
105 prevText = unresolved.fText;
108 TextRange resolvedText(resolvedTextStart, fCurrentRun->leftToRight() ? unresolved.fText.start : unresolved.fText.end);
109 if (resolvedText.width() > 0) {
110 if (!fCurrentRun->leftToRight()) {
111 std::swap(resolvedText.start, resolvedText.end);
114 GlyphRange resolvedGlyphs(resolvedGlyphsStart, unresolved.fGlyphs.start);
115 RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
117 if (resolvedGlyphs.width() == 0) {
119 if (unresolved.fText.end <= resolved.fText.start) {
120 unresolved.fText.end = resolved.fText.end;
122 if (unresolved.fText.start >= resolved.fText.end) {
123 unresolved.fText.start = resolved.fText.start;
126 fResolvedBlocks.emplace_back(resolved);
129 resolvedGlyphsStart = unresolved.fGlyphs.end;
130 resolvedTextStart = fCurrentRun->leftToRight()
131 ? unresolved.fText.end
132 : unresolved.fText.start;
135 TextRange resolvedText(resolvedTextStart,resolvedTextLimits.end);
136 if (resolvedText.width() > 0) {
137 if (!fCurrentRun->leftToRight()) {
138 std::swap(resolvedText.start, resolvedText.end);
141 GlyphRange resolvedGlyphs(resolvedGlyphsStart, fCurrentRun->size());
142 RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
143 fResolvedBlocks.emplace_back(resolved);
148 auto blockText = block.fRange;
151 while (!fUnresolvedBlocks.empty()) {
152 auto unresolved = fUnresolvedBlocks.front();
153 fUnresolvedBlocks.pop_front();
154 if (unresolved.fText.width() == 0) {
157 fResolvedBlocks.emplace_back(unresolved);
158 fUnresolvedGlyphs += unresolved.fGlyphs.width();
163 std::sort(fResolvedBlocks.begin(), fResolvedBlocks.end(),
164 [](
const RunBlock&
a,
const RunBlock&
b) {
165 return a.fText.start < b.fText.start;
169 size_t lastTextEnd = blockText.start;
170 for (
auto& resolvedBlock : fResolvedBlocks) {
172 if (resolvedBlock.fText.end <= blockText.start) {
176 if (resolvedBlock.fRun !=
nullptr) {
177 fParagraph->fFontSwitches.emplace_back(resolvedBlock.fText.start, resolvedBlock.fRun->fFont);
180 auto run = resolvedBlock.fRun;
181 auto glyphs = resolvedBlock.fGlyphs;
182 auto text = resolvedBlock.fText;
183 if (lastTextEnd !=
text.start) {
184 SkDEBUGF(
"Text ranges mismatch: ...:%zu] - [%zu:%zu] (%zu-%zu)\n",
188 lastTextEnd =
text.end;
190 if (resolvedBlock.isFullyResolved()) {
192 resolvedBlock.fRun->fIndex = this->fParagraph->fRuns.size();
193 this->fParagraph->fRuns.emplace_back(*resolvedBlock.fRun);
194 resolvedBlock.fRun.reset();
196 }
else if (
run ==
nullptr) {
208 this->fParagraph->fRuns.emplace_back(
213 block.fStyle.getHalfLeading(),
214 block.fStyle.getBaselineShift(),
215 this->fParagraph->fRuns.size(),
218 auto piece = &this->fParagraph->fRuns.back();
227 piece->fGlyphs[index] =
run->fGlyphs[
i];
233 piece->fClusterIndexes[index] =
run->fClusterIndexes[
i];
235 piece->fPositions[index] =
run->fPositions[
i] - zero;
236 piece->fOffsets[index] =
run->fOffsets[
i];
237 piece->addX(index, advanceX);
241 fAdvance.
fX += runAdvance.fX;
245 advanceX = fAdvance.
fX;
246 if (lastTextEnd != blockText.end) {
247 SkDEBUGF(
"Last range mismatch: %zu - %zu\n", lastTextEnd, blockText.end);
255 if (fCurrentRun->leftToRight()) {
256 return TextRange(clusterIndex(glyphRange.start), clusterIndex(glyphRange.end));
258 return TextRange(clusterIndex(glyphRange.end - 1),
260 ? clusterIndex(glyphRange.start - 1)
261 : fCurrentRun->fTextRange.end);
265void OneLineShaper::addFullyResolved() {
266 if (this->fCurrentRun->size() == 0) {
269 RunBlock resolved(fCurrentRun,
270 this->fCurrentRun->fTextRange,
272 this->fCurrentRun->size());
273 fResolvedBlocks.emplace_back(resolved);
276void OneLineShaper::addUnresolvedWithRun(
GlyphRange glyphRange) {
277 auto extendedText = this->clusteredText(glyphRange);
278 RunBlock unresolved(fCurrentRun, extendedText, glyphRange, 0);
279 if (unresolved.fGlyphs.width() == fCurrentRun->size()) {
280 SkASSERT(unresolved.fText.width() == fCurrentRun->fTextRange.width());
281 }
else if (!fUnresolvedBlocks.empty()) {
282 auto& lastUnresolved = fUnresolvedBlocks.back();
283 if (lastUnresolved.fRun !=
nullptr &&
284 lastUnresolved.fRun->fIndex == fCurrentRun->fIndex) {
286 if (lastUnresolved.fText.end == unresolved.fText.start) {
288 lastUnresolved.fText.end = unresolved.fText.end;
289 lastUnresolved.fGlyphs.end = glyphRange.end;
291 }
else if(lastUnresolved.fText == unresolved.fText) {
294 }
else if (lastUnresolved.fText.contains(unresolved.fText)) {
297 }
else if (lastUnresolved.fText.intersects(unresolved.fText)) {
299 lastUnresolved.fGlyphs.start =
std::min(lastUnresolved.fGlyphs.start, glyphRange.start);
300 lastUnresolved.fGlyphs.end =
std::max(lastUnresolved.fGlyphs.end, glyphRange.end);
301 lastUnresolved.fText = this->clusteredText(lastUnresolved.fGlyphs);
306 fUnresolvedBlocks.emplace_back(unresolved);
314 bool graphemeResolved =
false;
316 for (
size_t i = 0;
i < fCurrentRun->size(); ++
i) {
322 auto glyph = fCurrentRun->fGlyphs[
i];
325 if ((fCurrentRun->leftToRight() ? gi > graphemeStart : gi < graphemeStart) || graphemeStart ==
EMPTY_INDEX) {
331 graphemeResolved = glyph != 0 || isControl8;
333 }
else if (glyph == 0) {
335 graphemeResolved =
false;
338 if (!graphemeResolved) {
352 sortOutUnresolvedBLock(block);
360 block.
end = fCurrentRun->size();
361 sortOutUnresolvedBLock(block);
365void OneLineShaper::iterateThroughFontStyles(
TextRange textRange,
367 const ShapeSingleFontVisitor& visitor) {
371 auto addFeatures = [&features](
const Block& block) {
372 for (
auto& ff : block.fStyle.getFontFeatures()) {
373 if (ff.fName.size() != 4) {
374 SkDEBUGF(
"Incorrect font feature: %s=%d\n", ff.fName.c_str(), ff.fValue);
386 if (block.fStyle.getLetterSpacing() > 0) {
388 SkSetFourByteTag(
'l',
'i',
'g',
'a'), 0, block.fRange.start, block.fRange.end
393 for (
auto& block : styleSpan) {
395 if (blockRange.empty()) {
398 SkASSERT(combinedBlock.fRange.width() == 0 || combinedBlock.fRange.end == block.fRange.start);
400 if (!combinedBlock.fRange.empty()) {
401 if (block.fStyle.matchOneAttribute(StyleType::kFont, combinedBlock.fStyle)) {
402 combinedBlock.add(blockRange);
407 visitor(combinedBlock, features);
410 combinedBlock.fRange = blockRange;
411 combinedBlock.fStyle = block.fStyle;
416 visitor(combinedBlock, features);
422void OneLineShaper::matchResolvedFonts(
const TextStyle& textStyle,
423 const TypefaceVisitor& visitor) {
424 std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->
fFontCollection->findTypefaces(textStyle.getFontFamilies(), textStyle.getFontStyle(), textStyle.getFontArguments());
426 for (
const auto& typeface : typefaces) {
427 if (visitor(typeface) == Resolved::Everything) {
436 std::vector<RunBlock> hopelessBlocks;
437 while (!fUnresolvedBlocks.empty()) {
438 auto unresolvedRange = fUnresolvedBlocks.front().fText;
439 auto unresolvedText = fParagraph->
text(unresolvedRange);
440 const char* ch = unresolvedText.
begin();
446 if (ch == unresolvedText.end()) {
448 hopelessBlocks.push_back(fUnresolvedBlocks.front());
449 fUnresolvedBlocks.pop_front();
457 while (ch != unresolvedText.end()) {
459 fParagraph->fUnicode.
get(),
461 unresolvedText.end());
462 if (emojiStart != -1) {
467 if (!alreadyTriedCodepoints.
contains(codepoint)) {
468 alreadyTriedCodepoints.
add(codepoint);
474 SkASSERT(codepoint != -1 || emojiStart != -1);
477 if (emojiStart == -1) {
479 FontKey fontKey(codepoint, textStyle.getFontStyle(), textStyle.getLocale());
480 auto found = fFallbackFonts.find(fontKey);
481 if (found !=
nullptr) {
484 if (typeface ==
nullptr) {
487 textStyle.getFontStyle(),
488 textStyle.getLocale());
489 if (typeface !=
nullptr) {
490 fFallbackFonts.set(fontKey, typeface);
496 textStyle.getFontStyle(),
497 textStyle.getLocale());
500 if (typeface ==
nullptr) {
513 auto resolvedBlocksBefore = fResolvedBlocks.size();
514 auto resolved = visitor(typeface);
515 if (resolved == Resolved::Everything) {
516 if (hopelessBlocks.empty()) {
519 }
else if (resolvedBlocksBefore < fResolvedBlocks.size()) {
521 resolved = Resolved::Something;
524 resolved = Resolved::Nothing;
528 if (resolved == Resolved::Something) {
536 for (
auto& block : hopelessBlocks) {
537 fUnresolvedBlocks.emplace_front(block);
542bool OneLineShaper::iterateThroughShapingRegions(
const ShapeVisitor& shape) {
544 size_t bidiIndex = 0;
547 for (
auto& placeholder : fParagraph->fPlaceholders) {
549 if (placeholder.fTextBefore.width() > 0) {
551 while (bidiIndex < fParagraph->fBidiRegions.size()) {
559 if (!blockRange.empty()) {
563 if (!
shape(textRange, styleSpan, advanceX, start, bidiRegion.
level)) {
568 if (end == bidiRegion.
end) {
576 if (placeholder.fRange.width() == 0) {
581 std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->
fFontCollection->findTypefaces(
582 placeholder.fTextStyle.getFontFamilies(),
583 placeholder.fTextStyle.getFontStyle(),
584 placeholder.fTextStyle.getFontArguments());
586 SkFont font(typeface, placeholder.fTextStyle.getFontSize());
589 uint8_t bidiLevel = (bidiIndex < fParagraph->fBidiRegions.size())
590 ? fParagraph->fBidiRegions[bidiIndex].level
595 SkPoint::Make(placeholder.fStyle.fWidth, placeholder.fStyle.fHeight),
599 auto&
run = fParagraph->fRuns.emplace_back(this->fParagraph,
601 placeholder.fRange.start,
605 fParagraph->fRuns.size(),
608 run.fPositions[0] = { advanceX, 0 };
609 run.fOffsets[0] = {0, 0};
610 run.fClusterIndexes[0] = 0;
611 run.fPlaceholderIndex = &placeholder - fParagraph->fPlaceholders.begin();
612 advanceX += placeholder.fStyle.fWidth;
617bool OneLineShaper::shape() {
623 auto result = iterateThroughShapingRegions(
624 [
this, limitlessWidth]
630 if (shaper ==
nullptr) {
635 iterateThroughFontStyles(textRange, styleSpan,
636 [
this, &shaper, defaultBidiLevel, limitlessWidth, &advanceX]
645 fCurrentText = block.
fRange;
646 fUnresolvedBlocks.emplace_back(RunBlock(block.
fRange));
651 SkFont font(std::move(typeface), block.fStyle.getFontSize());
652 font.setEdging(SkFont::Edging::kAntiAlias);
653 font.setHinting(SkFontHinting::kSlight);
654 font.setSubpixel(true);
658 int wantedWeight = block.fStyle.getFontStyle().weight();
660 wantedWeight >= SkFontStyle::kSemiBold_Weight &&
661 wantedWeight - font.getTypeface()->fontStyle().weight() >= 200;
663 block.fStyle.getFontStyle().slant() == SkFontStyle::kItalic_Slant &&
664 font.getTypeface()->fontStyle().slant() != SkFontStyle::kItalic_Slant;
665 font.setEmbolden(fakeBold);
666 font.setSkewX(fakeItalic ? -SK_Scalar1 / 4 : 0);
670 auto resolvedCount = fResolvedBlocks.size();
671 auto unresolvedCount = fUnresolvedBlocks.size();
672 while (unresolvedCount-- > 0) {
673 auto unresolvedRange = fUnresolvedBlocks.front().fText;
674 if (unresolvedRange == EMPTY_TEXT) {
676 fUnresolvedBlocks.pop_front();
679 auto unresolvedText = fParagraph->text(unresolvedRange);
681 SkShaper::TrivialFontRunIterator fontIter(font, unresolvedText.size());
682 LangIterator langIter(unresolvedText, blockSpan,
683 fParagraph->paragraphStyle().getTextStyle());
684 SkShaper::TrivialBiDiRunIterator bidiIter(defaultBidiLevel, unresolvedText.size());
685 auto scriptIter = SkShapers::HB::ScriptRunIterator(unresolvedText.begin(),
686 unresolvedText.size());
687 fCurrentText = unresolvedRange;
690 TArray<SkShaper::Feature> adjustedFeatures(features.size());
691 for (const SkShaper::Feature& feature : features) {
692 SkRange<size_t> featureRange(feature.start, feature.end);
693 if (unresolvedRange.intersects(featureRange)) {
694 SkRange<size_t> adjustedRange = unresolvedRange.intersection(featureRange);
695 adjustedRange.Shift(-static_cast<std::make_signed_t<size_t>>(unresolvedRange.start));
696 adjustedFeatures.push_back({feature.tag, feature.value, adjustedRange.start, adjustedRange.end});
700 shaper->shape(unresolvedText.begin(), unresolvedText.size(),
701 fontIter, bidiIter,*scriptIter, langIter,
702 adjustedFeatures.data(), adjustedFeatures.size(),
703 limitlessWidth, this);
707 fUnresolvedBlocks.pop_front();
710 if (fUnresolvedBlocks.empty()) {
713 return Resolved::Everything;
714 }
else if (resolvedCount < fResolvedBlocks.size()) {
715 return Resolved::Something;
717 return Resolved::Nothing;
721 this->finish(block, fHeight, advanceX);
734 enum class Pos { inclusive, exclusive };
740 while (index < fCurrentRun->fTextRange.end) {
741 if (this->fParagraph->codeUnitHasProperty(index,
742 SkUnicode::CodeUnitFlags::kGraphemeStart)) {
747 return fCurrentRun->fTextRange.end;
749 while (index > fCurrentRun->fTextRange.start) {
750 if (this->fParagraph->codeUnitHasProperty(index,
751 SkUnicode::CodeUnitFlags::kGraphemeStart)) {
756 return fCurrentRun->fTextRange.start;
761 textRange.start = findBaseChar(textRange.start,
Dir::left);
762 textRange.end = findBaseChar(textRange.end,
Dir::right);
766 if (fCurrentRun->leftToRight()) {
767 while (
glyphs.start > 0 && clusterIndex(
glyphs.start) > textRange.start) {
770 while (
glyphs.end < fCurrentRun->size() && clusterIndex(
glyphs.end) < textRange.end) {
774 while (
glyphs.start > 0 && clusterIndex(
glyphs.start - 1) < textRange.end) {
777 while (
glyphs.end < fCurrentRun->size() && clusterIndex(
glyphs.end) > textRange.start) {
782 return { textRange.start, textRange.end };
786 return fUnicode == other.fUnicode && fFontStyle == other.fFontStyle && fLocale == other.fLocale;
789uint32_t OneLineShaper::FontKey::Hasher::operator()(
const OneLineShaper::FontKey&
key)
const {
809 if (!
unicode->isEmoji(codepoint1)) {
814 if (!
unicode->isEmojiComponent(codepoint1)) {
821 const char* last =
next;
825 if (
unicode->isRegionalIndicator(codepoint2)) {
827 if (
unicode->isRegionalIndicator(codepoint2)) {
837 if (codepoint2 == 0xFE0F) {
839 if (codepoint3 == 0x20E3) {
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
static float next(float f)
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static std::vector< SkPDFIndirectReference > sort(const THashSet< SkPDFIndirectReference > &src)
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
void swap(sk_sp< T > &a, sk_sp< T > &b)
constexpr uint32_t SkToU32(S x)
static constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d)
static sk_sp< SkFontMgr > RefEmpty()
constexpr T * begin() const
SkTypefaceID uniqueID() const
static SkUnichar getEmojiSequenceStart(SkUnicode *unicode, const char **begin, const char *end)
bool codeUnitHasProperty(size_t index, SkUnicode::CodeUnitFlags property) const
SkSpan< const char > text() const
SkSpan< Block > blocks(BlockRange blockRange)
void addUnresolvedCodepoints(TextRange textRange)
BlockRange findAllBlocks(TextRange textRange)
TextIndex findPreviousGraphemeBoundary(TextIndex utf8) const
sk_sp< FontCollection > fFontCollection
bool getHeightOverride() const
bool getHalfLeading() const
SkScalar getHeight() const
SkScalar getBaselineShift() const
T & emplace_back(Args &&... args)
bool contains(const T &item) const
static const char * begin(const StringSlice &s)
Dart_NativeFunction function
static float max(float r, float g, float b)
static float min(float r, float g, float b)
SKSHAPER_API std::unique_ptr< SkShaper > ShapeDontWrapOrReorder(sk_sp< SkUnicode > unicode, sk_sp< SkFontMgr > fallback)
SK_SPI SkUnichar NextUTF8WithReplacement(const char **ptr, const char *end)
DEF_SWITCHES_START aot vmservice shared library name
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 dir
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
bool operator==(C p1, const scoped_nsprotocol< C > &p2)
font
Font Metadata and Metrics.
const SkRange< size_t > EMPTY_TEXT
const SkRange< size_t > EMPTY_RANGE
SkRange< size_t > TextRange
SkRange< GlyphIndex > GlyphRange
SkRange< size_t > BlockRange
static constexpr SkPoint Make(float x, float y)