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