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