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