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