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