• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Google LLC.
2 
3 #include "modules/skparagraph/src/Iterators.h"
4 #include "modules/skparagraph/src/OneLineShaper.h"
5 #include "src/Run.h"
6 #include "src/utils/SkUTF.h"
7 
8 #include <algorithm>
9 #include <cstdint>
10 #include <unordered_set>
11 
12 
nextUtf8Unit(const char ** ptr,const char * end)13 static inline SkUnichar nextUtf8Unit(const char** ptr, const char* end) {
14     SkUnichar val = SkUTF::NextUTF8(ptr, end);
15     return val < 0 ? 0xFFFD : val;
16 }
17 
18 namespace skia {
19 namespace textlayout {
20 
commitRunBuffer(const RunInfo &)21 void OneLineShaper::commitRunBuffer(const RunInfo&) {
22 
23     fCurrentRun->commit();
24 
25     auto oldUnresolvedCount = fUnresolvedBlocks.size();
26 /*
27     SkDebugf("Run [%zu:%zu)\n", fCurrentRun->fTextRange.start, fCurrentRun->fTextRange.end);
28     for (size_t i = 0; i < fCurrentRun->size(); ++i) {
29         SkDebugf("[%zu] %hu %u %f\n", i, fCurrentRun->fGlyphs[i], fCurrentRun->fClusterIndexes[i], fCurrentRun->fPositions[i].fX);
30     }
31 */
32     // Find all unresolved blocks
33     sortOutGlyphs([&](GlyphRange block){
34         if (block.width() == 0) {
35             return;
36         }
37         addUnresolvedWithRun(block);
38     });
39 
40     // Fill all the gaps between unresolved blocks with resolved ones
41     if (oldUnresolvedCount == fUnresolvedBlocks.size()) {
42         // No unresolved blocks added - we resolved the block with one run entirely
43         addFullyResolved();
44         return;
45     } else if (oldUnresolvedCount == fUnresolvedBlocks.size() - 1) {
46         auto& unresolved = fUnresolvedBlocks.back();
47         if (fCurrentRun->textRange() == unresolved.fText) {
48             // Nothing was resolved; preserve the initial run if it makes sense
49             auto& front = fUnresolvedBlocks.front();
50             if (front.fRun != nullptr) {
51                unresolved.fRun = front.fRun;
52                unresolved.fGlyphs = front.fGlyphs;
53             }
54             return;
55         }
56     }
57 
58     fillGaps(oldUnresolvedCount);
59 }
60 
61 #ifdef SK_DEBUG
printState()62 void OneLineShaper::printState() {
63     SkDebugf("Resolved: %zu\n", fResolvedBlocks.size());
64     for (auto& resolved : fResolvedBlocks) {
65         if (resolved.fRun ==  nullptr) {
66             SkDebugf("[%zu:%zu) unresolved\n",
67                     resolved.fText.start, resolved.fText.end);
68             continue;
69         }
70         SkString name("???");
71         if (resolved.fRun->fFont.getTypeface() != nullptr) {
72             resolved.fRun->fFont.getTypeface()->getFamilyName(&name);
73         }
74         SkDebugf("[%zu:%zu) ", resolved.fGlyphs.start, resolved.fGlyphs.end);
75         SkDebugf("[%zu:%zu) with %s\n",
76                 resolved.fText.start, resolved.fText.end,
77                 name.c_str());
78     }
79 
80     auto size = fUnresolvedBlocks.size();
81     SkDebugf("Unresolved: %zu\n", size);
82     for (const auto& unresolved : fUnresolvedBlocks) {
83         SkDebugf("[%zu:%zu)\n", unresolved.fText.start, unresolved.fText.end);
84     }
85 }
86 #endif
87 
fillGaps(size_t startingCount)88 void OneLineShaper::fillGaps(size_t startingCount) {
89     // Fill out gaps between all unresolved blocks
90     TextRange resolvedTextLimits = fCurrentRun->fTextRange;
91     if (!fCurrentRun->leftToRight()) {
92         std::swap(resolvedTextLimits.start, resolvedTextLimits.end);
93     }
94     TextIndex resolvedTextStart = resolvedTextLimits.start;
95     GlyphIndex resolvedGlyphsStart = 0;
96 
97     auto begin = fUnresolvedBlocks.begin();
98     auto end = fUnresolvedBlocks.end();
99     begin += startingCount; // Skip the old ones, do the new ones
100     TextRange prevText = EMPTY_TEXT;
101     for (; begin != end; ++begin) {
102         auto& unresolved = *begin;
103 
104         if (unresolved.fText == prevText) {
105             // Clean up repetitive blocks that appear inside the same grapheme block
106             unresolved.fText = EMPTY_TEXT;
107             continue;
108         } else {
109             prevText = unresolved.fText;
110         }
111 
112         TextRange resolvedText(resolvedTextStart, fCurrentRun->leftToRight() ? unresolved.fText.start : unresolved.fText.end);
113         if (resolvedText.width() > 0) {
114             if (!fCurrentRun->leftToRight()) {
115                 std::swap(resolvedText.start, resolvedText.end);
116             }
117 
118             GlyphRange resolvedGlyphs(resolvedGlyphsStart, unresolved.fGlyphs.start);
119             RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
120 
121             if (resolvedGlyphs.width() == 0) {
122                 // Extend the unresolved block with an empty resolved
123                 if (unresolved.fText.end <= resolved.fText.start) {
124                     unresolved.fText.end = resolved.fText.end;
125                 }
126                 if (unresolved.fText.start >= resolved.fText.end) {
127                     unresolved.fText.start = resolved.fText.start;
128                 }
129             } else {
130                 fResolvedBlocks.emplace_back(resolved);
131             }
132         }
133         resolvedGlyphsStart = unresolved.fGlyphs.end;
134         resolvedTextStart =  fCurrentRun->leftToRight()
135                                 ? unresolved.fText.end
136                                 : unresolved.fText.start;
137     }
138 
139     TextRange resolvedText(resolvedTextStart,resolvedTextLimits.end);
140     if (resolvedText.width() > 0) {
141         if (!fCurrentRun->leftToRight()) {
142             std::swap(resolvedText.start, resolvedText.end);
143         }
144 
145         GlyphRange resolvedGlyphs(resolvedGlyphsStart, fCurrentRun->size());
146         RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
147         fResolvedBlocks.emplace_back(resolved);
148     }
149 }
150 
151 #ifdef OHOS_SUPPORT
152 // 1. glyphs in run between [glyphStart, glyphEnd) are all equal to zero.
153 // 2. run is nullptr.
154 // 3. charStart flag has kCombine.
155 // one of the above conditions is met, will return true.
156 // anything else, return false.
isUnresolvedCombineGlyphRange(std::shared_ptr<Run> run,size_t glyphStart,size_t glyphEnd,size_t charStart) const157 bool OneLineShaper::isUnresolvedCombineGlyphRange(std::shared_ptr<Run> run, size_t glyphStart, size_t glyphEnd,
158     size_t charStart) const
159 {
160     if (run == nullptr ||
161         (fParagraph != nullptr && !fParagraph->codeUnitHasProperty(charStart, SkUnicode::kCombine))) {
162         return true;
163     }
164     size_t iterGlyphEnd = std::min(run->fGlyphs.size(), glyphEnd);
165     for (size_t glyph = glyphStart; glyph < iterGlyphEnd; ++glyph) {
166         if (run->fGlyphs[glyph] != 0) {
167             return false;
168         }
169     }
170 
171     return true;
172 }
173 
174 // split unresolvedBlock.
175 // extract resolvedBlock(which isUnresolvedGlyphRange is false), emplace back to stagedUnresolvedBlocks.
176 // the rest block emplace back to fUnresolvedBlocks without run.
splitUnresolvedBlockAndStageResolvedSubBlock(std::deque<RunBlock> & stagedUnresolvedBlocks,const RunBlock & unresolvedBlock)177 void OneLineShaper::splitUnresolvedBlockAndStageResolvedSubBlock(
178     std::deque<RunBlock>& stagedUnresolvedBlocks, const RunBlock& unresolvedBlock)
179 {
180     if (unresolvedBlock.fRun == nullptr) {
181         return;
182     }
183     std::shared_ptr<Run> run = unresolvedBlock.fRun;
184     bool hasUnresolvedText = false;
185     size_t curTextStart = EMPTY_INDEX;
186     size_t curGlyphEdge = EMPTY_INDEX;
187     run->iterateGlyphRangeInTextOrder(unresolvedBlock.fGlyphs,
188         [&stagedUnresolvedBlocks, &hasUnresolvedText, &curTextStart, &curGlyphEdge, run, this]
189         (size_t glyphStart, size_t glyphEnd, size_t charStart, size_t charEnd) {
190             if (!isUnresolvedCombineGlyphRange(run, glyphStart, glyphEnd, charStart)) {
191                 if (curTextStart == EMPTY_INDEX) {
192                     curTextStart = charStart;
193                 }
194                 if (curGlyphEdge == EMPTY_INDEX) {
195                     curGlyphEdge = run->leftToRight() ? glyphStart : glyphEnd;
196                 }
197                 return;
198             }
199             hasUnresolvedText = true;
200             this->fUnresolvedBlocks.emplace_back(RunBlock(TextRange(charStart, charEnd)));
201             if (curTextStart == EMPTY_INDEX) {
202                 return;
203             }
204             if (run->leftToRight()) {
205                 stagedUnresolvedBlocks.emplace_back(
206                     run, TextRange(curTextStart, charStart), GlyphRange(curGlyphEdge, glyphStart), 0);
207             } else {
208                 stagedUnresolvedBlocks.emplace_back(
209                     run, TextRange(curTextStart, charStart), GlyphRange(glyphEnd, curGlyphEdge), 0);
210             }
211             curTextStart = EMPTY_INDEX;
212             curGlyphEdge = EMPTY_INDEX;
213         });
214     if (!hasUnresolvedText) {
215         stagedUnresolvedBlocks.emplace_back(unresolvedBlock);
216         return;
217     }
218     if (curTextStart == EMPTY_INDEX) {
219         return;
220     }
221     if (run->leftToRight()) {
222         stagedUnresolvedBlocks.emplace_back(run, TextRange(curTextStart, unresolvedBlock.fText.end),
223             GlyphRange(curGlyphEdge, unresolvedBlock.fGlyphs.end), 0);
224     } else {
225         stagedUnresolvedBlocks.emplace_back(run, TextRange(curTextStart, unresolvedBlock.fText.end),
226             GlyphRange(unresolvedBlock.fGlyphs.start, curGlyphEdge), 0);
227     }
228 }
229 
230 // shape unresolved text separately.
shapeUnresolvedTextSeparatelyFromUnresolvedBlock(const TextStyle & textStyle,const TypefaceVisitor & visitor)231 void OneLineShaper::shapeUnresolvedTextSeparatelyFromUnresolvedBlock(
232     const TextStyle& textStyle, const TypefaceVisitor& visitor)
233 {
234     if (fUnresolvedBlocks.empty()) {
235         return;
236     }
237     std::deque<OneLineShaper::RunBlock> stagedUnresolvedBlocks;
238     size_t unresolvedBlockCount = fUnresolvedBlocks.size();
239     while (unresolvedBlockCount-- > 0) {
240         RunBlock unresolvedBlock = fUnresolvedBlocks.front();
241         fUnresolvedBlocks.pop_front();
242 
243         if (unresolvedBlock.fText.width() <= 1 || unresolvedBlock.fRun == nullptr) {
244             stagedUnresolvedBlocks.emplace_back(unresolvedBlock);
245             continue;
246         }
247 
248         splitUnresolvedBlockAndStageResolvedSubBlock(stagedUnresolvedBlocks, unresolvedBlock);
249     }
250 
251     this->matchResolvedFonts(textStyle, visitor);
252 
253     while (!stagedUnresolvedBlocks.empty()) {
254         auto block = stagedUnresolvedBlocks.front();
255         stagedUnresolvedBlocks.pop_front();
256         fUnresolvedBlocks.emplace_back(block);
257     }
258 }
259 #endif
260 
finish(const Block & block,SkScalar height,SkScalar & advanceX)261 void OneLineShaper::finish(const Block& block, SkScalar height, SkScalar& advanceX) {
262     auto blockText = block.fRange;
263 
264     // Add all unresolved blocks to resolved blocks
265     while (!fUnresolvedBlocks.empty()) {
266         auto unresolved = fUnresolvedBlocks.front();
267         fUnresolvedBlocks.pop_front();
268         if (unresolved.fText.width() == 0) {
269             continue;
270         }
271         fResolvedBlocks.emplace_back(unresolved);
272         fUnresolvedGlyphs += unresolved.fGlyphs.width();
273         fParagraph->addUnresolvedCodepoints(unresolved.fText);
274     }
275 
276     // Sort all pieces by text
277     std::sort(fResolvedBlocks.begin(), fResolvedBlocks.end(),
278               [](const RunBlock& a, const RunBlock& b) {
279                 return a.fText.start < b.fText.start;
280               });
281 
282     // Go through all of them
283     size_t lastTextEnd = blockText.start;
284     for (auto& resolvedBlock : fResolvedBlocks) {
285 
286         if (resolvedBlock.fText.end <= blockText.start) {
287             continue;
288         }
289 
290         if (resolvedBlock.fRun != nullptr) {
291             fParagraph->fFontSwitches.emplace_back(resolvedBlock.fText.start, resolvedBlock.fRun->fFont);
292         }
293 
294         auto run = resolvedBlock.fRun;
295         auto glyphs = resolvedBlock.fGlyphs;
296         auto text = resolvedBlock.fText;
297         if (lastTextEnd != text.start) {
298             SkDEBUGF("Text ranges mismatch: ...:%zu] - [%zu:%zu] (%zu-%zu)\n",
299                      lastTextEnd, text.start, text.end,  glyphs.start, glyphs.end);
300             SkASSERT(false);
301         }
302         lastTextEnd = text.end;
303 
304         if (resolvedBlock.isFullyResolved()) {
305             // Just move the entire run
306             resolvedBlock.fRun->fIndex = this->fParagraph->fRuns.size();
307             this->fParagraph->fRuns.emplace_back(*resolvedBlock.fRun);
308             resolvedBlock.fRun.reset();
309             continue;
310         } else if (run == nullptr) {
311             continue;
312         }
313 
314         auto runAdvance = SkVector::Make(run->posX(glyphs.end) - run->posX(glyphs.start), run->fAdvance.fY);
315         const SkShaper::RunHandler::RunInfo info = {
316                 run->fFont,
317                 run->fBidiLevel,
318                 runAdvance,
319                 glyphs.width(),
320                 SkShaper::RunHandler::Range(text.start - run->fClusterStart, text.width())
321         };
322         this->fParagraph->fRuns.emplace_back(
323                     this->fParagraph,
324                     info,
325                     run->fClusterStart,
326                     height,
327                     block.fStyle.getHalfLeading(),
328                     block.fStyle.getBaselineShift(),
329                     this->fParagraph->fRuns.size(),
330                     advanceX
331                 );
332         auto piece = &this->fParagraph->fRuns.back();
333 
334         // TODO: Optimize copying
335         SkPoint zero = {run->fPositions[glyphs.start].fX, 0};
336         for (size_t i = glyphs.start; i <= glyphs.end; ++i) {
337 
338             auto index = i - glyphs.start;
339             if (i < glyphs.end) {
340                 piece->fGlyphs[index] = run->fGlyphs[i];
341             }
342             piece->fClusterIndexes[index] = run->fClusterIndexes[i];
343             piece->fPositions[index] = run->fPositions[i] - zero;
344             piece->fOffsets[index] = run->fOffsets[i];
345             piece->addX(index, advanceX);
346         }
347 
348         // Carve out the line text out of the entire run text
349         fAdvance.fX += runAdvance.fX;
350         fAdvance.fY = std::max(fAdvance.fY, runAdvance.fY);
351     }
352 
353     advanceX = fAdvance.fX;
354     if (lastTextEnd != blockText.end) {
355         SkDEBUGF("Last range mismatch: %zu - %zu\n", lastTextEnd, blockText.end);
356         SkASSERT(false);
357     }
358 }
359 
360 // Make it [left:right) regardless of a text direction
normalizeTextRange(GlyphRange glyphRange)361 TextRange OneLineShaper::normalizeTextRange(GlyphRange glyphRange) {
362 
363     if (fCurrentRun->leftToRight()) {
364         return TextRange(clusterIndex(glyphRange.start), clusterIndex(glyphRange.end));
365     } else {
366         return TextRange(clusterIndex(glyphRange.end - 1),
367                 glyphRange.start > 0
368                 ? clusterIndex(glyphRange.start - 1)
369                 : fCurrentRun->fTextRange.end);
370     }
371 }
372 
addFullyResolved()373 void OneLineShaper::addFullyResolved() {
374     if (this->fCurrentRun->size() == 0) {
375         return;
376     }
377     RunBlock resolved(fCurrentRun,
378                       this->fCurrentRun->fTextRange,
379                       GlyphRange(0, this->fCurrentRun->size()),
380                       this->fCurrentRun->size());
381     fResolvedBlocks.emplace_back(resolved);
382 }
383 
addUnresolvedWithRun(GlyphRange glyphRange)384 void OneLineShaper::addUnresolvedWithRun(GlyphRange glyphRange) {
385     auto extendedText = this->clusteredText(glyphRange); // It also modifies glyphRange if needed
386     RunBlock unresolved(fCurrentRun, extendedText, glyphRange, 0);
387     if (unresolved.fGlyphs.width() == fCurrentRun->size()) {
388         SkASSERT(unresolved.fText.width() == fCurrentRun->fTextRange.width());
389     } else if (fUnresolvedBlocks.size() > 0) {
390         auto& lastUnresolved = fUnresolvedBlocks.back();
391         if (lastUnresolved.fRun != nullptr &&
392             lastUnresolved.fRun->fIndex == fCurrentRun->fIndex) {
393 
394             if (lastUnresolved.fText.end == unresolved.fText.start) {
395               // Two pieces next to each other - can join them
396               lastUnresolved.fText.end = unresolved.fText.end;
397               lastUnresolved.fGlyphs.end = glyphRange.end;
398               return;
399             } else if(lastUnresolved.fText == unresolved.fText) {
400                 // Nothing was resolved; ignore it
401                 return;
402             } else if (lastUnresolved.fText.contains(unresolved.fText)) {
403                 // We get here for the very first unresolved piece
404                 return;
405             } else if (lastUnresolved.fText.intersects(unresolved.fText)) {
406                 // Few pieces of the same unresolved text block can ignore the second one
407                 lastUnresolved.fGlyphs.start = std::min(lastUnresolved.fGlyphs.start, glyphRange.start);
408                 lastUnresolved.fGlyphs.end = std::max(lastUnresolved.fGlyphs.end, glyphRange.end);
409                 lastUnresolved.fText = this->clusteredText(lastUnresolved.fGlyphs);
410                 return;
411             }
412         }
413     }
414     fUnresolvedBlocks.emplace_back(unresolved);
415 }
416 
417 // Glue whitespaces to the next/prev unresolved blocks
418 // (so we don't have chinese text with english whitespaces broken into millions of tiny runs)
sortOutGlyphs(std::function<void (GlyphRange)> && sortOutUnresolvedBLock)419 void OneLineShaper::sortOutGlyphs(std::function<void(GlyphRange)>&& sortOutUnresolvedBLock) {
420     GlyphRange block = EMPTY_RANGE;
421     bool graphemeResolved = false;
422     TextIndex graphemeStart = EMPTY_INDEX;
423     for (size_t i = 0; i < fCurrentRun->size(); ++i) {
424 
425         ClusterIndex ci = clusterIndex(i);
426         // Removing all pretty optimizations for whitespaces
427         // because they get in a way of grapheme rounding
428         // Inspect the glyph
429         auto glyph = fCurrentRun->fGlyphs[i];
430 
431         GraphemeIndex gi = fParagraph->findPreviousGraphemeBoundary(ci);
432         if ((fCurrentRun->leftToRight() ? gi > graphemeStart : gi < graphemeStart) || graphemeStart == EMPTY_INDEX) {
433             // This is the Flutter change
434             // Do not count control codepoints as unresolved
435             bool isControl8 = fParagraph->codeUnitHasProperty(ci,
436                                                               SkUnicode::CodeUnitFlags::kControl);
437             // We only count glyph resolved if all the glyphs in its grapheme are resolved
438             graphemeResolved = glyph != 0 || isControl8;
439             graphemeStart = gi;
440         } else if (glyph == 0) {
441             // Found unresolved glyph - the entire grapheme is unresolved now
442             graphemeResolved = false;
443         }
444 
445         if (!graphemeResolved) { // Unresolved glyph and not control codepoint
446             if (block.start == EMPTY_INDEX) {
447                 // Start new unresolved block
448                 block.start = i;
449                 block.end = EMPTY_INDEX;
450             } else {
451                 // Keep skipping unresolved block
452             }
453         } else { // Resolved glyph or control codepoint
454             if (block.start == EMPTY_INDEX) {
455                 // Keep skipping resolved code points
456             } else {
457                 // This is the end of unresolved block
458                 block.end = i;
459                 sortOutUnresolvedBLock(block);
460                 block = EMPTY_RANGE;
461             }
462         }
463     }
464 
465     // One last block could have been left
466     if (block.start != EMPTY_INDEX) {
467         block.end = fCurrentRun->size();
468         sortOutUnresolvedBLock(block);
469     }
470 }
471 
generateBlockRange(const Block & block,const TextRange & textRange)472 BlockRange OneLineShaper::generateBlockRange(const Block& block, const TextRange& textRange)
473 {
474     size_t start = std::max(block.fRange.start, textRange.start);
475     size_t end = std::min(block.fRange.end, textRange.end);
476     if (fParagraph->fParagraphStyle.getMaxLines() == 1 &&
477         fParagraph->fParagraphStyle.getEllipsisMod() == EllipsisModal::MIDDLE &&
478         !fParagraph->getEllipsisState()) {
479         end = fParagraph->fText.size();
480     }
481     return BlockRange(start, end);
482 }
483 
iterateThroughFontStyles(TextRange textRange,SkSpan<Block> styleSpan,const ShapeSingleFontVisitor & visitor)484 void OneLineShaper::iterateThroughFontStyles(TextRange textRange,
485                                              SkSpan<Block> styleSpan,
486                                              const ShapeSingleFontVisitor& visitor) {
487     Block combinedBlock;
488     SkTArray<SkShaper::Feature> features;
489 
490     auto addFeatures = [&features](const Block& block) {
491         for (auto& ff : block.fStyle.getFontFeatures()) {
492             if (ff.fName.size() != 4) {
493                 SkDEBUGF("Incorrect font feature: %s=%d\n", ff.fName.c_str(), ff.fValue);
494                 continue;
495             }
496             SkShaper::Feature feature = {
497                 SkSetFourByteTag(ff.fName[0], ff.fName[1], ff.fName[2], ff.fName[3]),
498                 SkToU32(ff.fValue),
499                 block.fRange.start,
500                 block.fRange.end
501             };
502             features.emplace_back(feature);
503         }
504         // Disable ligatures if letter spacing is enabled.
505         if (block.fStyle.getLetterSpacing() > 0) {
506             features.emplace_back(SkShaper::Feature{
507                 SkSetFourByteTag('l', 'i', 'g', 'a'), 0, block.fRange.start, block.fRange.end
508             });
509         }
510     };
511 
512     for (auto& block : styleSpan) {
513         BlockRange blockRange = generateBlockRange(block, textRange);
514         if (blockRange.empty()) {
515             continue;
516         }
517         SkASSERT(combinedBlock.fRange.width() == 0 || combinedBlock.fRange.end == block.fRange.start);
518 
519         if (!combinedBlock.fRange.empty()) {
520             if (block.fStyle.matchOneAttribute(StyleType::kFont, combinedBlock.fStyle)) {
521                 combinedBlock.add(blockRange);
522                 addFeatures(block);
523                 continue;
524             }
525             // Resolve all characters in the block for this style
526             visitor(combinedBlock, features);
527         }
528 
529         combinedBlock.fRange = blockRange;
530         combinedBlock.fStyle = block.fStyle;
531         features.reset();
532         addFeatures(block);
533     }
534 
535     visitor(combinedBlock, features);
536 #ifdef SK_DEBUG
537     //printState();
538 #endif
539 }
540 
matchResolvedFonts(const TextStyle & textStyle,const TypefaceVisitor & visitor)541 void OneLineShaper::matchResolvedFonts(const TextStyle& textStyle,
542                                        const TypefaceVisitor& visitor) {
543 #ifndef USE_SKIA_TXT
544     std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(textStyle.getFontFamilies(), textStyle.getFontStyle(), textStyle.getFontArguments());
545 #else
546     std::vector<std::shared_ptr<RSTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(
547         textStyle.getFontFamilies(), textStyle.getFontStyle(), textStyle.getFontArguments());
548 #endif
549 
550     for (const auto& typeface : typefaces) {
551         if (visitor(typeface) == Resolved::Everything) {
552             // Resolved everything
553             return;
554         }
555     }
556 
557     if (fParagraph->fFontCollection->fontFallbackEnabled()) {
558         // Give fallback a clue
559         // Some unresolved subblocks might be resolved with different fallback fonts
560         std::vector<RunBlock> hopelessBlocks;
561         while (!fUnresolvedBlocks.empty()) {
562             auto unresolvedRange = fUnresolvedBlocks.front().fText;
563             auto unresolvedText = fParagraph->text(unresolvedRange);
564             const char* ch = unresolvedText.begin();
565             // We have the global cache for all already found typefaces for SkUnichar
566             // but we still need to keep track of all SkUnichars used in this unresolved block
567             SkTHashSet<SkUnichar> alreadyTriedCodepoints;
568             SkTHashSet<uint32_t> alreadyTriedTypefaces;
569             while (true) {
570 
571                 if (ch == unresolvedText.end()) {
572                     // Not a single codepoint could be resolved but we finished the block
573                     hopelessBlocks.push_back(fUnresolvedBlocks.front());
574                     fUnresolvedBlocks.pop_front();
575                     break;
576                 }
577 
578                 // See if we can switch to the next DIFFERENT codepoint
579                 SkUnichar unicode = -1;
580                 while (ch != unresolvedText.end()) {
581                     unicode = nextUtf8Unit(&ch, unresolvedText.end());
582                     if (!alreadyTriedCodepoints.contains(unicode)) {
583                         alreadyTriedCodepoints.add(unicode);
584                         break;
585                     }
586                 }
587                 SkASSERT(unicode != -1);
588 
589                 // First try to find in in a cache
590 #ifndef USE_SKIA_TXT
591                 sk_sp<SkTypeface> typeface;
592 #else
593                 std::shared_ptr<RSTypeface> typeface;
594 #endif
595                 FontKey fontKey(unicode, textStyle.getFontStyle(), textStyle.getLocale());
596                 auto found = fFallbackFonts.find(fontKey);
597 #ifndef USE_SKIA_TXT
598                 if (found != nullptr) {
599                     typeface = *found;
600 #else
601                 if (found != fFallbackFonts.end()) {
602                     typeface = found->second;
603 #endif
604                 } else {
605                     typeface = fParagraph->fFontCollection->defaultFallback(
606                             unicode, textStyle.getFontStyle(), textStyle.getLocale());
607 
608                     if (typeface == nullptr) {
609                         // There is no fallback font for this character, so move on to the next character.
610                         continue;
611                     }
612 #ifndef USE_SKIA_TXT
613                     fFallbackFonts.set(fontKey, typeface);
614 #else
615                     fFallbackFonts.emplace(fontKey, typeface);
616 #endif
617                 }
618 
619                 // Check if we already tried this font on this text range
620 #ifndef USE_SKIA_TXT
621                 if (!alreadyTriedTypefaces.contains(typeface->uniqueID())) {
622                     alreadyTriedTypefaces.add(typeface->uniqueID());
623 #else
624                 if (!alreadyTriedTypefaces.contains(typeface->GetUniqueID())) {
625                     alreadyTriedTypefaces.add(typeface->GetUniqueID());
626 #endif
627                 } else {
628                     continue;
629                 }
630 
631                 if (typeface && textStyle.getFontArguments()) {
632                     typeface = fParagraph->fFontCollection->CloneTypeface(typeface, textStyle.getFontArguments());
633                 }
634 
635                 auto resolvedBlocksBefore = fResolvedBlocks.size();
636                 auto resolved = visitor(typeface);
637                 if (resolved == Resolved::Everything) {
638                     if (hopelessBlocks.empty()) {
639                         // Resolved everything, no need to try another font
640                         return;
641                     } else if (resolvedBlocksBefore < fResolvedBlocks.size()) {
642                         // There are some resolved blocks
643                         resolved = Resolved::Something;
644                     } else {
645                         // All blocks are hopeless
646                         resolved = Resolved::Nothing;
647                     }
648                 }
649 
650                 if (resolved == Resolved::Something) {
651                     // Resolved something, no need to try another codepoint
652                     break;
653                 }
654             }
655         }
656 
657         // Return hopeless blocks back
658         for (auto& block : hopelessBlocks) {
659             fUnresolvedBlocks.emplace_front(block);
660         }
661     }
662 }
663 
664 bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) {
665 
666     size_t bidiIndex = 0;
667 
668     SkScalar advanceX = 0;
669     for (auto& placeholder : fParagraph->fPlaceholders) {
670 
671         if (placeholder.fTextBefore.width() > 0) {
672             // Shape the text by bidi regions
673             while (bidiIndex < fParagraph->fBidiRegions.size()) {
674                 SkUnicode::BidiRegion& bidiRegion = fParagraph->fBidiRegions[bidiIndex];
675                 auto start = std::max(bidiRegion.start, placeholder.fTextBefore.start);
676                 auto end = std::min(bidiRegion.end, placeholder.fTextBefore.end);
677                 if (fParagraph->fParagraphStyle.getMaxLines() == 1
678                     && fParagraph->fParagraphStyle.getEllipsisMod() == EllipsisModal::MIDDLE
679                     && !fParagraph->getEllipsisState()) {
680                     end = fParagraph->fText.size();
681                 }
682 
683                 // Set up the iterators (the style iterator points to a bigger region that it could
684                 TextRange textRange(start, end);
685                 auto blockRange = fParagraph->findAllBlocks(textRange);
686                 if (!blockRange.empty()) {
687                     SkSpan<Block> styleSpan(fParagraph->blocks(blockRange));
688 
689                     // Shape the text between placeholders
690                     if (!shape(textRange, styleSpan, advanceX, start, bidiRegion.level)) {
691                         return false;
692                     }
693                 }
694 
695                 if (end == bidiRegion.end) {
696                     ++bidiIndex;
697                 } else /*if (end == placeholder.fTextBefore.end)*/ {
698                     break;
699                 }
700             }
701         }
702 
703         if (placeholder.fRange.width() == 0) {
704             continue;
705         }
706 
707         // Get the placeholder font
708         auto typefaces = fParagraph->fFontCollection->findTypefaces(
709             placeholder.fTextStyle.getFontFamilies(),
710             placeholder.fTextStyle.getFontStyle(),
711             placeholder.fTextStyle.getFontArguments());
712         auto typeface = typefaces.size() ? typefaces.front() : nullptr;
713 #ifndef USE_SKIA_TXT
714         SkFont font(typeface, placeholder.fTextStyle.getFontSize());
715 #else
716         RSFont font(typeface, placeholder.fTextStyle.getFontSize(), 1, 0);
717 #endif
718 
719         // "Shape" the placeholder
720         uint8_t bidiLevel = (bidiIndex < fParagraph->fBidiRegions.size())
721             ? fParagraph->fBidiRegions[bidiIndex].level
722             : 2;
723         const SkShaper::RunHandler::RunInfo runInfo = {
724             font,
725             bidiLevel,
726             SkPoint::Make(placeholder.fStyle.fWidth, placeholder.fStyle.fHeight),
727             1,
728             SkShaper::RunHandler::Range(0, placeholder.fRange.width())
729         };
730         auto& run = fParagraph->fRuns.emplace_back(this->fParagraph,
731                                        runInfo,
732                                        placeholder.fRange.start,
733                                        0.0f,
734                                        0.0f,
735                                        false,
736                                        fParagraph->fRuns.size(),
737                                        advanceX);
738 
739         run.fPositions[0] = { advanceX, 0 };
740         run.fOffsets[0] = {0, 0};
741         run.fClusterIndexes[0] = 0;
742         run.fPlaceholderIndex = &placeholder - fParagraph->fPlaceholders.begin();
743         advanceX += placeholder.fStyle.fWidth;
744     }
745     return true;
746 }
747 
748 bool OneLineShaper::shape() {
749 
750     // The text can be broken into many shaping sequences
751     // (by place holders, possibly, by hard line breaks or tabs, too)
752     auto limitlessWidth = std::numeric_limits<SkScalar>::max();
753 
754     auto result = iterateThroughShapingRegions(
755             [this, limitlessWidth]
756             (TextRange textRange, SkSpan<Block> styleSpan, SkScalar& advanceX, TextIndex textStart, uint8_t defaultBidiLevel) {
757 
758         // Set up the shaper and shape the next
759         auto shaper = SkShaper::MakeShapeDontWrapOrReorder(fParagraph->fUnicode->copy());
760         if (shaper == nullptr) {
761             // For instance, loadICU does not work. We have to stop the process
762             return false;
763         }
764 
765         iterateThroughFontStyles(textRange, styleSpan,
766                 [this, &shaper, defaultBidiLevel, limitlessWidth, &advanceX]
767                 (Block block, SkTArray<SkShaper::Feature> features) {
768             auto blockSpan = SkSpan<Block>(&block, 1);
769 
770             // Start from the beginning (hoping that it's a simple case one block - one run)
771             fHeight = block.fStyle.getHeightOverride() ? block.fStyle.getHeight() : 0;
772             fUseHalfLeading = block.fStyle.getHalfLeading();
773             fBaselineShift = block.fStyle.getBaselineShift();
774             fAdvance = SkVector::Make(advanceX, 0);
775             fCurrentText = block.fRange;
776             fUnresolvedBlocks.emplace_back(RunBlock(block.fRange));
777 
778 #ifdef OHOS_SUPPORT
779 #ifdef USE_SKIA_TXT
780             auto typefaceVisitor = [&](std::shared_ptr<RSTypeface> typeface) {
781 #else
782             auto typefaceVisitor = [&](sk_sp<SkTypeface> typeface) {
783 #endif
784 #else
785             this->matchResolvedFonts(block.fStyle, [&](sk_sp<SkTypeface> typeface) {
786 #endif
787                 // Create one more font to try
788 #ifndef USE_SKIA_TXT
789                 SkFont font(std::move(typeface), block.fStyle.getFontSize());
790                 font.setEdging(SkFont::Edging::kAntiAlias);
791                 font.setHinting(SkFontHinting::kNone);
792                 font.setSubpixel(true);
793                 font.setBaselineSnap(false);
794 #else
795                 RSFont font(std::move(typeface), block.fStyle.getFontSize(), 1, 0);
796                 font.SetEdging(RSDrawing::FontEdging::ANTI_ALIAS);
797                 font.SetHinting(RSDrawing::FontHinting::NONE);
798                 font.SetSubpixel(true);
799                 font.SetBaselineSnap(false);
800 #endif
801 
802 #ifdef OHOS_SUPPORT
803                 scaleFontWithCompressionConfig(font, ScaleOP::COMPRESS);
804 #endif
805                 // Apply fake bold and/or italic settings to the font if the
806                 // typeface's attributes do not match the intended font style.
807 #ifndef USE_SKIA_TXT
808                 int wantedWeight = block.fStyle.getFontStyle().weight();
809                 bool fakeBold =
810                     wantedWeight >= SkFontStyle::kSemiBold_Weight &&
811                     wantedWeight - font.getTypeface()->fontStyle().weight() >= 200;
812                 bool fakeItalic =
813                     block.fStyle.getFontStyle().slant() == SkFontStyle::kItalic_Slant &&
814                     font.getTypeface()->fontStyle().slant() != SkFontStyle::kItalic_Slant;
815                 font.setEmbolden(fakeBold);
816                 font.setSkewX(fakeItalic ? -SK_Scalar1 / 4 : 0);
817 #else
818                 int wantedWeight = block.fStyle.getFontStyle().GetWeight();
819                 bool fakeBold =
820                     wantedWeight >= RSFontStyle::SEMI_BOLD_WEIGHT &&
821                     wantedWeight - font.GetTypeface()->GetFontStyle().GetWeight() >= 200;
822                 bool fakeItalic =
823                     block.fStyle.getFontStyle().GetSlant() == RSFontStyle::ITALIC_SLANT &&
824                     font.GetTypeface()->GetFontStyle().GetSlant() != RSFontStyle::ITALIC_SLANT;
825                 font.SetEmbolden(fakeBold);
826                 font.SetSkewX(fakeItalic ? -SK_Scalar1 / 4 : 0);
827 #endif
828 
829                 // Walk through all the currently unresolved blocks
830                 // (ignoring those that appear later)
831                 auto resolvedCount = fResolvedBlocks.size();
832                 auto unresolvedCount = fUnresolvedBlocks.size();
833                 while (unresolvedCount-- > 0) {
834                     auto unresolvedRange = fUnresolvedBlocks.front().fText;
835                     if (unresolvedRange == EMPTY_TEXT) {
836                         // Duplicate blocks should be ignored
837                         fUnresolvedBlocks.pop_front();
838                         continue;
839                     }
840                     auto unresolvedText = fParagraph->text(unresolvedRange);
841 
842                     SkShaper::TrivialFontRunIterator fontIter(font, unresolvedText.size());
843                     LangIterator langIter(unresolvedText, blockSpan,
844                                       fParagraph->paragraphStyle().getTextStyle());
845                     SkShaper::TrivialBiDiRunIterator bidiIter(defaultBidiLevel, unresolvedText.size());
846                     auto scriptIter = SkShaper::MakeSkUnicodeHbScriptRunIterator(
847                             unresolvedText.begin(), unresolvedText.size());
848                     fCurrentText = unresolvedRange;
849 
850                     // Map the block's features to subranges within the unresolved range.
851                     SkTArray<SkShaper::Feature> adjustedFeatures(features.size());
852                     for (const SkShaper::Feature& feature : features) {
853                         SkRange<size_t> featureRange(feature.start, feature.end);
854                         if (unresolvedRange.intersects(featureRange)) {
855                             SkRange<size_t> adjustedRange = unresolvedRange.intersection(featureRange);
856                             adjustedRange.Shift(-static_cast<std::make_signed_t<size_t>>(unresolvedRange.start));
857                             adjustedFeatures.push_back({feature.tag, feature.value, adjustedRange.start, adjustedRange.end});
858                         }
859                     }
860 
861                     shaper->shape(unresolvedText.begin(), unresolvedText.size(),
862                             fontIter, bidiIter,*scriptIter, langIter,
863                             adjustedFeatures.data(), adjustedFeatures.size(),
864                             limitlessWidth, this);
865 
866                     // Take off the queue the block we tried to resolved -
867                     // whatever happened, we have now smaller pieces of it to deal with
868                     fUnresolvedBlocks.pop_front();
869                 }
870 
871                 if (fUnresolvedBlocks.empty()) {
872                     // In some cases it does not mean everything
873                     // (when we excluded some hopeless blocks from the list)
874                     return Resolved::Everything;
875                 } else if (resolvedCount < fResolvedBlocks.size()) {
876                     return Resolved::Something;
877                 } else {
878                     return Resolved::Nothing;
879                 }
880 #ifdef OHOS_SUPPORT
881             };
882             this->matchResolvedFonts(block.fStyle, typefaceVisitor);
883             this->shapeUnresolvedTextSeparatelyFromUnresolvedBlock(block.fStyle, typefaceVisitor);
884 #else
885             });
886 #endif
887             this->finish(block, fHeight, advanceX);
888         });
889 
890         return true;
891     });
892 
893     return result;
894 }
895 
896 #ifdef OHOS_SUPPORT
897 void OneLineShaper::adjustRange(GlyphRange& glyphs, TextRange& textRange) {
898     if (fCurrentRun->leftToRight()) {
899         while (glyphs.start > 0 && clusterIndex(glyphs.start) > textRange.start) {
900             glyphs.start--;
901             ClusterIndex currentIndex = clusterIndex(glyphs.start);
902             if (currentIndex < textRange.start) {
903                 textRange.start = currentIndex;
904             }
905         }
906         while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) < textRange.end) {
907             glyphs.end++;
908             ClusterIndex currentIndex = clusterIndex(glyphs.end);
909             if (currentIndex > textRange.end) {
910                 textRange.end = currentIndex;
911             }
912         }
913     } else {
914         while (glyphs.start > 0 && clusterIndex(glyphs.start - 1) < textRange.end) {
915             glyphs.start--;
916             if (glyphs.start == 0) {
917                 break;
918             }
919             ClusterIndex currentIndex = clusterIndex(glyphs.start - 1);
920             if (currentIndex > textRange.end) {
921                 textRange.end = currentIndex;
922             }
923         }
924         while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) > textRange.start) {
925             glyphs.end++;
926             ClusterIndex currentIndex = clusterIndex(glyphs.end);
927             if (currentIndex < textRange.start) {
928                 textRange.start = currentIndex;
929             }
930         }
931     }
932 }
933 #endif
934 
935 // When we extend TextRange to the grapheme edges, we also extend glyphs range
936 TextRange OneLineShaper::clusteredText(GlyphRange& glyphs) {
937 
938     enum class Dir { left, right };
939     enum class Pos { inclusive, exclusive };
940 
941     // [left: right)
942     auto findBaseChar = [&](TextIndex index, Dir dir) -> TextIndex {
943 
944         if (dir == Dir::right) {
945             while (index < fCurrentRun->fTextRange.end) {
946                 if (this->fParagraph->codeUnitHasProperty(index,
947                                                       SkUnicode::CodeUnitFlags::kGraphemeStart)) {
948                     return index;
949                 }
950                 ++index;
951             }
952             return fCurrentRun->fTextRange.end;
953         } else {
954             while (index > fCurrentRun->fTextRange.start) {
955                 if (this->fParagraph->codeUnitHasProperty(index,
956                                                       SkUnicode::CodeUnitFlags::kGraphemeStart)) {
957                     return index;
958                 }
959                 --index;
960             }
961             return fCurrentRun->fTextRange.start;
962         }
963     };
964 
965     TextRange textRange(normalizeTextRange(glyphs));
966     textRange.start = findBaseChar(textRange.start, Dir::left);
967     textRange.end = findBaseChar(textRange.end, Dir::right);
968 
969     // Correct the glyphRange in case we extended the text to the grapheme edges
970     // TODO: code it without if (as a part of LTR/RTL refactoring)
971 #ifdef OHOS_SUPPORT
972     adjustRange(glyphs, textRange);
973 #else
974     if (fCurrentRun->leftToRight()) {
975         while (glyphs.start > 0 && clusterIndex(glyphs.start) > textRange.start) {
976             glyphs.start--;
977         }
978         while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) < textRange.end) {
979             glyphs.end++;
980         }
981     } else {
982         while (glyphs.start > 0 && clusterIndex(glyphs.start - 1) < textRange.end) {
983             glyphs.start--;
984         }
985         while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) > textRange.start) {
986             glyphs.end++;
987         }
988     }
989 #endif
990 
991 
992     return { textRange.start, textRange.end };
993 }
994 
995 bool OneLineShaper::FontKey::operator==(const OneLineShaper::FontKey& other) const {
996     return fUnicode == other.fUnicode && fFontStyle == other.fFontStyle && fLocale == other.fLocale;
997 }
998 
999 uint32_t OneLineShaper::FontKey::Hasher::operator()(const OneLineShaper::FontKey& key) const {
1000     return SkGoodHash()(key.fUnicode) ^
1001            SkGoodHash()(key.fFontStyle) ^
1002            SkGoodHash()(key.fLocale);
1003 }
1004 
1005 }  // namespace textlayout
1006 }  // namespace skia
1007