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