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