• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Google LLC.
2 #include "include/core/SkFontMetrics.h"
3 #include "include/core/SkTextBlob.h"
4 #include "include/private/base/SkFloatingPoint.h"
5 #include "include/private/base/SkMalloc.h"
6 #include "include/private/base/SkTo.h"
7 #include "modules/skparagraph/include/DartTypes.h"
8 #include "modules/skparagraph/include/TextStyle.h"
9 #include "modules/skparagraph/src/ParagraphImpl.h"
10 #include "modules/skparagraph/src/Run.h"
11 #include "modules/skshaper/include/SkShaper.h"
12 #include "src/base/SkUTF.h"
13 
14 #ifdef ENABLE_TEXT_ENHANCE
15 #include "include/FontCollection.h"
16 #include "log.h"
17 #endif
18 namespace skia {
19 namespace textlayout {
20 #ifdef ENABLE_TEXT_ENHANCE
21 constexpr SkScalar PARAM_TWO = 2.0;
22 // 1px font size "HarmonyOS Sans" metrics
23 constexpr SkScalar DEFAULT_TOP = -1.056;
24 constexpr SkScalar DEFAULT_BOTTOM = 0.271;
25 constexpr SkScalar DEFAULT_ASCENT = -0.928;
26 constexpr SkScalar DEFAULT_DESCENT = 0.244;
27 struct ScaleParam {
28     SkScalar fontScale;
29     SkScalar baselineShiftScale;
30 };
31 // unordered_map<familyName, ScaleParam>: compress <familyName> font height, shift font baseline.
32 // target font size = font size * ScaleParam.scale.
33 // target baseline = baseline - height * font size * ScaleParam.baselineShiftScale.
34 const std::unordered_map<std::string, ScaleParam> FONT_FAMILY_COMPRESSION_CONFIG = {
35     {"Noto Serif Tibetan", ScaleParam{ .fontScale = 0.79, .baselineShiftScale = 0.1 }},
36     {"Noto Sans Tibetan", ScaleParam{ .fontScale = 0.79, .baselineShiftScale = 0.1 }},
37 };
38 const std::unordered_map<std::string, ScaleParam> FONT_FAMILY_COMPRESSION_WITH_HEIGHT_ADAPTER_CONFIG = {
39     {"Noto Serif Tibetan", ScaleParam{ .fontScale = 0.85, .baselineShiftScale = 0.11 }},
40     {"Noto Sans Tibetan", ScaleParam{ .fontScale = 0.85, .baselineShiftScale = 0.11 }},
41 };
42 const ScaleParam DEFAULT_SCALE_PARAM = ScaleParam{ .fontScale = 0, .baselineShiftScale = 0 };
43 enum FontCompressionStatus {
44     UNDEFINED,
45     COMPRESSED,
46     UNCOMPRESSED,
47 };
48 // the font padding does not take effect for these font families.
49 const std::unordered_set<std::string> FONT_PADDING_NOT_EFFECT_FAMILY = {
50     "Harmony Clock_01",
51     "Harmony Clock_02",
52     "Harmony Clock_03",
53     "Harmony Clock_04",
54     "Harmony Clock_05",
55     "Harmony Clock_06",
56     "Harmony Clock_07",
57     "Harmony Clock_08",
58 // symbol: need to ensure "the symbol height = the font size".
59 // so the height compression is not enabled for symbol.
60     "HM Symbol",
61 };
62 
getFontCompressionStatus(const RSFont & font)63 FontCompressionStatus getFontCompressionStatus(const RSFont& font)
64 {
65     auto typeface = font.GetTypeface();
66     if (typeface == nullptr) {
67         return FontCompressionStatus::UNDEFINED;
68     }
69     return (typeface->IsCustomTypeface() && !typeface->IsThemeTypeface())
70                    ? FontCompressionStatus::UNCOMPRESSED
71                    : FontCompressionStatus::COMPRESSED;
72 }
getFamilyNameFromFont(const RSFont & font)73 std::string getFamilyNameFromFont(const RSFont& font)
74 {
75     auto typeface = font.GetTypeface();
76     return typeface == nullptr ? "" : typeface->GetFamilyName();
77 }
78 
findCompressionConfigWithFont(const RSFont & font)79 const ScaleParam& findCompressionConfigWithFont(const RSFont& font)
80 {
81     auto fontCompressionStatus = getFontCompressionStatus(font);
82     if (fontCompressionStatus != FontCompressionStatus::COMPRESSED) {
83         return DEFAULT_SCALE_PARAM;
84     }
85 
86     const auto& config = FontCollection::IsAdapterTextHeightEnabled() ?
87         FONT_FAMILY_COMPRESSION_WITH_HEIGHT_ADAPTER_CONFIG : FONT_FAMILY_COMPRESSION_CONFIG;
88     std::string familyName = getFamilyNameFromFont(font);
89     auto iter = config.find(familyName);
90     if (iter == config.end()) {
91         return DEFAULT_SCALE_PARAM;
92     }
93     return iter->second;
94 }
95 
metricsIncludeFontPadding(RSFontMetrics * metrics,const RSFont & font)96 void metricsIncludeFontPadding(RSFontMetrics* metrics, const RSFont& font)
97 {
98     if (metrics == nullptr) {
99         return;
100     }
101     auto fontCompressionStatus = getFontCompressionStatus(font);
102     auto typeface = font.GetTypeface();
103     if (typeface == nullptr || fontCompressionStatus == FontCompressionStatus::UNDEFINED) {
104         return;
105     }
106     SkScalar fontSize = font.GetSize();
107     if (!FontCollection::IsAdapterTextHeightEnabled()) {
108         if (fontCompressionStatus == FontCompressionStatus::COMPRESSED &&
109             (!SkScalarNearlyZero(findCompressionConfigWithFont(font).fontScale) ||
110             typeface->IsThemeTypeface())) {
111             metrics->fAscent = DEFAULT_ASCENT * fontSize;
112             metrics->fDescent = DEFAULT_DESCENT * fontSize;
113         }
114         return;
115     }
116 
117     std::string curFamilyName = getFamilyNameFromFont(font);
118     auto setIter = FONT_PADDING_NOT_EFFECT_FAMILY.find(curFamilyName);
119     if (setIter == FONT_PADDING_NOT_EFFECT_FAMILY.end()) {
120         if (fontCompressionStatus == FontCompressionStatus::COMPRESSED) {
121             metrics->fAscent = DEFAULT_TOP * fontSize;
122             metrics->fDescent = DEFAULT_BOTTOM * fontSize;
123             return;
124         }
125         // use top and bottom as ascent and descent.
126         // calculate height with top and bottom.(includeFontPadding)
127         metrics->fAscent = metrics->fTop;
128         metrics->fDescent = metrics->fBottom;
129     }
130 }
131 
scaleFontWithCompressionConfig(RSFont & font,ScaleOP op)132 void scaleFontWithCompressionConfig(RSFont& font, ScaleOP op)
133 {
134     SkScalar fontSize = font.GetSize();
135     auto config = findCompressionConfigWithFont(font);
136     if (SkScalarNearlyZero(config.fontScale)) {
137         return;
138     }
139     switch (op) {
140     case ScaleOP::COMPRESS:
141         fontSize *= config.fontScale;
142         break;
143     case ScaleOP::DECOMPRESS:
144         fontSize /= config.fontScale;
145         break;
146     default:
147         return;
148     }
149     font.SetSize(fontSize);
150 }
151 #endif
152 
Run(ParagraphImpl * owner,const SkShaper::RunHandler::RunInfo & info,size_t firstChar,SkScalar heightMultiplier,bool useHalfLeading,SkScalar baselineShift,size_t index,SkScalar offsetX)153 Run::Run(ParagraphImpl* owner,
154          const SkShaper::RunHandler::RunInfo& info,
155          size_t firstChar,
156          SkScalar heightMultiplier,
157          bool useHalfLeading,
158          SkScalar baselineShift,
159          size_t index,
160          SkScalar offsetX)
161     : fOwner(owner)
162     , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
163     , fClusterRange(EMPTY_CLUSTERS)
164     , fFont(info.fFont)
165     , fClusterStart(firstChar)
166     , fGlyphData(std::make_shared<GlyphData>())
167     , fGlyphs(fGlyphData->glyphs)
168     , fPositions(fGlyphData->positions)
169     , fOffsets(fGlyphData->offsets)
170     , fClusterIndexes(fGlyphData->clusterIndexes)
171 #ifdef ENABLE_TEXT_ENHANCE
172     , fGlyphAdvances(fGlyphData->advances)
173 #endif
174     , fHeightMultiplier(heightMultiplier)
175     , fUseHalfLeading(useHalfLeading)
176     , fBaselineShift(baselineShift)
177 {
178     fBidiLevel = info.fBidiLevel;
179     fAdvance = info.fAdvance;
180     fIndex = index;
181     fUtf8Range = info.utf8Range;
182     fOffset = SkVector::Make(offsetX, 0);
183 
184     fGlyphs.push_back_n(info.glyphCount);
185     fPositions.push_back_n(info.glyphCount + 1);
186     fOffsets.push_back_n(info.glyphCount + 1);
187     fClusterIndexes.push_back_n(info.glyphCount + 1);
188 #ifdef ENABLE_TEXT_ENHANCE
189     fGlyphAdvances.push_back_n(info.glyphCount + 1);
190     fHalfLetterspacings.push_back_n(info.glyphCount + 1);
191     std::fill(fHalfLetterspacings.begin(), fHalfLetterspacings.end(), 0.0);
192     info.fFont.GetMetrics(&fFontMetrics);
193 #else
194     info.fFont.getMetrics(&fFontMetrics);
195 #endif
196 
197 #ifdef ENABLE_TEXT_ENHANCE
198     auto decompressFont = info.fFont;
199     scaleFontWithCompressionConfig(decompressFont, ScaleOP::DECOMPRESS);
200     metricsIncludeFontPadding(&fFontMetrics, decompressFont);
201     auto config = findCompressionConfigWithFont(decompressFont);
202     fCompressionBaselineShift = (fFontMetrics.fDescent - fFontMetrics.fAscent) * config.baselineShiftScale;
203 #endif
204 
205     this->calculateMetrics();
206 
207     // To make edge cases easier:
208     fPositions[info.glyphCount] = fOffset + fAdvance;
209     fOffsets[info.glyphCount] = {0, 0};
210 #ifdef ENABLE_TEXT_ENHANCE
211     fGlyphAdvances[info.glyphCount] = {0, 0};
212     if (leftToRight()) {
213         fClusterIndexes[info.glyphCount] = info.utf8Range.end();
214     } else {
215         // First cluster index in rtl's language run is end of the utf8 range value
216         fClusterIndexes[0] = info.utf8Range.end();
217     }
218 #else
219     fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
220 #endif
221     fEllipsis = false;
222     fPlaceholderIndex = std::numeric_limits<size_t>::max();
223 }
224 
calculateMetrics()225 void Run::calculateMetrics() {
226     fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5;
227     fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5;
228     fCorrectLeading = 0;
229     if (SkScalarNearlyZero(fHeightMultiplier)) {
230         return;
231     }
232 #ifdef ENABLE_TEXT_ENHANCE
233     auto decompressFont = fFont;
234     scaleFontWithCompressionConfig(decompressFont, ScaleOP::DECOMPRESS);
235     const auto runHeight = fHeightMultiplier * decompressFont.GetSize();
236 #else
237     const auto runHeight = fHeightMultiplier * fFont.getSize();
238 #endif
239     const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent;
240     if (fUseHalfLeading) {
241         const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2;
242         fCorrectAscent -= extraLeading;
243         fCorrectDescent += extraLeading;
244     } else {
245         const auto multiplier = runHeight / fontIntrinsicHeight;
246         fCorrectAscent *= multiplier;
247         fCorrectDescent *= multiplier;
248     }
249     // If we shift the baseline we need to make sure the shifted text fits the line
250     fCorrectAscent += fBaselineShift;
251     fCorrectDescent += fBaselineShift;
252 }
253 
254 #ifdef ENABLE_TEXT_ENHANCE
Run(const Run & run,size_t runIndex)255 Run::Run(const Run& run, size_t runIndex)
256     : fOwner(run.fOwner),
257       fTextRange(run.textRange()),
258       fClusterRange(run.clusterRange()),
259       fFont(run.fFont),
260       fPlaceholderIndex(run.fPlaceholderIndex),
261       fIndex(runIndex),
262       fAdvance(SkVector::Make(0, 0)),
263       fOffset(SkVector::Make(0, 0)),
264       fClusterStart(run.fClusterStart),
265       fUtf8Range(run.fUtf8Range),
266       fGlyphData(std::make_shared<GlyphData>()),
267       fGlyphs(fGlyphData->glyphs),
268       fPositions(fGlyphData->positions),
269       fOffsets(fGlyphData->offsets),
270       fClusterIndexes(fGlyphData->clusterIndexes),
271       fGlyphAdvances(fGlyphData->advances),
272       fJustificationShifts(),
273       fAutoSpacings(),
274       fHalfLetterspacings(),
275       fFontMetrics(run.fFontMetrics),
276       fHeightMultiplier(run.fHeightMultiplier),
277       fUseHalfLeading(run.fUseHalfLeading),
278       fBaselineShift(run.fBaselineShift),
279       fCorrectAscent(run.fCorrectAscent),
280       fCorrectDescent(run.fCorrectDescent),
281       fCorrectLeading(run.fCorrectLeading),
282       fEllipsis(run.fEllipsis),
283       fBidiLevel(run.fBidiLevel),
284       fTopInGroup(run.fTopInGroup),
285       fBottomInGroup(run.fBottomInGroup),
286       fMaxRoundRectRadius(run.fMaxRoundRectRadius),
287       indexInLine(run.indexInLine),
288       fCompressionBaselineShift(run.fCompressionBaselineShift),
289       fVerticalAlignShift(run.fVerticalAlignShift) {}
290 
findSplitClusterPos(size_t target)291 size_t Run::findSplitClusterPos(size_t target) {
292     int left = -1;
293     int right = clusterIndexes().size();
294     while (left + 1 < right) {
295         int mid = left + (right - left) / 2;
296         if (clusterIndexes()[mid] >= target) {
297             if (leftToRight()) {
298                 right = mid;
299                 continue;
300             }
301             left = mid;
302         } else {
303             if (leftToRight()) {
304                 left = mid;
305                 continue;
306             }
307             right = mid;
308         }
309     }
310     if (leftToRight()) {
311         return static_cast<size_t>(right);
312     }
313     return static_cast<size_t>(left);
314 }
315 
316 // Compatible with getCoordinate RTL scenario
globalClusterIndex(size_t pos) const317 size_t Run::globalClusterIndex(size_t pos) const {
318     if (leftToRight() || pos == (size_t)fGlyphs.size()) {
319         return fClusterStart + fClusterIndexes[pos];
320     }
321     return fClusterStart + fClusterIndexes[pos + 1];
322 }
323 
updateSplitRunRangeInfo(Run & splitRun,const TextLine & splitLine,size_t headIndex,size_t tailIndex)324 void Run::updateSplitRunRangeInfo(Run& splitRun, const TextLine& splitLine, size_t headIndex, size_t tailIndex) {
325     splitRun.fTextRange.start = std::max(headIndex,
326         fOwner->cluster(splitLine.clustersWithSpaces().start).textRange().start);
327     splitRun.fClusterRange.start = fOwner->clusterIndex(headIndex);
328     splitRun.fTextRange.end = std::min(tailIndex,
329         fOwner->cluster(splitLine.clustersWithSpaces().end-1).textRange().end);
330     splitRun.fUtf8Range = {splitRun.fTextRange.start, splitRun.fTextRange.width()};
331     splitRun.fClusterRange.end = fOwner->clusterIndex(tailIndex);
332 }
333 
updateSplitRunMesureInfo(Run & splitRun,size_t startClusterPos,size_t endClusterPos)334 void Run::updateSplitRunMesureInfo(Run& splitRun, size_t startClusterPos, size_t endClusterPos) {
335     if (!leftToRight()) {
336         std::swap(startClusterPos, endClusterPos);
337     }
338     SkScalar glyphPosVal = 0.0f;
339     SkScalar posOffset = 0.0f;
340     posOffset = fGlyphData->positions[startClusterPos].fX;
341     for (; startClusterPos < endClusterPos; ++startClusterPos) {
342         splitRun.fGlyphData->glyphs.push_back(fGlyphData->glyphs[startClusterPos]);
343         glyphPosVal = fGlyphData->positions[startClusterPos].fX - posOffset;
344         splitRun.fGlyphData->positions.push_back({glyphPosVal, fGlyphData->positions[startClusterPos].fY});
345         splitRun.fGlyphData->offsets.push_back(fGlyphData->offsets[startClusterPos]);
346         splitRun.fGlyphData->clusterIndexes.push_back(fGlyphData->clusterIndexes[startClusterPos]);
347         splitRun.fGlyphData->advances.push_back(fGlyphData->advances[startClusterPos]);
348         splitRun.fHalfLetterspacings.push_back(fHalfLetterspacings[startClusterPos]);
349     }
350 
351     // Generate for ghost cluster
352     glyphPosVal = fGlyphData->positions[startClusterPos].fX - posOffset;
353     splitRun.fGlyphData->positions.push_back({glyphPosVal, fGlyphData->positions[startClusterPos].fY});
354     splitRun.fGlyphData->offsets.push_back({0.0f, 0.0f});
355     splitRun.fGlyphData->clusterIndexes.push_back(fGlyphData->clusterIndexes[startClusterPos]);
356     splitRun.fGlyphData->advances.push_back({0.0f, 0.0f});
357     splitRun.fPositions = splitRun.fGlyphData->positions;
358     splitRun.fOffsets = splitRun.fGlyphData->offsets;
359     splitRun.fClusterIndexes = splitRun.fGlyphData->clusterIndexes;
360     splitRun.fGlyphAdvances = splitRun.fGlyphData->advances;
361     splitRun.fGlyphs = splitRun.fGlyphData->glyphs;
362     splitRun.fAdvance = {glyphPosVal, fAdvance.fY};
363     splitRun.fHalfLetterspacings.push_back(fHalfLetterspacings[startClusterPos]);
364 }
365 
generateSplitRun(Run & splitRun,const SplitPoint & splitPoint)366 void Run::generateSplitRun(Run& splitRun, const SplitPoint& splitPoint) {
367     if (fGlyphData->positions.empty()) {
368         return;
369     }
370     size_t tailIndex = splitPoint.tailClusterIndex;
371     size_t headIndex = splitPoint.headClusterIndex;
372     const TextLine& splitLine = fOwner->lines()[splitPoint.lineIndex];
373     updateSplitRunRangeInfo(splitRun, splitLine, headIndex, tailIndex);
374     size_t startClusterPos = findSplitClusterPos(headIndex - fClusterStart);
375     size_t endClusterPos = findSplitClusterPos(tailIndex - fClusterStart);
376     if (endClusterPos >= clusterIndexes().size() || startClusterPos >= clusterIndexes().size()) {
377         LOGE("Failed to find clusterPos by binary search algorithm");
378         return;
379     }
380     updateSplitRunMesureInfo(splitRun, startClusterPos, endClusterPos);
381 }
382 #endif
383 
newRunBuffer()384 SkShaper::RunHandler::Buffer Run::newRunBuffer() {
385 #ifdef ENABLE_TEXT_ENHANCE
386     return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset, fGlyphAdvances.data()};
387 #else
388     return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
389 #endif
390 }
391 
392 #ifdef ENABLE_TEXT_ENHANCE
usingAutoSpaceWidth(const Cluster & cluster) const393 SkScalar Run::usingAutoSpaceWidth(const Cluster& cluster) const
394 {
395     return fOwner->clusterUsingAutoSpaceWidth(cluster);
396 }
copyTo(RSTextBlobBuilder & builder,size_t pos,size_t size) const397 void Run::copyTo(RSTextBlobBuilder& builder, size_t pos, size_t size) const {
398     SkASSERT(pos + size <= this->size());
399     const auto& blobBuffer = builder.AllocRunPos(fFont, SkToInt(size));
400     if (!blobBuffer.glyphs || !fGlyphs.data()) {
401         return;
402     }
403 
404     sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
405     auto points = reinterpret_cast<SkPoint*>(blobBuffer.pos);
406 
407     for (size_t i = 0; i < size; ++i) {
408         auto point = fPositions[i + pos];
409         if (!fJustificationShifts.empty()) {
410             point.fX += fJustificationShifts[i + pos].fX;
411         }
412         if (!fAutoSpacings.empty()) {
413             point.fX += fAutoSpacings[i + pos].fX;
414         }
415         point += fOffsets[i + pos];
416         points[i] = point;
417     }
418 }
419 
copyTo(RSTextBlobBuilder & builder,const RSPath * path,float hOffset,float vOffset,float fTextShift,size_t pos,size_t size) const420 void Run::copyTo(RSTextBlobBuilder& builder,
421                  const RSPath* path,
422                  float hOffset,
423                  float vOffset,
424                  float fTextShift,
425                  size_t pos,
426                  size_t size) const {
427     SkASSERT(pos + size <= this->size());
428     auto& blobBuffer = builder.AllocRunRSXform(fFont, SkToInt(size));
429     if (!blobBuffer.glyphs || !fGlyphs.data()) {
430         return;
431     }
432 
433     sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
434     std::vector<float> widths(size);
435     fFont.GetWidths(blobBuffer.glyphs, size, widths.data());
436     RSXform* xform = reinterpret_cast<RSXform*>(blobBuffer.pos);
437     for (size_t i = 0; i < size; ++i) {
438         float halfWidth = widths[i + pos] * 0.5f;
439         float x = hOffset + posX(i + pos) + halfWidth + fOffsets[i + pos].x() + fTextShift;
440         if (!fJustificationShifts.empty()) {
441             x += fJustificationShifts[i + pos].fX;
442         }
443         if (!fAutoSpacings.empty()) {
444             x += fAutoSpacings[i + pos].fX;
445         }
446         RSPoint rsPos;
447         RSPoint rsTan;
448         if (!path->GetPositionAndTangent(x, rsPos, rsTan, false)) {
449             rsPos.Set(x, vOffset);
450             rsTan.Set(1, 0);
451         }
452         xform[i].cos_ = rsTan.GetX();
453         xform[i].sin_ = rsTan.GetY();
454         xform[i].tx_ = rsPos.GetX() - rsTan.GetY() * vOffset - halfWidth * rsTan.GetX();
455         xform[i].ty_ = rsPos.GetY() + rsTan.GetX() * vOffset - halfWidth * rsTan.GetY();
456     }
457 }
458 #else
copyTo(SkTextBlobBuilder & builder,size_t pos,size_t size) const459 void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size) const {
460     SkASSERT(pos + size <= this->size());
461     const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
462     sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
463 
464     for (size_t i = 0; i < size; ++i) {
465         auto point = fPositions[i + pos];
466         if (!fJustificationShifts.empty()) {
467             point.fX += fJustificationShifts[i + pos].fX;
468         }
469         point += fOffsets[i + pos];
470         blobBuffer.points()[i] = point;
471     }
472 }
473 #endif
474 
475 // Find a cluster range from text range (within one run)
476 // Cluster range is normalized ([start:end) start < end regardless of TextDirection
477 // Boolean value in triple indicates whether the cluster range was found or not
findLimitingClusters(TextRange text) const478 std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) const {
479     if (text.width() == 0) {
480         // Special Flutter case for "\n" and "...\n"
481         if (text.end > this->fTextRange.start) {
482             ClusterIndex index = fOwner->clusterIndex(text.end - 1);
483             return std::make_tuple(true, index, index);
484         } else {
485             return std::make_tuple(false, 0, 0);
486         }
487     }
488 
489     ClusterRange clusterRange;
490     bool found = true;
491     // Deal with the case when either start or end are not align with glyph cluster edge
492     // In such case we shift the text range to the right
493     // (cutting from the left and adding to the right)
494     if (leftToRight()) {
495         // LTR: [start:end)
496         found = clusterRange.start != fClusterRange.end;
497         clusterRange.start = fOwner->clusterIndex(text.start);
498         clusterRange.end = fOwner->clusterIndex(text.end - 1);
499     } else {
500         // RTL: (start:end]
501 #ifdef ENABLE_TEXT_ENHANCE
502         clusterRange.start = fOwner->clusterIndex(text.end - 1);
503         clusterRange.end = fOwner->clusterIndex(text.start);
504 #else
505         clusterRange.start = fOwner->clusterIndex(text.end);
506         clusterRange.end = fOwner->clusterIndex(text.start + 1);
507 #endif
508         found = clusterRange.end != fClusterRange.start;
509     }
510 
511     return std::make_tuple(
512             found,
513             clusterRange.start,
514             clusterRange.end);
515 }
516 
findLimitingGlyphClusters(TextRange text) const517 std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGlyphClusters(TextRange text) const {
518     TextIndex start = fOwner->findPreviousGlyphClusterBoundary(text.start);
519     TextIndex end = fOwner->findNextGlyphClusterBoundary(text.end);
520     return std::make_tuple(true, start, end);
521 }
522 
523 // Adjust the text to grapheme edges so the first grapheme start is in the text and the last grapheme start is in the text
524 // It actually means that the first grapheme is entirely in the text and the last grapheme does not have to be
525 // 12345 234 2:2 -> 2,5 4:4
findLimitingGraphemes(TextRange text) const526 std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGraphemes(TextRange text) const {
527     TextIndex start = fOwner->findPreviousGraphemeBoundary(text.start);
528     TextIndex end = fOwner->findNextGraphemeBoundary(text.end);
529     return std::make_tuple(true, start, end);
530 }
531 
iterateThroughClusters(const ClusterVisitor & visitor)532 void Run::iterateThroughClusters(const ClusterVisitor& visitor) {
533 
534     for (size_t index = 0; index < fClusterRange.width(); ++index) {
535         auto correctIndex = leftToRight() ? fClusterRange.start + index : fClusterRange.end - index - 1;
536         auto cluster = &fOwner->cluster(correctIndex);
537         visitor(cluster);
538     }
539 }
540 
addSpacesAtTheEnd(SkScalar space,Cluster * cluster)541 void Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
542     // Increment the run width
543     fAdvance.fX += space;
544     // Increment the cluster width
545     cluster->space(space);
546 }
547 
addSpacesEvenly(SkScalar space)548 SkScalar Run::addSpacesEvenly(SkScalar space) {
549     SkScalar shift = 0;
550 #ifdef ENABLE_TEXT_ENHANCE
551     if (this->size()) {
552         shift += space / PARAM_TWO;
553     }
554 #endif
555     for (size_t i = 0; i < this->size(); ++i) {
556         fPositions[i].fX += shift;
557 #ifdef ENABLE_TEXT_ENHANCE
558         fHalfLetterspacings[i] = space / PARAM_TWO;
559 #endif
560         shift += space;
561     }
562 #ifdef ENABLE_TEXT_ENHANCE
563     if (this->size()) {
564         shift -= space / PARAM_TWO;
565     }
566 #endif
567     fPositions[this->size()].fX += shift;
568     fAdvance.fX += shift;
569     return shift;
570 }
571 
572 #ifdef ENABLE_TEXT_ENHANCE
addSpacesEvenly(SkScalar space,Cluster * cluster)573 SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
574     // Offset all the glyphs in the cluster
575     SkScalar shift = 0;
576     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
577         fPositions[i].fX += shift;
578         fHalfLetterspacings[i] = space / PARAM_TWO;
579         shift += space;
580     }
581     if (this->size() == cluster->endPos()) {
582         // To make calculations easier
583         fPositions[cluster->endPos()].fX += shift;
584         fHalfLetterspacings[cluster->endPos()] = space / PARAM_TWO;
585     }
586     // Increment the run width
587     fAdvance.fX += shift;
588     // Increment the cluster width
589     cluster->space(shift);
590     cluster->setHalfLetterSpacing(space / PARAM_TWO);
591 
592     return shift;
593 }
594 
595 #else
addSpacesEvenly(SkScalar space,Cluster * cluster)596 SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
597     // Offset all the glyphs in the cluster
598     SkScalar shift = 0;
599     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
600         fPositions[i].fX += shift;
601         shift += space;
602     }
603     if (this->size() == cluster->endPos()) {
604         // To make calculations easier
605         fPositions[cluster->endPos()].fX += shift;
606     }
607     // Increment the run width
608     fAdvance.fX += shift;
609     // Increment the cluster width
610     cluster->space(shift);
611     cluster->setHalfLetterSpacing(space / 2);
612 
613     return shift;
614 }
615 #endif
616 
shift(const Cluster * cluster,SkScalar offset)617 void Run::shift(const Cluster* cluster, SkScalar offset) {
618     if (offset == 0) {
619         return;
620     }
621     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
622         fPositions[i].fX += offset;
623     }
624     if (this->size() == cluster->endPos()) {
625         // To make calculations easier
626         fPositions[cluster->endPos()].fX += offset;
627     }
628 }
629 
extend(const Cluster * cluster,SkScalar offset)630 void Run::extend(const Cluster* cluster, SkScalar offset) {
631     // Extend the cluster at the end
632     fPositions[cluster->endPos()].fX += offset;
633 }
634 
635 #ifdef ENABLE_TEXT_ENHANCE
extendClusterWidth(Cluster * cluster,SkScalar space)636 void Run::extendClusterWidth(Cluster* cluster, SkScalar space) {
637     addSpacesAtTheEnd(space, cluster);
638     for (size_t pos = cluster->endPos(); pos < fPositions.size(); pos++) {
639         fPositions[pos].fX += space;
640     }
641 }
642 
643 // Checks if the current line contains trailing spaces and current run is at the end of the line
isTrailingSpaceIncluded(const ClusterRange & fTextLineClusterRange,const ClusterRange & fTextLineGhostClusterRange) const644 bool Run::isTrailingSpaceIncluded(const ClusterRange& fTextLineClusterRange,
645     const ClusterRange& fTextLineGhostClusterRange) const {
646     return fTextLineGhostClusterRange.width() > 0 && this->clusterRange().width() > 0 &&
647            fTextLineClusterRange.width() > 0 && fTextLineGhostClusterRange.end != fTextLineClusterRange.end &&
648            fTextLineGhostClusterRange.end <= this->clusterRange().end &&
649            fTextLineGhostClusterRange.end > this->clusterRange().start;
650 }
651 
updatePlaceholderAlignmentIfNeeded(PlaceholderAlignment & alignment,TextVerticalAlign paragraphAlignment)652 void Run::updatePlaceholderAlignmentIfNeeded(PlaceholderAlignment& alignment, TextVerticalAlign paragraphAlignment) {
653     if (alignment != PlaceholderAlignment::kFollow) {
654         return;
655     }
656 
657     switch (paragraphAlignment) {
658         case TextVerticalAlign::TOP:
659             alignment = PlaceholderAlignment::kTop;
660             break;
661         case TextVerticalAlign::CENTER:
662             alignment = PlaceholderAlignment::kMiddle;
663             break;
664         case TextVerticalAlign::BOTTOM:
665             alignment = PlaceholderAlignment::kBottom;
666             break;
667         case TextVerticalAlign::BASELINE:
668             alignment = PlaceholderAlignment::kAboveBaseline;
669             break;
670         default:
671             break;
672     }
673 }
674 
updateMetrics(InternalLineMetrics * endlineMetrics)675 void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
676 
677     SkASSERT(isPlaceholder());
678     auto placeholderStyle = this->placeholderStyle();
679     // Difference between the placeholder baseline and the line bottom
680     SkScalar baselineAdjustment = 0;
681     switch (placeholderStyle->fBaseline) {
682         case TextBaseline::kAlphabetic:
683             break;
684 
685         case TextBaseline::kIdeographic:
686             baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
687             break;
688     }
689 
690     auto height = placeholderStyle->fHeight;
691     auto offset = placeholderStyle->fBaselineOffset;
692 
693     fFontMetrics.fLeading = 0;
694 
695     updatePlaceholderAlignmentIfNeeded(placeholderStyle->fAlignment,
696         fOwner->getParagraphStyle().getVerticalAlignment());
697 
698     switch (placeholderStyle->fAlignment) {
699         case PlaceholderAlignment::kBaseline:
700             fFontMetrics.fAscent = baselineAdjustment - height - offset;
701             fFontMetrics.fDescent = baselineAdjustment - offset;
702             break;
703 
704         case PlaceholderAlignment::kFollow:
705         case PlaceholderAlignment::kAboveBaseline:
706             fFontMetrics.fAscent = baselineAdjustment - height;
707             fFontMetrics.fDescent = baselineAdjustment;
708             break;
709 
710         case PlaceholderAlignment::kBelowBaseline:
711             fFontMetrics.fAscent = baselineAdjustment;
712             fFontMetrics.fDescent = baselineAdjustment + height;
713             break;
714 
715         case PlaceholderAlignment::kTop:
716             fFontMetrics.fAscent = endlineMetrics->ascent();
717             fFontMetrics.fDescent = height + fFontMetrics.fAscent;
718             break;
719 
720         case PlaceholderAlignment::kBottom:
721             fFontMetrics.fDescent = endlineMetrics->descent();
722             fFontMetrics.fAscent = fFontMetrics.fDescent - height;
723             break;
724 
725         case PlaceholderAlignment::kMiddle:
726             auto mid = (endlineMetrics->ascent() + endlineMetrics->descent()) / PARAM_TWO;
727             fFontMetrics.fDescent = mid + height / PARAM_TWO;
728             fFontMetrics.fAscent = mid - height / PARAM_TWO;
729             break;
730     }
731 
732     this->calculateMetrics();
733 
734     // Make sure the placeholder can fit the line
735     endlineMetrics->add(this);
736 }
737 #else
updateMetrics(InternalLineMetrics * endlineMetrics)738 void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
739 
740     SkASSERT(isPlaceholder());
741     auto placeholderStyle = this->placeholderStyle();
742     // Difference between the placeholder baseline and the line bottom
743     SkScalar baselineAdjustment = 0;
744     switch (placeholderStyle->fBaseline) {
745         case TextBaseline::kAlphabetic:
746             break;
747 
748         case TextBaseline::kIdeographic:
749             baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
750             break;
751     }
752 
753     auto height = placeholderStyle->fHeight;
754     auto offset = placeholderStyle->fBaselineOffset;
755 
756     fFontMetrics.fLeading = 0;
757     switch (placeholderStyle->fAlignment) {
758         case PlaceholderAlignment::kBaseline:
759             fFontMetrics.fAscent = baselineAdjustment - offset;
760             fFontMetrics.fDescent = baselineAdjustment + height - offset;
761             break;
762 
763         case PlaceholderAlignment::kAboveBaseline:
764             fFontMetrics.fAscent = baselineAdjustment - height;
765             fFontMetrics.fDescent = baselineAdjustment;
766             break;
767 
768         case PlaceholderAlignment::kBelowBaseline:
769             fFontMetrics.fAscent = baselineAdjustment;
770             fFontMetrics.fDescent = baselineAdjustment + height;
771             break;
772 
773         case PlaceholderAlignment::kTop:
774             fFontMetrics.fDescent = height + fFontMetrics.fAscent;
775             break;
776 
777         case PlaceholderAlignment::kBottom:
778             fFontMetrics.fAscent = fFontMetrics.fDescent - height;
779             break;
780 
781         case PlaceholderAlignment::kMiddle:
782             auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
783             fFontMetrics.fDescent = height/2.0 - mid;
784             fFontMetrics.fAscent =  - height/2.0 - mid;
785             break;
786     }
787 
788     this->calculateMetrics();
789 
790     // Make sure the placeholder can fit the line
791     endlineMetrics->add(this);
792 }
793 #endif
794 
sizeToChar(TextIndex ch) const795 SkScalar Cluster::sizeToChar(TextIndex ch) const {
796     if (ch < fTextRange.start || ch >= fTextRange.end) {
797         return 0;
798     }
799     auto shift = ch - fTextRange.start;
800     auto ratio = shift * 1.0 / fTextRange.width();
801 
802     return SkDoubleToScalar(fWidth * ratio);
803 }
804 
sizeFromChar(TextIndex ch) const805 SkScalar Cluster::sizeFromChar(TextIndex ch) const {
806     if (ch < fTextRange.start || ch >= fTextRange.end) {
807         return 0;
808     }
809     auto shift = fTextRange.end - ch - 1;
810     auto ratio = shift * 1.0 / fTextRange.width();
811 
812     return SkDoubleToScalar(fWidth * ratio);
813 }
814 
roundPos(SkScalar s) const815 size_t Cluster::roundPos(SkScalar s) const {
816     auto ratio = (s * 1.0) / fWidth;
817     return sk_double_floor2int(ratio * size());
818 }
819 
trimmedWidth(size_t pos) const820 SkScalar Cluster::trimmedWidth(size_t pos) const {
821     // Find the width until the pos and return the min between trimmedWidth and the width(pos)
822     // We don't have to take in account cluster shift since it's the same for 0 and for pos
823     auto& run = fOwner->run(fRunIndex);
824 #ifdef ENABLE_TEXT_ENHANCE
825     SkScalar delta = getHalfLetterSpacing() - run.halfLetterspacing(pos);
826     return std::min(run.positionX(pos) - run.positionX(fStart) + delta, fWidth);
827 #else
828     return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
829 #endif
830 }
831 
positionX(size_t pos) const832 SkScalar Run::positionX(size_t pos) const {
833 #ifdef ENABLE_TEXT_ENHANCE
834     return posX(pos) + (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY) +
835         (fAutoSpacings.empty() ? 0 : fAutoSpacings[pos].fY);
836 #else
837     return posX(pos) + (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY);
838 #endif
839 }
840 
841 #ifdef ENABLE_TEXT_ENHANCE
posX(size_t index) const842 SkScalar Run::posX(size_t index) const {
843     if (index < fPositions.size()) {
844         return fPositions[index].fX;
845     }
846     LOGE("index:%{public}zu,size:%{public}d", index, fPositions.size());
847     if (fPositions.empty()) {
848         return 0.0f;
849     }
850     return fPositions[fPositions.size() - 1].fX;
851 }
852 #endif
853 
placeholderStyle() const854 PlaceholderStyle* Run::placeholderStyle() const {
855     if (isPlaceholder()) {
856         return &fOwner->placeholders()[fPlaceholderIndex].fStyle;
857     } else {
858         return nullptr;
859     }
860 }
861 
isResolved() const862 bool Run::isResolved() const {
863     for (auto& glyph :fGlyphs) {
864         if (glyph == 0) {
865             return false;
866         }
867     }
868     return true;
869 }
870 
runOrNull() const871 Run* Cluster::runOrNull() const {
872     if (fRunIndex >= fOwner->runs().size()) {
873         return nullptr;
874     }
875     return &fOwner->run(fRunIndex);
876 }
877 
run() const878 Run& Cluster::run() const {
879     SkASSERT(fRunIndex < fOwner->runs().size());
880     return fOwner->run(fRunIndex);
881 }
882 
883 #ifdef ENABLE_TEXT_ENHANCE
font() const884 RSFont Cluster::font() const {
885 #else
886 SkFont Cluster::font() const {
887 #endif
888     SkASSERT(fRunIndex < fOwner->runs().size());
889     return fOwner->run(fRunIndex).font();
890 }
891 
892 bool Cluster::isSoftBreak() const {
893     return fOwner->codeUnitHasProperty(fTextRange.end,
894                                        SkUnicode::CodeUnitFlags::kSoftLineBreakBefore);
895 }
896 
897 bool Cluster::isGraphemeBreak() const {
898     return fOwner->codeUnitHasProperty(fTextRange.end, SkUnicode::CodeUnitFlags::kGraphemeStart);
899 }
900 
901 #ifdef ENABLE_TEXT_ENHANCE
902 bool Cluster::isStartCombineBreak() const {
903     return fOwner->codeUnitHasProperty(fTextRange.start, SkUnicode::CodeUnitFlags::kCombine);
904 }
905 
906 bool Cluster::isEndCombineBreak() const {
907     return fOwner->codeUnitHasProperty(fTextRange.end, SkUnicode::CodeUnitFlags::kCombine);
908 }
909 #endif
910 }  // namespace textlayout
911 }  // namespace skia
912