• 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/base/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 [%zu:%zu)\n", fCurrentRun->fTextRange.start, fCurrentRun->fTextRange.end);
24     for (size_t i = 0; i < fCurrentRun->size(); ++i) {
25         SkDebugf("[%zu] %hu %u %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: %zu\n", fResolvedBlocks.size());
60     for (auto& resolved : fResolvedBlocks) {
61         if (resolved.fRun ==  nullptr) {
62             SkDebugf("[%zu:%zu) 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("[%zu:%zu) ", resolved.fGlyphs.start, resolved.fGlyphs.end);
71         SkDebugf("[%zu:%zu) with %s\n",
72                 resolved.fText.start, resolved.fText.end,
73                 name.c_str());
74     }
75 
76     auto size = fUnresolvedBlocks.size();
77     SkDebugf("Unresolved: %zu\n", size);
78     for (const auto& unresolved : fUnresolvedBlocks) {
79         SkDebugf("[%zu:%zu)\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: ...:%zu] - [%zu:%zu] (%zu-%zu)\n",
184                      lastTextEnd, text.start, text.end,  glyphs.start, glyphs.end);
185             SkASSERT(false);
186         }
187         lastTextEnd = text.end;
188 
189         if (resolvedBlock.isFullyResolved()) {
190             // Just move the entire run
191             resolvedBlock.fRun->fIndex = this->fParagraph->fRuns.size();
192             this->fParagraph->fRuns.emplace_back(*resolvedBlock.fRun);
193             resolvedBlock.fRun.reset();
194             continue;
195         } else if (run == nullptr) {
196             continue;
197         }
198 
199         auto runAdvance = SkVector::Make(run->posX(glyphs.end) - run->posX(glyphs.start), run->fAdvance.fY);
200         const SkShaper::RunHandler::RunInfo info = {
201                 run->fFont,
202                 run->fBidiLevel,
203                 runAdvance,
204                 glyphs.width(),
205                 SkShaper::RunHandler::Range(text.start - run->fClusterStart, text.width())
206         };
207         this->fParagraph->fRuns.emplace_back(
208                     this->fParagraph,
209                     info,
210                     run->fClusterStart,
211                     height,
212                     block.fStyle.getHalfLeading(),
213                     block.fStyle.getBaselineShift(),
214                     this->fParagraph->fRuns.size(),
215                     advanceX
216                 );
217         auto piece = &this->fParagraph->fRuns.back();
218 
219         // TODO: Optimize copying
220         SkPoint zero = {run->fPositions[glyphs.start].fX, 0};
221         for (size_t i = glyphs.start; i <= glyphs.end; ++i) {
222 
223             auto index = i - glyphs.start;
224             if (i < glyphs.end) {
225                 piece->fGlyphs[index] = run->fGlyphs[i];
226             }
227             piece->fClusterIndexes[index] = run->fClusterIndexes[i];
228             piece->fPositions[index] = run->fPositions[i] - zero;
229             piece->fOffsets[index] = run->fOffsets[i];
230             piece->addX(index, advanceX);
231         }
232 
233         // Carve out the line text out of the entire run text
234         fAdvance.fX += runAdvance.fX;
235         fAdvance.fY = std::max(fAdvance.fY, runAdvance.fY);
236     }
237 
238     advanceX = fAdvance.fX;
239     if (lastTextEnd != blockText.end) {
240         SkDEBUGF("Last range mismatch: %zu - %zu\n", lastTextEnd, blockText.end);
241         SkASSERT(false);
242     }
243 }
244 
245 // Make it [left:right) regardless of a text direction
normalizeTextRange(GlyphRange glyphRange)246 TextRange OneLineShaper::normalizeTextRange(GlyphRange glyphRange) {
247 
248     if (fCurrentRun->leftToRight()) {
249         return TextRange(clusterIndex(glyphRange.start), clusterIndex(glyphRange.end));
250     } else {
251         return TextRange(clusterIndex(glyphRange.end - 1),
252                 glyphRange.start > 0
253                 ? clusterIndex(glyphRange.start - 1)
254                 : fCurrentRun->fTextRange.end);
255     }
256 }
257 
addFullyResolved()258 void OneLineShaper::addFullyResolved() {
259     if (this->fCurrentRun->size() == 0) {
260         return;
261     }
262     RunBlock resolved(fCurrentRun,
263                       this->fCurrentRun->fTextRange,
264                       GlyphRange(0, this->fCurrentRun->size()),
265                       this->fCurrentRun->size());
266     fResolvedBlocks.emplace_back(resolved);
267 }
268 
addUnresolvedWithRun(GlyphRange glyphRange)269 void OneLineShaper::addUnresolvedWithRun(GlyphRange glyphRange) {
270     auto extendedText = this->clusteredText(glyphRange); // It also modifies glyphRange if needed
271     RunBlock unresolved(fCurrentRun, extendedText, glyphRange, 0);
272     if (unresolved.fGlyphs.width() == fCurrentRun->size()) {
273         SkASSERT(unresolved.fText.width() == fCurrentRun->fTextRange.width());
274     } else if (fUnresolvedBlocks.size() > 0) {
275         auto& lastUnresolved = fUnresolvedBlocks.back();
276         if (lastUnresolved.fRun != nullptr &&
277             lastUnresolved.fRun->fIndex == fCurrentRun->fIndex) {
278 
279             if (lastUnresolved.fText.end == unresolved.fText.start) {
280               // Two pieces next to each other - can join them
281               lastUnresolved.fText.end = unresolved.fText.end;
282               lastUnresolved.fGlyphs.end = glyphRange.end;
283               return;
284             } else if(lastUnresolved.fText == unresolved.fText) {
285                 // Nothing was resolved; ignore it
286                 return;
287             } else if (lastUnresolved.fText.contains(unresolved.fText)) {
288                 // We get here for the very first unresolved piece
289                 return;
290             } else if (lastUnresolved.fText.intersects(unresolved.fText)) {
291                 // Few pieces of the same unresolved text block can ignore the second one
292                 lastUnresolved.fGlyphs.start = std::min(lastUnresolved.fGlyphs.start, glyphRange.start);
293                 lastUnresolved.fGlyphs.end = std::max(lastUnresolved.fGlyphs.end, glyphRange.end);
294                 lastUnresolved.fText = this->clusteredText(lastUnresolved.fGlyphs);
295                 return;
296             }
297         }
298     }
299     fUnresolvedBlocks.emplace_back(unresolved);
300 }
301 
302 // Glue whitespaces to the next/prev unresolved blocks
303 // (so we don't have chinese text with english whitespaces broken into millions of tiny runs)
sortOutGlyphs(std::function<void (GlyphRange)> && sortOutUnresolvedBLock)304 void OneLineShaper::sortOutGlyphs(std::function<void(GlyphRange)>&& sortOutUnresolvedBLock) {
305 
306     GlyphRange block = EMPTY_RANGE;
307     bool graphemeResolved = false;
308     TextIndex graphemeStart = EMPTY_INDEX;
309     for (size_t i = 0; i < fCurrentRun->size(); ++i) {
310 
311         ClusterIndex ci = clusterIndex(i);
312         // Removing all pretty optimizations for whitespaces
313         // because they get in a way of grapheme rounding
314         // Inspect the glyph
315         auto glyph = fCurrentRun->fGlyphs[i];
316 
317         GraphemeIndex gi = fParagraph->findPreviousGraphemeBoundary(ci);
318         if ((fCurrentRun->leftToRight() ? gi > graphemeStart : gi < graphemeStart) || graphemeStart == EMPTY_INDEX) {
319             // This is the Flutter change
320             // Do not count control codepoints as unresolved
321             bool isControl8 = fParagraph->codeUnitHasProperty(ci,
322                                                               SkUnicode::CodeUnitFlags::kControl);
323             // We only count glyph resolved if all the glyphs in its grapheme are resolved
324             graphemeResolved = glyph != 0 || isControl8;
325             graphemeStart = gi;
326         } else if (glyph == 0) {
327             // Found unresolved glyph - the entire grapheme is unresolved now
328             graphemeResolved = false;
329         }
330 
331         if (!graphemeResolved) { // Unresolved glyph and not control codepoint
332             if (block.start == EMPTY_INDEX) {
333                 // Start new unresolved block
334                 block.start = i;
335                 block.end = EMPTY_INDEX;
336             } else {
337                 // Keep skipping unresolved block
338             }
339         } else { // Resolved glyph or control codepoint
340             if (block.start == EMPTY_INDEX) {
341                 // Keep skipping resolved code points
342             } else {
343                 // This is the end of unresolved block
344                 block.end = i;
345                 sortOutUnresolvedBLock(block);
346                 block = EMPTY_RANGE;
347             }
348         }
349     }
350 
351     // One last block could have been left
352     if (block.start != EMPTY_INDEX) {
353         block.end = fCurrentRun->size();
354         sortOutUnresolvedBLock(block);
355     }
356 }
357 
iterateThroughFontStyles(TextRange textRange,SkSpan<Block> styleSpan,const ShapeSingleFontVisitor & visitor)358 void OneLineShaper::iterateThroughFontStyles(TextRange textRange,
359                                              SkSpan<Block> styleSpan,
360                                              const ShapeSingleFontVisitor& visitor) {
361     Block combinedBlock;
362     SkTArray<SkShaper::Feature> features;
363 
364     auto addFeatures = [&features](const Block& block) {
365         for (auto& ff : block.fStyle.getFontFeatures()) {
366             if (ff.fName.size() != 4) {
367                 SkDEBUGF("Incorrect font feature: %s=%d\n", ff.fName.c_str(), ff.fValue);
368                 continue;
369             }
370             SkShaper::Feature feature = {
371                 SkSetFourByteTag(ff.fName[0], ff.fName[1], ff.fName[2], ff.fName[3]),
372                 SkToU32(ff.fValue),
373                 block.fRange.start,
374                 block.fRange.end
375             };
376             features.emplace_back(feature);
377         }
378         // Disable ligatures if letter spacing is enabled.
379         if (block.fStyle.getLetterSpacing() > 0) {
380             features.emplace_back(SkShaper::Feature{
381                 SkSetFourByteTag('l', 'i', 'g', 'a'), 0, block.fRange.start, block.fRange.end
382             });
383         }
384     };
385 
386     for (auto& block : styleSpan) {
387         BlockRange blockRange(std::max(block.fRange.start, textRange.start), std::min(block.fRange.end, textRange.end));
388         if (blockRange.empty()) {
389             continue;
390         }
391         SkASSERT(combinedBlock.fRange.width() == 0 || combinedBlock.fRange.end == block.fRange.start);
392 
393         if (!combinedBlock.fRange.empty()) {
394             if (block.fStyle.matchOneAttribute(StyleType::kFont, combinedBlock.fStyle)) {
395                 combinedBlock.add(blockRange);
396                 addFeatures(block);
397                 continue;
398             }
399             // Resolve all characters in the block for this style
400             visitor(combinedBlock, features);
401         }
402 
403         combinedBlock.fRange = blockRange;
404         combinedBlock.fStyle = block.fStyle;
405         features.clear();
406         addFeatures(block);
407     }
408 
409     visitor(combinedBlock, features);
410 #ifdef SK_DEBUG
411     //printState();
412 #endif
413 }
414 
matchResolvedFonts(const TextStyle & textStyle,const TypefaceVisitor & visitor)415 void OneLineShaper::matchResolvedFonts(const TextStyle& textStyle,
416                                        const TypefaceVisitor& visitor) {
417     std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(textStyle.getFontFamilies(), textStyle.getFontStyle(), textStyle.getFontArguments());
418 
419     for (const auto& typeface : typefaces) {
420         if (visitor(typeface) == Resolved::Everything) {
421             // Resolved everything
422             return;
423         }
424     }
425 
426     if (fParagraph->fFontCollection->fontFallbackEnabled()) {
427         // Give fallback a clue
428         // Some unresolved subblocks might be resolved with different fallback fonts
429         std::vector<RunBlock> hopelessBlocks;
430         while (!fUnresolvedBlocks.empty()) {
431             auto unresolvedRange = fUnresolvedBlocks.front().fText;
432             auto unresolvedText = fParagraph->text(unresolvedRange);
433             const char* ch = unresolvedText.begin();
434             // We have the global cache for all already found typefaces for SkUnichar
435             // but we still need to keep track of all SkUnichars used in this unresolved block
436             SkTHashSet<SkUnichar> alreadyTriedCodepoints;
437             SkTHashSet<SkTypefaceID> alreadyTriedTypefaces;
438             while (true) {
439 
440                 if (ch == unresolvedText.end()) {
441                     // Not a single codepoint could be resolved but we finished the block
442                     hopelessBlocks.push_back(fUnresolvedBlocks.front());
443                     fUnresolvedBlocks.pop_front();
444                     break;
445                 }
446 
447                 // See if we can switch to the next DIFFERENT codepoint
448                 SkUnichar unicode = -1;
449                 while (ch != unresolvedText.end()) {
450                     unicode = nextUtf8Unit(&ch, unresolvedText.end());
451                     if (!alreadyTriedCodepoints.contains(unicode)) {
452                         alreadyTriedCodepoints.add(unicode);
453                         break;
454                     }
455                 }
456                 SkASSERT(unicode != -1);
457 
458                 // First try to find in in a cache
459                 sk_sp<SkTypeface> typeface;
460                 FontKey fontKey(unicode, textStyle.getFontStyle(), textStyle.getLocale());
461                 auto found = fFallbackFonts.find(fontKey);
462                 if (found != nullptr) {
463                     typeface = *found;
464                 } else {
465                     typeface = fParagraph->fFontCollection->defaultFallback(
466                             unicode, textStyle.getFontStyle(), textStyle.getLocale());
467 
468                     if (typeface == nullptr) {
469                         // There is no fallback font for this character, so move on to the next character.
470                         continue;
471                     }
472                     fFallbackFonts.set(fontKey, typeface);
473                 }
474 
475                 // Check if we already tried this font on this text range
476                 if (!alreadyTriedTypefaces.contains(typeface->uniqueID())) {
477                     alreadyTriedTypefaces.add(typeface->uniqueID());
478                 } else {
479                     continue;
480                 }
481 
482                 auto resolvedBlocksBefore = fResolvedBlocks.size();
483                 auto resolved = visitor(typeface);
484                 if (resolved == Resolved::Everything) {
485                     if (hopelessBlocks.empty()) {
486                         // Resolved everything, no need to try another font
487                         return;
488                     } else if (resolvedBlocksBefore < fResolvedBlocks.size()) {
489                         // There are some resolved blocks
490                         resolved = Resolved::Something;
491                     } else {
492                         // All blocks are hopeless
493                         resolved = Resolved::Nothing;
494                     }
495                 }
496 
497                 if (resolved == Resolved::Something) {
498                     // Resolved something, no need to try another codepoint
499                     break;
500                 }
501             }
502         }
503 
504         // Return hopeless blocks back
505         for (auto& block : hopelessBlocks) {
506             fUnresolvedBlocks.emplace_front(block);
507         }
508     }
509 }
510 
iterateThroughShapingRegions(const ShapeVisitor & shape)511 bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) {
512 
513     size_t bidiIndex = 0;
514 
515     SkScalar advanceX = 0;
516     for (auto& placeholder : fParagraph->fPlaceholders) {
517 
518         if (placeholder.fTextBefore.width() > 0) {
519             // Shape the text by bidi regions
520             while (bidiIndex < fParagraph->fBidiRegions.size()) {
521                 SkUnicode::BidiRegion& bidiRegion = fParagraph->fBidiRegions[bidiIndex];
522                 auto start = std::max(bidiRegion.start, placeholder.fTextBefore.start);
523                 auto end = std::min(bidiRegion.end, placeholder.fTextBefore.end);
524 
525                 // Set up the iterators (the style iterator points to a bigger region that it could
526                 TextRange textRange(start, end);
527                 auto blockRange = fParagraph->findAllBlocks(textRange);
528                 if (!blockRange.empty()) {
529                     SkSpan<Block> styleSpan(fParagraph->blocks(blockRange));
530 
531                     // Shape the text between placeholders
532                     if (!shape(textRange, styleSpan, advanceX, start, bidiRegion.level)) {
533                         return false;
534                     }
535                 }
536 
537                 if (end == bidiRegion.end) {
538                     ++bidiIndex;
539                 } else /*if (end == placeholder.fTextBefore.end)*/ {
540                     break;
541                 }
542             }
543         }
544 
545         if (placeholder.fRange.width() == 0) {
546             continue;
547         }
548 
549         // Get the placeholder font
550         std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(
551             placeholder.fTextStyle.getFontFamilies(),
552             placeholder.fTextStyle.getFontStyle(),
553             placeholder.fTextStyle.getFontArguments());
554         sk_sp<SkTypeface> typeface = typefaces.size() ? typefaces.front() : nullptr;
555         SkFont font(typeface, placeholder.fTextStyle.getFontSize());
556 
557         // "Shape" the placeholder
558         uint8_t bidiLevel = (bidiIndex < fParagraph->fBidiRegions.size())
559             ? fParagraph->fBidiRegions[bidiIndex].level
560             : 2;
561         const SkShaper::RunHandler::RunInfo runInfo = {
562             font,
563             bidiLevel,
564             SkPoint::Make(placeholder.fStyle.fWidth, placeholder.fStyle.fHeight),
565             1,
566             SkShaper::RunHandler::Range(0, placeholder.fRange.width())
567         };
568         auto& run = fParagraph->fRuns.emplace_back(this->fParagraph,
569                                        runInfo,
570                                        placeholder.fRange.start,
571                                        0.0f,
572                                        0.0f,
573                                        false,
574                                        fParagraph->fRuns.size(),
575                                        advanceX);
576 
577         run.fPositions[0] = { advanceX, 0 };
578         run.fOffsets[0] = {0, 0};
579         run.fClusterIndexes[0] = 0;
580         run.fPlaceholderIndex = &placeholder - fParagraph->fPlaceholders.begin();
581         advanceX += placeholder.fStyle.fWidth;
582     }
583     return true;
584 }
585 
shape()586 bool OneLineShaper::shape() {
587 
588     // The text can be broken into many shaping sequences
589     // (by place holders, possibly, by hard line breaks or tabs, too)
590     auto limitlessWidth = std::numeric_limits<SkScalar>::max();
591 
592     auto result = iterateThroughShapingRegions(
593             [this, limitlessWidth]
594             (TextRange textRange, SkSpan<Block> styleSpan, SkScalar& advanceX, TextIndex textStart, uint8_t defaultBidiLevel) {
595 
596         // Set up the shaper and shape the next
597         auto shaper = SkShaper::MakeShapeDontWrapOrReorder(fParagraph->fUnicode->copy());
598         if (shaper == nullptr) {
599             // For instance, loadICU does not work. We have to stop the process
600             return false;
601         }
602 
603         iterateThroughFontStyles(textRange, styleSpan,
604                 [this, &shaper, defaultBidiLevel, limitlessWidth, &advanceX]
605                 (Block block, SkTArray<SkShaper::Feature> features) {
606             auto blockSpan = SkSpan<Block>(&block, 1);
607 
608             // Start from the beginning (hoping that it's a simple case one block - one run)
609             fHeight = block.fStyle.getHeightOverride() ? block.fStyle.getHeight() : 0;
610             fUseHalfLeading = block.fStyle.getHalfLeading();
611             fBaselineShift = block.fStyle.getBaselineShift();
612             fAdvance = SkVector::Make(advanceX, 0);
613             fCurrentText = block.fRange;
614             fUnresolvedBlocks.emplace_back(RunBlock(block.fRange));
615 
616             this->matchResolvedFonts(block.fStyle, [&](sk_sp<SkTypeface> typeface) {
617 
618                 // Create one more font to try
619                 SkFont font(std::move(typeface), block.fStyle.getFontSize());
620                 font.setEdging(SkFont::Edging::kAntiAlias);
621                 font.setHinting(SkFontHinting::kSlight);
622                 font.setSubpixel(true);
623 
624                 // Apply fake bold and/or italic settings to the font if the
625                 // typeface's attributes do not match the intended font style.
626                 int wantedWeight = block.fStyle.getFontStyle().weight();
627                 bool fakeBold =
628                     wantedWeight >= SkFontStyle::kSemiBold_Weight &&
629                     wantedWeight - font.getTypeface()->fontStyle().weight() >= 200;
630                 bool fakeItalic =
631                     block.fStyle.getFontStyle().slant() == SkFontStyle::kItalic_Slant &&
632                     font.getTypeface()->fontStyle().slant() != SkFontStyle::kItalic_Slant;
633                 font.setEmbolden(fakeBold);
634                 font.setSkewX(fakeItalic ? -SK_Scalar1 / 4 : 0);
635 
636                 // Walk through all the currently unresolved blocks
637                 // (ignoring those that appear later)
638                 auto resolvedCount = fResolvedBlocks.size();
639                 auto unresolvedCount = fUnresolvedBlocks.size();
640                 while (unresolvedCount-- > 0) {
641                     auto unresolvedRange = fUnresolvedBlocks.front().fText;
642                     if (unresolvedRange == EMPTY_TEXT) {
643                         // Duplicate blocks should be ignored
644                         fUnresolvedBlocks.pop_front();
645                         continue;
646                     }
647                     auto unresolvedText = fParagraph->text(unresolvedRange);
648 
649                     SkShaper::TrivialFontRunIterator fontIter(font, unresolvedText.size());
650                     LangIterator langIter(unresolvedText, blockSpan,
651                                       fParagraph->paragraphStyle().getTextStyle());
652                     SkShaper::TrivialBiDiRunIterator bidiIter(defaultBidiLevel, unresolvedText.size());
653                     auto scriptIter = SkShaper::MakeSkUnicodeHbScriptRunIterator(
654                             unresolvedText.begin(), unresolvedText.size());
655                     fCurrentText = unresolvedRange;
656 
657                     // Map the block's features to subranges within the unresolved range.
658                     SkTArray<SkShaper::Feature> adjustedFeatures(features.size());
659                     for (const SkShaper::Feature& feature : features) {
660                         SkRange<size_t> featureRange(feature.start, feature.end);
661                         if (unresolvedRange.intersects(featureRange)) {
662                             SkRange<size_t> adjustedRange = unresolvedRange.intersection(featureRange);
663                             adjustedRange.Shift(-static_cast<std::make_signed_t<size_t>>(unresolvedRange.start));
664                             adjustedFeatures.push_back({feature.tag, feature.value, adjustedRange.start, adjustedRange.end});
665                         }
666                     }
667 
668                     shaper->shape(unresolvedText.begin(), unresolvedText.size(),
669                             fontIter, bidiIter,*scriptIter, langIter,
670                             adjustedFeatures.data(), adjustedFeatures.size(),
671                             limitlessWidth, this);
672 
673                     // Take off the queue the block we tried to resolved -
674                     // whatever happened, we have now smaller pieces of it to deal with
675                     fUnresolvedBlocks.pop_front();
676                 }
677 
678                 if (fUnresolvedBlocks.empty()) {
679                     // In some cases it does not mean everything
680                     // (when we excluded some hopeless blocks from the list)
681                     return Resolved::Everything;
682                 } else if (resolvedCount < fResolvedBlocks.size()) {
683                     return Resolved::Something;
684                 } else {
685                     return Resolved::Nothing;
686                 }
687             });
688 
689             this->finish(block, fHeight, advanceX);
690         });
691 
692         return true;
693     });
694 
695     return result;
696 }
697 
698 // When we extend TextRange to the grapheme edges, we also extend glyphs range
clusteredText(GlyphRange & glyphs)699 TextRange OneLineShaper::clusteredText(GlyphRange& glyphs) {
700 
701     enum class Dir { left, right };
702     enum class Pos { inclusive, exclusive };
703 
704     // [left: right)
705     auto findBaseChar = [&](TextIndex index, Dir dir) -> TextIndex {
706 
707         if (dir == Dir::right) {
708             while (index < fCurrentRun->fTextRange.end) {
709                 if (this->fParagraph->codeUnitHasProperty(index,
710                                                       SkUnicode::CodeUnitFlags::kGraphemeStart)) {
711                     return index;
712                 }
713                 ++index;
714             }
715             return fCurrentRun->fTextRange.end;
716         } else {
717             while (index > fCurrentRun->fTextRange.start) {
718                 if (this->fParagraph->codeUnitHasProperty(index,
719                                                       SkUnicode::CodeUnitFlags::kGraphemeStart)) {
720                     return index;
721                 }
722                 --index;
723             }
724             return fCurrentRun->fTextRange.start;
725         }
726     };
727 
728     TextRange textRange(normalizeTextRange(glyphs));
729     textRange.start = findBaseChar(textRange.start, Dir::left);
730     textRange.end = findBaseChar(textRange.end, Dir::right);
731 
732     // Correct the glyphRange in case we extended the text to the grapheme edges
733     // TODO: code it without if (as a part of LTR/RTL refactoring)
734     if (fCurrentRun->leftToRight()) {
735         while (glyphs.start > 0 && clusterIndex(glyphs.start) > textRange.start) {
736           glyphs.start--;
737         }
738         while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) < textRange.end) {
739           glyphs.end++;
740         }
741     } else {
742         while (glyphs.start > 0 && clusterIndex(glyphs.start - 1) < textRange.end) {
743           glyphs.start--;
744         }
745         while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) > textRange.start) {
746           glyphs.end++;
747         }
748     }
749 
750     return { textRange.start, textRange.end };
751 }
752 
operator ==(const OneLineShaper::FontKey & other) const753 bool OneLineShaper::FontKey::operator==(const OneLineShaper::FontKey& other) const {
754     return fUnicode == other.fUnicode && fFontStyle == other.fFontStyle && fLocale == other.fLocale;
755 }
756 
operator ()(const OneLineShaper::FontKey & key) const757 size_t OneLineShaper::FontKey::Hasher::operator()(const OneLineShaper::FontKey& key) const {
758 
759     return SkGoodHash()(key.fUnicode) ^
760            SkGoodHash()(key.fFontStyle) ^
761            SkGoodHash()(key.fLocale.c_str());
762 }
763 
764 }  // namespace textlayout
765 }  // namespace skia
766