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