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