• 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/utils/SkUTF.h"
6 #include <algorithm>
7 #include <unordered_set>
8 
nextUtf8Unit(const char ** ptr,const char * end)9 static inline SkUnichar nextUtf8Unit(const char** ptr, const char* end) {
10     SkUnichar val = SkUTF::NextUTF8(ptr, end);
11     return val < 0 ? 0xFFFD : val;
12 }
13 
14 namespace skia {
15 namespace textlayout {
16 
commitRunBuffer(const RunInfo &)17 void OneLineShaper::commitRunBuffer(const RunInfo&) {
18 
19     fCurrentRun->commit();
20 
21     auto oldUnresolvedCount = fUnresolvedBlocks.size();
22 /*
23     SkDebugf("Run [%d:%d)\n", fCurrentRun->fTextRange.start, fCurrentRun->fTextRange.end);
24     for (size_t i = 0; i < fCurrentRun->size(); ++i) {
25         SkDebugf("[%d] %d %d %f\n", i, fCurrentRun->fGlyphs[i], fCurrentRun->fClusterIndexes[i], fCurrentRun->fPositions[i].fX);
26     }
27 */
28     // Find all unresolved blocks
29     sortOutGlyphs([&](GlyphRange block){
30         if (block.width() == 0) {
31             return;
32         }
33         addUnresolvedWithRun(block);
34     });
35 
36     // Fill all the gaps between unresolved blocks with resolved ones
37     if (oldUnresolvedCount == fUnresolvedBlocks.size()) {
38         // No unresolved blocks added - we resolved the block with one run entirely
39         addFullyResolved();
40         return;
41     } else if (oldUnresolvedCount == fUnresolvedBlocks.size() - 1) {
42         auto& unresolved = fUnresolvedBlocks.back();
43         if (fCurrentRun->textRange() == unresolved.fText) {
44             // Nothing was resolved; preserve the initial run if it makes sense
45             auto& front = fUnresolvedBlocks.front();
46             if (front.fRun != nullptr) {
47                unresolved.fRun = front.fRun;
48                unresolved.fGlyphs = front.fGlyphs;
49             }
50             return;
51         }
52     }
53 
54     fillGaps(oldUnresolvedCount);
55 }
56 
57 #ifdef SK_DEBUG
printState()58 void OneLineShaper::printState() {
59     SkDebugf("Resolved: %d\n", fResolvedBlocks.size());
60     for (auto& resolved : fResolvedBlocks) {
61         if (resolved.fRun ==  nullptr) {
62             SkDebugf("[%d:%d) unresolved\n",
63                     resolved.fText.start, resolved.fText.end);
64             continue;
65         }
66         SkString name("???");
67         if (resolved.fRun->fFont.getTypeface() != nullptr) {
68             resolved.fRun->fFont.getTypeface()->getFamilyName(&name);
69         }
70         SkDebugf("[%d:%d) ", resolved.fGlyphs.start, resolved.fGlyphs.end);
71         SkDebugf("[%d:%d) with %s\n",
72                 resolved.fText.start, resolved.fText.end,
73                 name.c_str());
74     }
75 
76     auto size = fUnresolvedBlocks.size();
77     SkDebugf("Unresolved: %d\n", size);
78     for (const auto& unresolved : fUnresolvedBlocks) {
79         SkDebugf("[%d:%d)\n", unresolved.fText.start, unresolved.fText.end);
80     }
81 }
82 #endif
83 
fillGaps(size_t startingCount)84 void OneLineShaper::fillGaps(size_t startingCount) {
85     // Fill out gaps between all unresolved blocks
86     TextRange resolvedTextLimits = fCurrentRun->fTextRange;
87     if (!fCurrentRun->leftToRight()) {
88         std::swap(resolvedTextLimits.start, resolvedTextLimits.end);
89     }
90     TextIndex resolvedTextStart = resolvedTextLimits.start;
91     GlyphIndex resolvedGlyphsStart = 0;
92 
93     auto begin = fUnresolvedBlocks.begin();
94     auto end = fUnresolvedBlocks.end();
95     begin += startingCount; // Skip the old ones, do the new ones
96     TextRange prevText = EMPTY_TEXT;
97     for (; begin != end; ++begin) {
98         auto& unresolved = *begin;
99 
100         if (unresolved.fText == prevText) {
101             // Clean up repetitive blocks that appear inside the same grapheme block
102             unresolved.fText = EMPTY_TEXT;
103             continue;
104         } else {
105             prevText = unresolved.fText;
106         }
107 
108         TextRange resolvedText(resolvedTextStart, fCurrentRun->leftToRight() ? unresolved.fText.start : unresolved.fText.end);
109         if (resolvedText.width() > 0) {
110             if (!fCurrentRun->leftToRight()) {
111                 std::swap(resolvedText.start, resolvedText.end);
112             }
113 
114             GlyphRange resolvedGlyphs(resolvedGlyphsStart, unresolved.fGlyphs.start);
115             RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
116 
117             if (resolvedGlyphs.width() == 0) {
118                 // Extend the unresolved block with an empty resolved
119                 if (unresolved.fText.end <= resolved.fText.start) {
120                     unresolved.fText.end = resolved.fText.end;
121                 }
122                 if (unresolved.fText.start >= resolved.fText.end) {
123                     unresolved.fText.start = resolved.fText.start;
124                 }
125             } else {
126                 fResolvedBlocks.emplace_back(resolved);
127             }
128         }
129         resolvedGlyphsStart = unresolved.fGlyphs.end;
130         resolvedTextStart =  fCurrentRun->leftToRight()
131                                 ? unresolved.fText.end
132                                 : unresolved.fText.start;
133     }
134 
135     TextRange resolvedText(resolvedTextStart,resolvedTextLimits.end);
136     if (resolvedText.width() > 0) {
137         if (!fCurrentRun->leftToRight()) {
138             std::swap(resolvedText.start, resolvedText.end);
139         }
140 
141         GlyphRange resolvedGlyphs(resolvedGlyphsStart, fCurrentRun->size());
142         RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
143         fResolvedBlocks.emplace_back(resolved);
144     }
145 }
146 
finish(const Block & block,SkScalar height,SkScalar & advanceX)147 void OneLineShaper::finish(const Block& block, SkScalar height, SkScalar& advanceX) {
148     auto blockText = block.fRange;
149 
150     // Add all unresolved blocks to resolved blocks
151     while (!fUnresolvedBlocks.empty()) {
152         auto unresolved = fUnresolvedBlocks.front();
153         fUnresolvedBlocks.pop_front();
154         if (unresolved.fText.width() == 0) {
155             continue;
156         }
157         fResolvedBlocks.emplace_back(unresolved);
158         fUnresolvedGlyphs += unresolved.fGlyphs.width();
159     }
160 
161     // Sort all pieces by text
162     std::sort(fResolvedBlocks.begin(), fResolvedBlocks.end(),
163               [](const RunBlock& a, const RunBlock& b) {
164                 return a.fText.start < b.fText.start;
165               });
166 
167     // Go through all of them
168     size_t lastTextEnd = blockText.start;
169     for (auto& resolvedBlock : fResolvedBlocks) {
170 
171         if (resolvedBlock.fText.end <= blockText.start) {
172             continue;
173         }
174 
175         if (resolvedBlock.fRun != nullptr) {
176             fParagraph->fFontSwitches.emplace_back(resolvedBlock.fText.start, resolvedBlock.fRun->fFont);
177         }
178 
179         auto run = resolvedBlock.fRun;
180         auto glyphs = resolvedBlock.fGlyphs;
181         auto text = resolvedBlock.fText;
182         if (lastTextEnd != text.start) {
183             SkDEBUGF("Text ranges mismatch: ...:%d] - [%d:%d] (%d-%d)\n", lastTextEnd, text.start, text.end,  glyphs.start, glyphs.end);
184             SkASSERT(false);
185         }
186         lastTextEnd = text.end;
187 
188         if (resolvedBlock.isFullyResolved()) {
189             // Just move the entire run
190             resolvedBlock.fRun->fIndex = this->fParagraph->fRuns.size();
191             this->fParagraph->fRuns.emplace_back(*resolvedBlock.fRun);
192             resolvedBlock.fRun.reset();
193             continue;
194         } else if (run == nullptr) {
195             continue;
196         }
197 
198         auto runAdvance = SkVector::Make(run->posX(glyphs.end) - run->posX(glyphs.start), run->fAdvance.fY);
199         const SkShaper::RunHandler::RunInfo info = {
200                 run->fFont,
201                 run->fBidiLevel,
202                 runAdvance,
203                 glyphs.width(),
204                 SkShaper::RunHandler::Range(text.start - run->fClusterStart, text.width())
205         };
206         this->fParagraph->fRuns.emplace_back(
207                     this->fParagraph,
208                     info,
209                     run->fClusterStart,
210                     height,
211                     block.fStyle.getHalfLeading(),
212                     this->fParagraph->fRuns.count(),
213                     advanceX
214                 );
215         auto piece = &this->fParagraph->fRuns.back();
216 
217         // TODO: Optimize copying
218         auto zero = run->fPositions[glyphs.start];
219         for (size_t i = glyphs.start; i <= glyphs.end; ++i) {
220 
221             auto index = i - glyphs.start;
222             if (i < glyphs.end) {
223                 piece->fGlyphs[index] = run->fGlyphs[i];
224                 piece->fBounds[index] = run->fBounds[i];
225             }
226             piece->fClusterIndexes[index] = run->fClusterIndexes[i];
227             piece->fPositions[index] = run->fPositions[i] - zero;
228             piece->addX(index, advanceX);
229         }
230 
231         // Carve out the line text out of the entire run text
232         fAdvance.fX += runAdvance.fX;
233         fAdvance.fY = std::max(fAdvance.fY, runAdvance.fY);
234     }
235 
236     advanceX = fAdvance.fX;
237     if (lastTextEnd != blockText.end) {
238         SkDEBUGF("Last range mismatch: %d - %d\n", lastTextEnd, blockText.end);
239         SkASSERT(false);
240     }
241 }
242 
243 // Make it [left:right) regardless of a text direction
normalizeTextRange(GlyphRange glyphRange)244 TextRange OneLineShaper::normalizeTextRange(GlyphRange glyphRange) {
245 
246     if (fCurrentRun->leftToRight()) {
247         return TextRange(clusterIndex(glyphRange.start), clusterIndex(glyphRange.end));
248     } else {
249         return TextRange(clusterIndex(glyphRange.end - 1),
250                 glyphRange.start > 0
251                 ? clusterIndex(glyphRange.start - 1)
252                 : fCurrentRun->fTextRange.end);
253     }
254 }
255 
addFullyResolved()256 void OneLineShaper::addFullyResolved() {
257     if (this->fCurrentRun->size() == 0) {
258         return;
259     }
260     RunBlock resolved(fCurrentRun,
261                       this->fCurrentRun->fTextRange,
262                       GlyphRange(0, this->fCurrentRun->size()),
263                       this->fCurrentRun->size());
264     fResolvedBlocks.emplace_back(resolved);
265 }
266 
addUnresolvedWithRun(GlyphRange glyphRange)267 void OneLineShaper::addUnresolvedWithRun(GlyphRange glyphRange) {
268     RunBlock unresolved(fCurrentRun, clusteredText(glyphRange), glyphRange, 0);
269     if (unresolved.fGlyphs.width() == fCurrentRun->size()) {
270         SkASSERT(unresolved.fText.width() == fCurrentRun->fTextRange.width());
271     } else if (fUnresolvedBlocks.size() > 0) {
272         auto& lastUnresolved = fUnresolvedBlocks.back();
273         if (lastUnresolved.fRun != nullptr &&
274             lastUnresolved.fRun->fIndex == fCurrentRun->fIndex) {
275 
276             if (lastUnresolved.fText.end == unresolved.fText.start) {
277               // Two pieces next to each other - can join them
278               lastUnresolved.fText.end = unresolved.fText.end;
279               lastUnresolved.fGlyphs.end = glyphRange.end;
280               return;
281             } else if(lastUnresolved.fText == unresolved.fText) {
282                 // Nothing was resolved; ignore it
283                 return;
284             } else if (lastUnresolved.fText.contains(unresolved.fText)) {
285                 // We get here for the very first unresolved piece
286                 return;
287             } else if (lastUnresolved.fText.intersects(unresolved.fText)) {
288                 // Few pieces of the same unresolved text block can ignore the second one
289                 lastUnresolved.fGlyphs.start = std::min(lastUnresolved.fGlyphs.start, glyphRange.start);
290                 lastUnresolved.fGlyphs.end = std::max(lastUnresolved.fGlyphs.end, glyphRange.end);
291                 lastUnresolved.fText = clusteredText(lastUnresolved.fGlyphs);
292                 return;
293             }
294         }
295     }
296     fUnresolvedBlocks.emplace_back(unresolved);
297 }
298 
299 // Glue whitespaces to the next/prev unresolved blocks
300 // (so we don't have chinese text with english whitespaces broken into millions of tiny runs)
301 #ifndef SK_PARAGRAPH_GRAPHEME_EDGES
sortOutGlyphs(std::function<void (GlyphRange)> && sortOutUnresolvedBLock)302 void OneLineShaper::sortOutGlyphs(std::function<void(GlyphRange)>&& sortOutUnresolvedBLock) {
303 
304     auto text = fCurrentRun->fOwner->text();
305     size_t unresolvedGlyphs = 0;
306 
307     GlyphRange block = EMPTY_RANGE;
308     bool graphemeResolved = false;
309     TextIndex graphemeStart = EMPTY_INDEX;
310     for (size_t i = 0; i < fCurrentRun->size(); ++i) {
311 
312         ClusterIndex ci = clusterIndex(i);
313         // Removing all pretty optimizations for whitespaces
314         // because they get in a way of grapheme rounding
315         // Inspect the glyph
316         auto glyph = fCurrentRun->fGlyphs[i];
317 
318         GraphemeIndex gi = fParagraph->findPreviousGraphemeBoundary(ci);
319         if ((fCurrentRun->leftToRight() ? gi > graphemeStart : gi < graphemeStart) || graphemeStart == EMPTY_INDEX) {
320             // This is the Flutter change
321             // Do not count control codepoints as unresolved
322             const char* cluster = text.begin() + ci;
323             SkUnichar codepoint = nextUtf8Unit(&cluster, text.end());
324             bool isControl8 = fParagraph->getUnicode()->isControl(codepoint);
325             // We only count glyph resolved if all the glyphs in its grapheme are resolved
326             graphemeResolved = glyph != 0 || isControl8;
327             graphemeStart = gi;
328         } else if (glyph == 0) {
329             // Found unresolved glyph - the entire grapheme is unresolved now
330             graphemeResolved = false;
331         }
332 
333         if (!graphemeResolved) { // Unresolved glyph and not control codepoint
334             ++unresolvedGlyphs;
335             if (block.start == EMPTY_INDEX) {
336                 // Start new unresolved block
337                 block.start = i;
338                 block.end = EMPTY_INDEX;
339             } else {
340                 // Keep skipping unresolved block
341             }
342         } else { // Resolved glyph or control codepoint
343             if (block.start == EMPTY_INDEX) {
344                 // Keep skipping resolved code points
345             } else {
346                 // This is the end of unresolved block
347                 block.end = i;
348                 sortOutUnresolvedBLock(block);
349                 block = EMPTY_RANGE;
350             }
351         }
352     }
353 
354     // One last block could have been left
355     if (block.start != EMPTY_INDEX) {
356         block.end = fCurrentRun->size();
357         sortOutUnresolvedBLock(block);
358     }
359 }
360 #else
sortOutGlyphs(std::function<void (GlyphRange)> && sortOutUnresolvedBLock)361 void OneLineShaper::sortOutGlyphs(std::function<void(GlyphRange)>&& sortOutUnresolvedBLock) {
362 
363     auto text = fCurrentRun->fOwner->text();
364     size_t unresolvedGlyphs = 0;
365 
366     TextIndex whitespacesStart = EMPTY_INDEX;
367     GlyphRange block = EMPTY_RANGE;
368     for (size_t i = 0; i < fCurrentRun->size(); ++i) {
369 
370         const char* cluster = text.begin() + clusterIndex(i);
371         SkUnichar codepoint = nextUtf8Unit(&cluster, text.end());
372         bool isControl8 = fParagraph->getUnicode()->isControl(codepoint);
373         // TODO: This is a temp change to match space handiling in LibTxt
374         // (all spaces are resolved with the main font)
375 #ifdef SK_PARAGRAPH_LIBTXT_SPACES_RESOLUTION
376         bool isWhitespace8 = false; // fParagraph->getUnicode()->isWhitespace(codepoint);
377 #else
378         bool isWhitespace8 = fParagraph->getUnicode()->isWhitespace(codepoint);
379 #endif
380         // Inspect the glyph
381         auto glyph = fCurrentRun->fGlyphs[i];
382         if (glyph == 0 && !isControl8) { // Unresolved glyph and not control codepoint
383             ++unresolvedGlyphs;
384             if (block.start == EMPTY_INDEX) {
385                 // Start new unresolved block
386                 // (all leading whitespaces glued to the resolved part if it's not empty)
387                 block.start = whitespacesStart == 0 ? 0 : i;
388                 block.end = EMPTY_INDEX;
389             } else {
390                 // Keep skipping unresolved block
391             }
392         } else { // Resolved glyph or control codepoint
393             if (block.start == EMPTY_INDEX) {
394                 // Keep skipping resolved code points
395             } else if (isWhitespace8) {
396                 // Glue whitespaces after to the unresolved block
397                 ++unresolvedGlyphs;
398             } else {
399                 // This is the end of unresolved block (all trailing whitespaces glued to the resolved part)
400                 block.end = whitespacesStart == EMPTY_INDEX ? i : whitespacesStart;
401                 sortOutUnresolvedBLock(block);
402                 block = EMPTY_RANGE;
403                 whitespacesStart = EMPTY_INDEX;
404             }
405         }
406 
407         // Keep updated the start of the latest whitespaces patch
408         if (isWhitespace8) {
409             if (whitespacesStart == EMPTY_INDEX) {
410                 whitespacesStart = i;
411             }
412         } else {
413             whitespacesStart = EMPTY_INDEX;
414         }
415     }
416 
417     // One last block could have been left
418     if (block.start != EMPTY_INDEX) {
419         block.end = fCurrentRun->size();
420         sortOutUnresolvedBLock(block);
421     }
422 }
423 #endif
424 
iterateThroughFontStyles(TextRange textRange,SkSpan<Block> styleSpan,const ShapeSingleFontVisitor & visitor)425 void OneLineShaper::iterateThroughFontStyles(TextRange textRange,
426                                              SkSpan<Block> styleSpan,
427                                              const ShapeSingleFontVisitor& visitor) {
428     Block combinedBlock;
429     SkTArray<SkShaper::Feature> features;
430 
431     auto addFeatures = [&features](const Block& block) {
432         for (auto& ff : block.fStyle.getFontFeatures()) {
433             if (ff.fName.size() != 4) {
434                 SkDEBUGF("Incorrect font feature: %s=%d\n", ff.fName.c_str(), ff.fValue);
435                 continue;
436             }
437             SkShaper::Feature feature = {
438                 SkSetFourByteTag(ff.fName[0], ff.fName[1], ff.fName[2], ff.fName[3]),
439                 SkToU32(ff.fValue),
440                 block.fRange.start,
441                 block.fRange.end
442             };
443             features.emplace_back(feature);
444         }
445     };
446 
447     for (auto& block : styleSpan) {
448         BlockRange blockRange(std::max(block.fRange.start, textRange.start), std::min(block.fRange.end, textRange.end));
449         if (blockRange.empty()) {
450             continue;
451         }
452         SkASSERT(combinedBlock.fRange.width() == 0 || combinedBlock.fRange.end == block.fRange.start);
453 
454         if (!combinedBlock.fRange.empty()) {
455             if (block.fStyle.matchOneAttribute(StyleType::kFont, combinedBlock.fStyle)) {
456                 combinedBlock.add(blockRange);
457                 addFeatures(block);
458                 continue;
459             }
460             // Resolve all characters in the block for this style
461             visitor(combinedBlock, features);
462         }
463 
464         combinedBlock.fRange = blockRange;
465         combinedBlock.fStyle = block.fStyle;
466         features.reset();
467         addFeatures(block);
468     }
469 
470     visitor(combinedBlock, features);
471 #ifdef SK_DEBUG
472     //printState();
473 #endif
474 }
475 
matchResolvedFonts(const TextStyle & textStyle,const TypefaceVisitor & visitor)476 void OneLineShaper::matchResolvedFonts(const TextStyle& textStyle,
477                                        const TypefaceVisitor& visitor) {
478     std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(textStyle.getFontFamilies(), textStyle.getFontStyle());
479 
480     for (const auto& typeface : typefaces) {
481         if (visitor(typeface) == Resolved::Everything) {
482             // Resolved everything
483             return;
484         }
485     }
486 
487     if (fParagraph->fFontCollection->fontFallbackEnabled()) {
488         // Give fallback a clue
489         // Some unresolved subblocks might be resolved with different fallback fonts
490         while (!fUnresolvedBlocks.empty()) {
491             auto unresolvedRange = fUnresolvedBlocks.front().fText;
492             auto unresolvedText = fParagraph->text(unresolvedRange);
493             const char* ch = unresolvedText.begin();
494             // We have the global cache for all already found typefaces for SkUnichar
495             // but we still need to keep track of all SkUnichars used in this unresolved block
496             SkTHashSet<SkUnichar> alreadyTried;
497             SkUnichar unicode = nextUtf8Unit(&ch, unresolvedText.end());
498             while (true) {
499 
500                 sk_sp<SkTypeface> typeface;
501 
502                 // First try to find in in a cache
503                 FontKey fontKey(unicode, textStyle.getFontStyle(), textStyle.getLocale());
504                 auto found = fFallbackFonts.find(fontKey);
505                 if (found != nullptr) {
506                     typeface = *found;
507                 } else {
508                     typeface = fParagraph->fFontCollection->defaultFallback(
509                             unicode, textStyle.getFontStyle(), textStyle.getLocale());
510 
511                     if (typeface == nullptr) {
512                         return;
513                     }
514                     fFallbackFonts.set(fontKey, typeface);
515                 }
516 
517                 auto resolved = visitor(typeface);
518                 if (resolved == Resolved::Everything) {
519                     // Resolved everything, no need to try another font
520                     return;
521                 }
522 
523                 if (resolved == Resolved::Something) {
524                     // Resolved something, no need to try another codepoint
525                     break;
526                 }
527 
528                 if (ch == unresolvedText.end()) {
529                     // Not a single codepoint could be resolved but we finished the block
530                     break;
531                 }
532 
533                 // We can stop here or we can switch to another DIFFERENT codepoint
534                 while (ch != unresolvedText.end()) {
535                     unicode = nextUtf8Unit(&ch, unresolvedText.end());
536                     auto found = alreadyTried.find(unicode);
537                     if (found == nullptr) {
538                         alreadyTried.add(unicode);
539                         break;
540                     }
541                 }
542             }
543 
544         }
545     }
546 }
547 
iterateThroughShapingRegions(const ShapeVisitor & shape)548 bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) {
549 
550     size_t bidiIndex = 0;
551 
552     SkScalar advanceX = 0;
553     for (auto& placeholder : fParagraph->fPlaceholders) {
554 
555         if (placeholder.fTextBefore.width() > 0) {
556             // Shape the text by bidi regions
557             while (bidiIndex < fParagraph->fBidiRegions.size()) {
558                 SkUnicode::BidiRegion& bidiRegion = fParagraph->fBidiRegions[bidiIndex];
559                 auto start = std::max(bidiRegion.start, placeholder.fTextBefore.start);
560                 auto end = std::min(bidiRegion.end, placeholder.fTextBefore.end);
561 
562                 // Set up the iterators (the style iterator points to a bigger region that it could
563                 TextRange textRange(start, end);
564                 auto blockRange = fParagraph->findAllBlocks(textRange);
565                 SkSpan<Block> styleSpan(fParagraph->blocks(blockRange));
566 
567                 // Shape the text between placeholders
568                 if (!shape(textRange, styleSpan, advanceX, start, bidiRegion.level)) {
569                     return false;
570                 }
571 
572                 if (end == bidiRegion.end) {
573                     ++bidiIndex;
574                 } else /*if (end == placeholder.fTextBefore.end)*/ {
575                     break;
576                 }
577             }
578         }
579 
580         if (placeholder.fRange.width() == 0) {
581             continue;
582         }
583 
584         // Get the placeholder font
585         std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(
586             placeholder.fTextStyle.getFontFamilies(),
587             placeholder.fTextStyle.getFontStyle());
588         sk_sp<SkTypeface> typeface = typefaces.size() ? typefaces.front() : nullptr;
589         SkFont font(typeface, placeholder.fTextStyle.getFontSize());
590 
591         // "Shape" the placeholder
592         const SkShaper::RunHandler::RunInfo runInfo = {
593             font,
594             (uint8_t)2,
595             SkPoint::Make(placeholder.fStyle.fWidth, placeholder.fStyle.fHeight),
596             1,
597             SkShaper::RunHandler::Range(placeholder.fRange.start, placeholder.fRange.width())
598         };
599         auto& run = fParagraph->fRuns.emplace_back(this->fParagraph,
600                                        runInfo,
601                                        0,
602                                        0.0f,
603                                        false,
604                                        fParagraph->fRuns.count(),
605                                        advanceX);
606 
607         run.fPositions[0] = { advanceX, 0 };
608         run.fClusterIndexes[0] = 0;
609         run.fPlaceholderIndex = &placeholder - fParagraph->fPlaceholders.begin();
610         advanceX += placeholder.fStyle.fWidth;
611     }
612     return true;
613 }
614 
shape()615 bool OneLineShaper::shape() {
616 
617     // The text can be broken into many shaping sequences
618     // (by place holders, possibly, by hard line breaks or tabs, too)
619     auto limitlessWidth = std::numeric_limits<SkScalar>::max();
620 
621     auto result = iterateThroughShapingRegions(
622             [this, limitlessWidth]
623             (TextRange textRange, SkSpan<Block> styleSpan, SkScalar& advanceX, TextIndex textStart, uint8_t defaultBidiLevel) {
624 
625         // Set up the shaper and shape the next
626         auto shaper = SkShaper::MakeShapeDontWrapOrReorder();
627         if (shaper == nullptr) {
628             // For instance, loadICU does not work. We have to stop the process
629             return false;
630         }
631 
632         iterateThroughFontStyles(textRange, styleSpan,
633                 [this, &shaper, defaultBidiLevel, limitlessWidth, &advanceX]
634                 (Block block, SkTArray<SkShaper::Feature> features) {
635             auto blockSpan = SkSpan<Block>(&block, 1);
636 
637             // Start from the beginning (hoping that it's a simple case one block - one run)
638             fHeight = block.fStyle.getHeightOverride() ? block.fStyle.getHeight() : 0;
639             fUseHalfLeading = block.fStyle.getHalfLeading();
640             fAdvance = SkVector::Make(advanceX, 0);
641             fCurrentText = block.fRange;
642             fUnresolvedBlocks.emplace_back(RunBlock(block.fRange));
643 
644             matchResolvedFonts(block.fStyle, [&](sk_sp<SkTypeface> typeface) {
645 
646                 // Create one more font to try
647                 SkFont font(std::move(typeface), block.fStyle.getFontSize());
648                 font.setEdging(SkFont::Edging::kAntiAlias);
649                 font.setHinting(SkFontHinting::kSlight);
650                 font.setSubpixel(true);
651 
652                 // Apply fake bold and/or italic settings to the font if the
653                 // typeface's attributes do not match the intended font style.
654                 int wantedWeight = block.fStyle.getFontStyle().weight();
655                 bool fakeBold =
656                     wantedWeight >= SkFontStyle::kSemiBold_Weight &&
657                     wantedWeight - font.getTypeface()->fontStyle().weight() >= 200;
658                 bool fakeItalic =
659                     block.fStyle.getFontStyle().slant() == SkFontStyle::kItalic_Slant &&
660                     font.getTypeface()->fontStyle().slant() != SkFontStyle::kItalic_Slant;
661                 font.setEmbolden(fakeBold);
662                 font.setSkewX(fakeItalic ? -SK_Scalar1 / 4 : 0);
663 
664                 // Walk through all the currently unresolved blocks
665                 // (ignoring those that appear later)
666                 auto resolvedCount = fResolvedBlocks.size();
667                 auto unresolvedCount = fUnresolvedBlocks.size();
668                 while (unresolvedCount-- > 0) {
669                     auto unresolvedRange = fUnresolvedBlocks.front().fText;
670                     if (unresolvedRange == EMPTY_TEXT) {
671                         // Duplicate blocks should be ignored
672                         fUnresolvedBlocks.pop_front();
673                         continue;
674                     }
675                     auto unresolvedText = fParagraph->text(unresolvedRange);
676 
677                     SkShaper::TrivialFontRunIterator fontIter(font, unresolvedText.size());
678                     LangIterator langIter(unresolvedText, blockSpan,
679                                       fParagraph->paragraphStyle().getTextStyle());
680                     SkShaper::TrivialBiDiRunIterator bidiIter(defaultBidiLevel, unresolvedText.size());
681                     auto scriptIter = SkShaper::MakeSkUnicodeHbScriptRunIterator
682                                      (fParagraph->getUnicode(), unresolvedText.begin(), unresolvedText.size());
683                     fCurrentText = unresolvedRange;
684                     shaper->shape(unresolvedText.begin(), unresolvedText.size(),
685                             fontIter, bidiIter,*scriptIter, langIter,
686                             features.data(), features.size(),
687                             limitlessWidth, this);
688 
689                     // Take off the queue the block we tried to resolved -
690                     // whatever happened, we have now smaller pieces of it to deal with
691                     fUnresolvedBlocks.pop_front();
692                 }
693 
694                 if (fUnresolvedBlocks.empty()) {
695                     return Resolved::Everything;
696                 } else if (resolvedCount < fResolvedBlocks.size()) {
697                     return Resolved::Something;
698                 } else {
699                     return Resolved::Nothing;
700                 }
701             });
702 
703             this->finish(block, fHeight, advanceX);
704         });
705 
706         return true;
707     });
708 
709     return result;
710 }
711 
712 // When we extend TextRange to the grapheme edges, we also extend glyphs range
clusteredText(GlyphRange & glyphs)713 TextRange OneLineShaper::clusteredText(GlyphRange& glyphs) {
714 
715     enum class Dir { left, right };
716     enum class Pos { inclusive, exclusive };
717 
718     // [left: right)
719     auto findBaseChar = [&](TextIndex index, Dir dir) -> TextIndex {
720 
721         if (dir == Dir::right) {
722             while (index < fCurrentRun->fTextRange.end) {
723                 if (this->fParagraph->codeUnitHasProperty(index,
724                                                           CodeUnitFlags::kGraphemeStart)) {
725                     return index;
726                 }
727                 ++index;
728             }
729             return fCurrentRun->fTextRange.end;
730         } else {
731             while (index > fCurrentRun->fTextRange.start) {
732                 if (this->fParagraph->codeUnitHasProperty(index,
733                                                           CodeUnitFlags::kGraphemeStart)) {
734                     return index;
735                 }
736                 --index;
737             }
738             return fCurrentRun->fTextRange.start;
739         }
740     };
741 
742     TextRange textRange(normalizeTextRange(glyphs));
743     textRange.start = findBaseChar(textRange.start, Dir::left);
744     textRange.end = findBaseChar(textRange.end, Dir::right);
745 
746     // Correct the glyphRange in case we extended the text to the grapheme edges
747     // TODO: code it without if (as a part of LTR/RTL refactoring)
748     if (fCurrentRun->leftToRight()) {
749         while (glyphs.start > 0 && clusterIndex(glyphs.start) > textRange.start) {
750           glyphs.start--;
751         }
752         while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) < textRange.end) {
753           glyphs.end++;
754         }
755     } else {
756         while (glyphs.start > 0 && clusterIndex(glyphs.start - 1) < textRange.end) {
757           glyphs.start--;
758         }
759         while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) > textRange.start) {
760           glyphs.end++;
761         }
762     }
763 
764     return { textRange.start, textRange.end };
765 }
766 
operator ==(const OneLineShaper::FontKey & other) const767 bool OneLineShaper::FontKey::operator==(const OneLineShaper::FontKey& other) const {
768     return fUnicode == other.fUnicode && fFontStyle == other.fFontStyle && fLocale == other.fLocale;
769 }
770 
operator ()(const OneLineShaper::FontKey & key) const771 size_t OneLineShaper::FontKey::Hasher::operator()(const OneLineShaper::FontKey& key) const {
772 
773     return SkGoodHash()(key.fUnicode) ^
774            SkGoodHash()(key.fFontStyle) ^
775            SkGoodHash()(key.fLocale.c_str());
776 }
777 
778 }  // namespace textlayout
779 }  // namespace skia
780