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