1cb93a386Sopenharmony_ci// Copyright 2019 Google LLC. 2cb93a386Sopenharmony_ci#include "include/core/SkFontMetrics.h" 3cb93a386Sopenharmony_ci#include "include/core/SkTextBlob.h" 4cb93a386Sopenharmony_ci#include "include/private/SkFloatingPoint.h" 5cb93a386Sopenharmony_ci#include "include/private/SkMalloc.h" 6cb93a386Sopenharmony_ci#include "include/private/SkTo.h" 7cb93a386Sopenharmony_ci#include "modules/skparagraph/include/DartTypes.h" 8cb93a386Sopenharmony_ci#include "modules/skparagraph/include/TextStyle.h" 9cb93a386Sopenharmony_ci#include "modules/skparagraph/src/ParagraphImpl.h" 10cb93a386Sopenharmony_ci#include "modules/skparagraph/src/Run.h" 11cb93a386Sopenharmony_ci#include "modules/skshaper/include/SkShaper.h" 12cb93a386Sopenharmony_ci#include "src/utils/SkUTF.h" 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 15cb93a386Sopenharmony_ci#include "include/FontCollection.h" 16cb93a386Sopenharmony_ci#include "log.h" 17cb93a386Sopenharmony_ci#endif 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_cinamespace skia { 20cb93a386Sopenharmony_cinamespace textlayout { 21cb93a386Sopenharmony_ciconstexpr SkScalar PARAM_TWO = 2.0; 22cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 23cb93a386Sopenharmony_ci// 1px font size "HarmonyOS Sans" metrics 24cb93a386Sopenharmony_ciconstexpr SkScalar DEFAULT_TOP = -1.056; 25cb93a386Sopenharmony_ciconstexpr SkScalar DEFAULT_BOTTOM = 0.271; 26cb93a386Sopenharmony_ciconstexpr SkScalar DEFAULT_ASCENT = -0.928; 27cb93a386Sopenharmony_ciconstexpr SkScalar DEFAULT_DESCENT = 0.244; 28cb93a386Sopenharmony_cistruct ScaleParam { 29cb93a386Sopenharmony_ci SkScalar fontScale; 30cb93a386Sopenharmony_ci SkScalar baselineShiftScale; 31cb93a386Sopenharmony_ci}; 32cb93a386Sopenharmony_ci// unordered_map<familyName, ScaleParam>: compress <familyName> font height, shift font baseline. 33cb93a386Sopenharmony_ci// target font size = font size * ScaleParam.scale. 34cb93a386Sopenharmony_ci// target baseline = baseline - height * font size * ScaleParam.baselineShiftScale. 35cb93a386Sopenharmony_ciconst std::unordered_map<std::string, ScaleParam> FONT_FAMILY_COMPRESSION_CONFIG = { 36cb93a386Sopenharmony_ci {"Noto Serif Tibetan", ScaleParam{ .fontScale = 0.79, .baselineShiftScale = 0.1 }}, 37cb93a386Sopenharmony_ci {"Noto Sans Tibetan", ScaleParam{ .fontScale = 0.79, .baselineShiftScale = 0.1 }}, 38cb93a386Sopenharmony_ci}; 39cb93a386Sopenharmony_ciconst std::unordered_map<std::string, ScaleParam> FONT_FAMILY_COMPRESSION_WITH_HEIGHT_ADAPTER_CONFIG = { 40cb93a386Sopenharmony_ci {"Noto Serif Tibetan", ScaleParam{ .fontScale = 0.85, .baselineShiftScale = 0.11 }}, 41cb93a386Sopenharmony_ci {"Noto Sans Tibetan", ScaleParam{ .fontScale = 0.85, .baselineShiftScale = 0.11 }}, 42cb93a386Sopenharmony_ci}; 43cb93a386Sopenharmony_ciconst ScaleParam DEFAULT_SCALE_PARAM = ScaleParam{ .fontScale = 0, .baselineShiftScale = 0 }; 44cb93a386Sopenharmony_cienum FontCompressionStatus { 45cb93a386Sopenharmony_ci UNDEFINED, // undefined font, the typeface is null. 46cb93a386Sopenharmony_ci SYSTEM, // system font, need to be compressed. 47cb93a386Sopenharmony_ci CUSTOM, // custom font, doesn't need to be compressed. 48cb93a386Sopenharmony_ci}; 49cb93a386Sopenharmony_ci// the font padding does not take effect for these font families. 50cb93a386Sopenharmony_ciconst std::unordered_set<std::string> FONT_PADDING_NOT_EFFECT_FAMILY = { 51cb93a386Sopenharmony_ci "Harmony Clock_01", 52cb93a386Sopenharmony_ci "Harmony Clock_02", 53cb93a386Sopenharmony_ci "Harmony Clock_03", 54cb93a386Sopenharmony_ci "Harmony Clock_04", 55cb93a386Sopenharmony_ci "Harmony Clock_05", 56cb93a386Sopenharmony_ci "Harmony Clock_06", 57cb93a386Sopenharmony_ci "Harmony Clock_07", 58cb93a386Sopenharmony_ci "Harmony Clock_08", 59cb93a386Sopenharmony_ci// symbol: need to ensure "the symbol height = the font size". 60cb93a386Sopenharmony_ci// so the height compression is not enabled for symbol. 61cb93a386Sopenharmony_ci "HM Symbol", 62cb93a386Sopenharmony_ci}; 63cb93a386Sopenharmony_ci 64cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT 65cb93a386Sopenharmony_ciFontCompressionStatus getFontCompressionStatus(const RSFont& font) 66cb93a386Sopenharmony_ci{ 67cb93a386Sopenharmony_ci auto typeface = font.GetTypeface(); 68cb93a386Sopenharmony_ci if (typeface == nullptr) { 69cb93a386Sopenharmony_ci return FontCompressionStatus::UNDEFINED; 70cb93a386Sopenharmony_ci } 71cb93a386Sopenharmony_ci return typeface->IsCustomTypeface() ? FontCompressionStatus::CUSTOM : FontCompressionStatus::SYSTEM; 72cb93a386Sopenharmony_ci} 73cb93a386Sopenharmony_cistd::string getFamilyNameFromFont(const RSFont& font) 74cb93a386Sopenharmony_ci{ 75cb93a386Sopenharmony_ci auto typeface = font.GetTypeface(); 76cb93a386Sopenharmony_ci return typeface == nullptr ? "" : typeface->GetFamilyName(); 77cb93a386Sopenharmony_ci} 78cb93a386Sopenharmony_ci#else 79cb93a386Sopenharmony_ciFontCompressionStatus getFontCompressionStatus(const SkFont& font) 80cb93a386Sopenharmony_ci{ 81cb93a386Sopenharmony_ci auto typeface = font.refTypeface(); 82cb93a386Sopenharmony_ci if (typeface == nullptr) { 83cb93a386Sopenharmony_ci return FontCompressionStatus::UNDEFINED; 84cb93a386Sopenharmony_ci } 85cb93a386Sopenharmony_ci return typeface->isCustomTypeface() ? FontCompressionStatus::CUSTOM : FontCompressionStatus::SYSTEM; 86cb93a386Sopenharmony_ci} 87cb93a386Sopenharmony_cistd::string getFamilyNameFromFont(const SkFont& font) 88cb93a386Sopenharmony_ci{ 89cb93a386Sopenharmony_ci auto typeface = font.refTypeface(); 90cb93a386Sopenharmony_ci if (typeface == nullptr) { 91cb93a386Sopenharmony_ci return ""; 92cb93a386Sopenharmony_ci } 93cb93a386Sopenharmony_ci SkString familyName; 94cb93a386Sopenharmony_ci typeface->getFamilyName(&familyName); 95cb93a386Sopenharmony_ci return std::string(familyName.c_str(), familyName.size()); 96cb93a386Sopenharmony_ci} 97cb93a386Sopenharmony_ci#endif 98cb93a386Sopenharmony_ci 99cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT 100cb93a386Sopenharmony_ciconst ScaleParam& findCompressionConfigWithFont(const RSFont& font) 101cb93a386Sopenharmony_ci#else 102cb93a386Sopenharmony_ciconst ScaleParam& findCompressionConfigWithFont(const SkFont& font) 103cb93a386Sopenharmony_ci#endif 104cb93a386Sopenharmony_ci{ 105cb93a386Sopenharmony_ci auto fontCompressionStatus = getFontCompressionStatus(font); 106cb93a386Sopenharmony_ci if (fontCompressionStatus != FontCompressionStatus::SYSTEM) { 107cb93a386Sopenharmony_ci return DEFAULT_SCALE_PARAM; 108cb93a386Sopenharmony_ci } 109cb93a386Sopenharmony_ci 110cb93a386Sopenharmony_ci const auto& config = FontCollection::IsAdapterTextHeightEnabled() ? 111cb93a386Sopenharmony_ci FONT_FAMILY_COMPRESSION_WITH_HEIGHT_ADAPTER_CONFIG : FONT_FAMILY_COMPRESSION_CONFIG; 112cb93a386Sopenharmony_ci std::string familyName = getFamilyNameFromFont(font); 113cb93a386Sopenharmony_ci auto iter = config.find(familyName); 114cb93a386Sopenharmony_ci if (iter == config.end()) { 115cb93a386Sopenharmony_ci return DEFAULT_SCALE_PARAM; 116cb93a386Sopenharmony_ci } 117cb93a386Sopenharmony_ci return iter->second; 118cb93a386Sopenharmony_ci} 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT 121cb93a386Sopenharmony_civoid metricsIncludeFontPadding(RSFontMetrics* metrics, const RSFont& font) 122cb93a386Sopenharmony_ci#else 123cb93a386Sopenharmony_civoid metricsIncludeFontPadding(SkFontMetrics* metrics, const SkFont& font) 124cb93a386Sopenharmony_ci#endif 125cb93a386Sopenharmony_ci{ 126cb93a386Sopenharmony_ci if (metrics == nullptr) { 127cb93a386Sopenharmony_ci return; 128cb93a386Sopenharmony_ci } 129cb93a386Sopenharmony_ci auto fontCompressionStatus = getFontCompressionStatus(font); 130cb93a386Sopenharmony_ci if (fontCompressionStatus == FontCompressionStatus::UNDEFINED) { 131cb93a386Sopenharmony_ci return; 132cb93a386Sopenharmony_ci } 133cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT 134cb93a386Sopenharmony_ci SkScalar fontSize = font.GetSize(); 135cb93a386Sopenharmony_ci#else 136cb93a386Sopenharmony_ci SkScalar fontSize = font.getSize(); 137cb93a386Sopenharmony_ci#endif 138cb93a386Sopenharmony_ci if (!FontCollection::IsAdapterTextHeightEnabled()) { 139cb93a386Sopenharmony_ci if (fontCompressionStatus == FontCompressionStatus::SYSTEM && 140cb93a386Sopenharmony_ci !SkScalarNearlyZero(findCompressionConfigWithFont(font).fontScale)) { 141cb93a386Sopenharmony_ci metrics->fAscent = DEFAULT_ASCENT * fontSize; 142cb93a386Sopenharmony_ci metrics->fDescent = DEFAULT_DESCENT * fontSize; 143cb93a386Sopenharmony_ci } 144cb93a386Sopenharmony_ci return; 145cb93a386Sopenharmony_ci } 146cb93a386Sopenharmony_ci 147cb93a386Sopenharmony_ci std::string curFamilyName = getFamilyNameFromFont(font); 148cb93a386Sopenharmony_ci auto setIter = FONT_PADDING_NOT_EFFECT_FAMILY.find(curFamilyName); 149cb93a386Sopenharmony_ci if (setIter == FONT_PADDING_NOT_EFFECT_FAMILY.end()) { 150cb93a386Sopenharmony_ci if (fontCompressionStatus == FontCompressionStatus::SYSTEM) { 151cb93a386Sopenharmony_ci metrics->fAscent = DEFAULT_TOP * fontSize; 152cb93a386Sopenharmony_ci metrics->fDescent = DEFAULT_BOTTOM * fontSize; 153cb93a386Sopenharmony_ci return; 154cb93a386Sopenharmony_ci } 155cb93a386Sopenharmony_ci // use top and bottom as ascent and descent. 156cb93a386Sopenharmony_ci // calculate height with top and bottom.(includeFontPadding) 157cb93a386Sopenharmony_ci metrics->fAscent = metrics->fTop; 158cb93a386Sopenharmony_ci metrics->fDescent = metrics->fBottom; 159cb93a386Sopenharmony_ci } 160cb93a386Sopenharmony_ci} 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT 163cb93a386Sopenharmony_civoid scaleFontWithCompressionConfig(RSFont& font, ScaleOP op) 164cb93a386Sopenharmony_ci{ 165cb93a386Sopenharmony_ci SkScalar fontSize = font.GetSize(); 166cb93a386Sopenharmony_ci#else 167cb93a386Sopenharmony_civoid scaleFontWithCompressionConfig(SkFont& font, ScaleOP op) 168cb93a386Sopenharmony_ci{ 169cb93a386Sopenharmony_ci SkScalar fontSize = font.getSize(); 170cb93a386Sopenharmony_ci#endif 171cb93a386Sopenharmony_ci auto config = findCompressionConfigWithFont(font); 172cb93a386Sopenharmony_ci if (SkScalarNearlyZero(config.fontScale)) { 173cb93a386Sopenharmony_ci return; 174cb93a386Sopenharmony_ci } 175cb93a386Sopenharmony_ci switch (op) { 176cb93a386Sopenharmony_ci case ScaleOP::COMPRESS: 177cb93a386Sopenharmony_ci fontSize *= config.fontScale; 178cb93a386Sopenharmony_ci break; 179cb93a386Sopenharmony_ci case ScaleOP::DECOMPRESS: 180cb93a386Sopenharmony_ci fontSize /= config.fontScale; 181cb93a386Sopenharmony_ci break; 182cb93a386Sopenharmony_ci default: 183cb93a386Sopenharmony_ci return; 184cb93a386Sopenharmony_ci } 185cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT 186cb93a386Sopenharmony_ci font.SetSize(fontSize); 187cb93a386Sopenharmony_ci#else 188cb93a386Sopenharmony_ci font.setSize(fontSize); 189cb93a386Sopenharmony_ci#endif 190cb93a386Sopenharmony_ci} 191cb93a386Sopenharmony_ci#endif 192cb93a386Sopenharmony_ci 193cb93a386Sopenharmony_ciRun::Run(ParagraphImpl* owner, 194cb93a386Sopenharmony_ci const SkShaper::RunHandler::RunInfo& info, 195cb93a386Sopenharmony_ci size_t firstChar, 196cb93a386Sopenharmony_ci SkScalar heightMultiplier, 197cb93a386Sopenharmony_ci bool useHalfLeading, 198cb93a386Sopenharmony_ci SkScalar baselineShift, 199cb93a386Sopenharmony_ci size_t index, 200cb93a386Sopenharmony_ci SkScalar offsetX) 201cb93a386Sopenharmony_ci : fOwner(owner) 202cb93a386Sopenharmony_ci , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end()) 203cb93a386Sopenharmony_ci , fClusterRange(EMPTY_CLUSTERS) 204cb93a386Sopenharmony_ci , fFont(info.fFont) 205cb93a386Sopenharmony_ci , fClusterStart(firstChar) 206cb93a386Sopenharmony_ci , fGlyphData(std::make_shared<GlyphData>()) 207cb93a386Sopenharmony_ci , fGlyphs(fGlyphData->glyphs) 208cb93a386Sopenharmony_ci , fPositions(fGlyphData->positions) 209cb93a386Sopenharmony_ci , fOffsets(fGlyphData->offsets) 210cb93a386Sopenharmony_ci , fClusterIndexes(fGlyphData->clusterIndexes) 211cb93a386Sopenharmony_ci , fHeightMultiplier(heightMultiplier) 212cb93a386Sopenharmony_ci , fUseHalfLeading(useHalfLeading) 213cb93a386Sopenharmony_ci , fBaselineShift(baselineShift) 214cb93a386Sopenharmony_ci{ 215cb93a386Sopenharmony_ci fBidiLevel = info.fBidiLevel; 216cb93a386Sopenharmony_ci fAdvance = info.fAdvance; 217cb93a386Sopenharmony_ci fIndex = index; 218cb93a386Sopenharmony_ci fUtf8Range = info.utf8Range; 219cb93a386Sopenharmony_ci fOffset = SkVector::Make(offsetX, 0); 220cb93a386Sopenharmony_ci 221cb93a386Sopenharmony_ci fGlyphs.push_back_n(info.glyphCount); 222cb93a386Sopenharmony_ci fPositions.push_back_n(info.glyphCount + 1); 223cb93a386Sopenharmony_ci fOffsets.push_back_n(info.glyphCount + 1); 224cb93a386Sopenharmony_ci fClusterIndexes.push_back_n(info.glyphCount + 1); 225cb93a386Sopenharmony_ci fHalfLetterspacings.push_back_n(info.glyphCount + 1); 226cb93a386Sopenharmony_ci std::fill(fHalfLetterspacings.begin(), fHalfLetterspacings.end(), 0.0); 227cb93a386Sopenharmony_ci#ifndef USE_SKIA_TXT 228cb93a386Sopenharmony_ci info.fFont.getMetrics(&fFontMetrics); 229cb93a386Sopenharmony_ci#else 230cb93a386Sopenharmony_ci info.fFont.GetMetrics(&fFontMetrics); 231cb93a386Sopenharmony_ci#endif 232cb93a386Sopenharmony_ci 233cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 234cb93a386Sopenharmony_ci auto decompressFont = info.fFont; 235cb93a386Sopenharmony_ci scaleFontWithCompressionConfig(decompressFont, ScaleOP::DECOMPRESS); 236cb93a386Sopenharmony_ci metricsIncludeFontPadding(&fFontMetrics, decompressFont); 237cb93a386Sopenharmony_ci auto config = findCompressionConfigWithFont(decompressFont); 238cb93a386Sopenharmony_ci fCompressionBaselineShift = (fFontMetrics.fDescent - fFontMetrics.fAscent) * config.baselineShiftScale; 239cb93a386Sopenharmony_ci#endif 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci this->calculateMetrics(); 242cb93a386Sopenharmony_ci 243cb93a386Sopenharmony_ci // To make edge cases easier: 244cb93a386Sopenharmony_ci fPositions[info.glyphCount] = fOffset + fAdvance; 245cb93a386Sopenharmony_ci fOffsets[info.glyphCount] = {0, 0}; 246cb93a386Sopenharmony_ci fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin(); 247cb93a386Sopenharmony_ci fEllipsis = false; 248cb93a386Sopenharmony_ci fPlaceholderIndex = std::numeric_limits<size_t>::max(); 249cb93a386Sopenharmony_ci} 250cb93a386Sopenharmony_ci 251cb93a386Sopenharmony_civoid Run::calculateMetrics() { 252cb93a386Sopenharmony_ci fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5; 253cb93a386Sopenharmony_ci fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5; 254cb93a386Sopenharmony_ci fCorrectLeading = 0; 255cb93a386Sopenharmony_ci if (SkScalarNearlyZero(fHeightMultiplier)) { 256cb93a386Sopenharmony_ci return; 257cb93a386Sopenharmony_ci } 258cb93a386Sopenharmony_ci#ifndef USE_SKIA_TXT 259cb93a386Sopenharmony_ci const auto runHeight = fHeightMultiplier * fFont.getSize(); 260cb93a386Sopenharmony_ci#else 261cb93a386Sopenharmony_ci const auto runHeight = fHeightMultiplier * fFont.GetSize(); 262cb93a386Sopenharmony_ci#endif 263cb93a386Sopenharmony_ci const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent; 264cb93a386Sopenharmony_ci if (fUseHalfLeading) { 265cb93a386Sopenharmony_ci const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2; 266cb93a386Sopenharmony_ci fCorrectAscent -= extraLeading; 267cb93a386Sopenharmony_ci fCorrectDescent += extraLeading; 268cb93a386Sopenharmony_ci } else { 269cb93a386Sopenharmony_ci const auto multiplier = runHeight / fontIntrinsicHeight; 270cb93a386Sopenharmony_ci fCorrectAscent *= multiplier; 271cb93a386Sopenharmony_ci fCorrectDescent *= multiplier; 272cb93a386Sopenharmony_ci } 273cb93a386Sopenharmony_ci // If we shift the baseline we need to make sure the shifted text fits the line 274cb93a386Sopenharmony_ci fCorrectAscent += fBaselineShift; 275cb93a386Sopenharmony_ci fCorrectDescent += fBaselineShift; 276cb93a386Sopenharmony_ci} 277cb93a386Sopenharmony_ci 278cb93a386Sopenharmony_ciSkShaper::RunHandler::Buffer Run::newRunBuffer() { 279cb93a386Sopenharmony_ci return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset}; 280cb93a386Sopenharmony_ci} 281cb93a386Sopenharmony_ci 282cb93a386Sopenharmony_ci#ifndef USE_SKIA_TXT 283cb93a386Sopenharmony_civoid Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size) const { 284cb93a386Sopenharmony_ci SkASSERT(pos + size <= this->size()); 285cb93a386Sopenharmony_ci const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size)); 286cb93a386Sopenharmony_ci sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID)); 287cb93a386Sopenharmony_ci 288cb93a386Sopenharmony_ci for (size_t i = 0; i < size; ++i) { 289cb93a386Sopenharmony_ci auto point = fPositions[i + pos]; 290cb93a386Sopenharmony_ci if (!fJustificationShifts.empty()) { 291cb93a386Sopenharmony_ci point.fX += fJustificationShifts[i + pos].fX; 292cb93a386Sopenharmony_ci } 293cb93a386Sopenharmony_ci if (!fAutoSpacings.empty()) { 294cb93a386Sopenharmony_ci point.fX += fAutoSpacings[i + pos].fX; 295cb93a386Sopenharmony_ci } 296cb93a386Sopenharmony_ci point += fOffsets[i + pos]; 297cb93a386Sopenharmony_ci blobBuffer.points()[i] = point; 298cb93a386Sopenharmony_ci } 299cb93a386Sopenharmony_ci} 300cb93a386Sopenharmony_ci#else 301cb93a386Sopenharmony_civoid Run::copyTo(RSTextBlobBuilder& builder, size_t pos, size_t size) const { 302cb93a386Sopenharmony_ci SkASSERT(pos + size <= this->size()); 303cb93a386Sopenharmony_ci const auto& blobBuffer = builder.AllocRunPos(fFont, SkToInt(size)); 304cb93a386Sopenharmony_ci sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID)); 305cb93a386Sopenharmony_ci auto points = reinterpret_cast<SkPoint*>(blobBuffer.pos); 306cb93a386Sopenharmony_ci 307cb93a386Sopenharmony_ci for (size_t i = 0; i < size; ++i) { 308cb93a386Sopenharmony_ci auto point = fPositions[i + pos]; 309cb93a386Sopenharmony_ci if (!fJustificationShifts.empty()) { 310cb93a386Sopenharmony_ci point.fX += fJustificationShifts[i + pos].fX; 311cb93a386Sopenharmony_ci } 312cb93a386Sopenharmony_ci if (!fAutoSpacings.empty()) { 313cb93a386Sopenharmony_ci point.fX += fAutoSpacings[i + pos].fX; 314cb93a386Sopenharmony_ci } 315cb93a386Sopenharmony_ci point += fOffsets[i + pos]; 316cb93a386Sopenharmony_ci points[i] = point; 317cb93a386Sopenharmony_ci } 318cb93a386Sopenharmony_ci} 319cb93a386Sopenharmony_ci 320cb93a386Sopenharmony_civoid Run::copyTo(RSTextBlobBuilder& builder, 321cb93a386Sopenharmony_ci const RSPath* path, 322cb93a386Sopenharmony_ci float hOffset, 323cb93a386Sopenharmony_ci float vOffset, 324cb93a386Sopenharmony_ci float fTextShift, 325cb93a386Sopenharmony_ci size_t pos, 326cb93a386Sopenharmony_ci size_t size) const { 327cb93a386Sopenharmony_ci SkASSERT(pos + size <= this->size()); 328cb93a386Sopenharmony_ci auto& blobBuffer = builder.AllocRunRSXform(fFont, SkToInt(size)); 329cb93a386Sopenharmony_ci sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID)); 330cb93a386Sopenharmony_ci std::vector<float> widths(size); 331cb93a386Sopenharmony_ci fFont.GetWidths(blobBuffer.glyphs, size, widths.data()); 332cb93a386Sopenharmony_ci RSXform* xform = reinterpret_cast<RSXform*>(blobBuffer.pos); 333cb93a386Sopenharmony_ci for (size_t i = 0; i < size; ++i) { 334cb93a386Sopenharmony_ci float halfWidth = widths[i + pos] * 0.5f; 335cb93a386Sopenharmony_ci float x = hOffset + posX(i + pos) + halfWidth + fOffsets[i + pos].x() + fTextShift; 336cb93a386Sopenharmony_ci if (!fJustificationShifts.empty()) { 337cb93a386Sopenharmony_ci x += fJustificationShifts[i + pos].fX; 338cb93a386Sopenharmony_ci } 339cb93a386Sopenharmony_ci RSPoint rsPos; 340cb93a386Sopenharmony_ci RSPoint rsTan; 341cb93a386Sopenharmony_ci if (!path->GetPositionAndTangent(x, rsPos, rsTan, false)) { 342cb93a386Sopenharmony_ci rsPos.Set(x, vOffset); 343cb93a386Sopenharmony_ci rsTan.Set(1, 0); 344cb93a386Sopenharmony_ci } 345cb93a386Sopenharmony_ci xform[i].cos_ = rsTan.GetX(); 346cb93a386Sopenharmony_ci xform[i].sin_ = rsTan.GetY(); 347cb93a386Sopenharmony_ci xform[i].tx_ = rsPos.GetX() - rsTan.GetY() * vOffset - halfWidth * rsTan.GetX(); 348cb93a386Sopenharmony_ci xform[i].ty_ = rsPos.GetY() + rsTan.GetX() * vOffset - halfWidth * rsTan.GetY(); 349cb93a386Sopenharmony_ci } 350cb93a386Sopenharmony_ci} 351cb93a386Sopenharmony_ci#endif 352cb93a386Sopenharmony_ci 353cb93a386Sopenharmony_ci// Find a cluster range from text range (within one run) 354cb93a386Sopenharmony_ci// Cluster range is normalized ([start:end) start < end regardless of TextDirection 355cb93a386Sopenharmony_ci// Boolean value in triple indicates whether the cluster range was found or not 356cb93a386Sopenharmony_cistd::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) const { 357cb93a386Sopenharmony_ci if (text.width() == 0) { 358cb93a386Sopenharmony_ci // Special Flutter case for "\n" and "...\n" 359cb93a386Sopenharmony_ci if (text.end > this->fTextRange.start) { 360cb93a386Sopenharmony_ci ClusterIndex index = fOwner->clusterIndex(text.end - 1); 361cb93a386Sopenharmony_ci return std::make_tuple(true, index, index); 362cb93a386Sopenharmony_ci } else { 363cb93a386Sopenharmony_ci return std::make_tuple(false, 0, 0); 364cb93a386Sopenharmony_ci } 365cb93a386Sopenharmony_ci } 366cb93a386Sopenharmony_ci 367cb93a386Sopenharmony_ci ClusterRange clusterRange; 368cb93a386Sopenharmony_ci bool found = true; 369cb93a386Sopenharmony_ci // Deal with the case when either start or end are not align with glyph cluster edge 370cb93a386Sopenharmony_ci // In such case we shift the text range to the right 371cb93a386Sopenharmony_ci // (cutting from the left and adding to the right) 372cb93a386Sopenharmony_ci if (leftToRight()) { 373cb93a386Sopenharmony_ci // LTR: [start:end) 374cb93a386Sopenharmony_ci found = clusterRange.start != fClusterRange.end; 375cb93a386Sopenharmony_ci clusterRange.start = fOwner->clusterIndex(text.start); 376cb93a386Sopenharmony_ci clusterRange.end = fOwner->clusterIndex(text.end - 1); 377cb93a386Sopenharmony_ci } else { 378cb93a386Sopenharmony_ci // RTL: (start:end] 379cb93a386Sopenharmony_ci clusterRange.start = fOwner->clusterIndex(text.end); 380cb93a386Sopenharmony_ci clusterRange.end = fOwner->clusterIndex(text.start + 1); 381cb93a386Sopenharmony_ci found = clusterRange.end != fClusterRange.start; 382cb93a386Sopenharmony_ci } 383cb93a386Sopenharmony_ci 384cb93a386Sopenharmony_ci return std::make_tuple( 385cb93a386Sopenharmony_ci found, 386cb93a386Sopenharmony_ci clusterRange.start, 387cb93a386Sopenharmony_ci clusterRange.end); 388cb93a386Sopenharmony_ci} 389cb93a386Sopenharmony_ci 390cb93a386Sopenharmony_cistd::tuple<bool, TextIndex, TextIndex> Run::findLimitingGlyphClusters(TextRange text) const { 391cb93a386Sopenharmony_ci TextIndex start = fOwner->findPreviousGlyphClusterBoundary(text.start); 392cb93a386Sopenharmony_ci TextIndex end = fOwner->findNextGlyphClusterBoundary(text.end); 393cb93a386Sopenharmony_ci return std::make_tuple(true, start, end); 394cb93a386Sopenharmony_ci} 395cb93a386Sopenharmony_ci 396cb93a386Sopenharmony_ci// Adjust the text to grapheme edges so the first grapheme start is in the text and the last grapheme start is in the text 397cb93a386Sopenharmony_ci// It actually means that the first grapheme is entirely in the text and the last grapheme does not have to be 398cb93a386Sopenharmony_ci// 12345 234 2:2 -> 2,5 4:4 399cb93a386Sopenharmony_cistd::tuple<bool, TextIndex, TextIndex> Run::findLimitingGraphemes(TextRange text) const { 400cb93a386Sopenharmony_ci TextIndex start = fOwner->findPreviousGraphemeBoundary(text.start); 401cb93a386Sopenharmony_ci TextIndex end = fOwner->findNextGraphemeBoundary(text.end); 402cb93a386Sopenharmony_ci return std::make_tuple(true, start, end); 403cb93a386Sopenharmony_ci} 404cb93a386Sopenharmony_ci 405cb93a386Sopenharmony_civoid Run::iterateThroughClusters(const ClusterVisitor& visitor) { 406cb93a386Sopenharmony_ci 407cb93a386Sopenharmony_ci for (size_t index = 0; index < fClusterRange.width(); ++index) { 408cb93a386Sopenharmony_ci auto correctIndex = leftToRight() ? fClusterRange.start + index : fClusterRange.end - index - 1; 409cb93a386Sopenharmony_ci auto cluster = &fOwner->cluster(correctIndex); 410cb93a386Sopenharmony_ci visitor(cluster); 411cb93a386Sopenharmony_ci } 412cb93a386Sopenharmony_ci} 413cb93a386Sopenharmony_ci 414cb93a386Sopenharmony_civoid Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) { 415cb93a386Sopenharmony_ci // Increment the run width 416cb93a386Sopenharmony_ci fAdvance.fX += space; 417cb93a386Sopenharmony_ci // Increment the cluster width 418cb93a386Sopenharmony_ci cluster->space(space); 419cb93a386Sopenharmony_ci} 420cb93a386Sopenharmony_ci 421cb93a386Sopenharmony_ciSkScalar Run::addSpacesEvenly(SkScalar space) { 422cb93a386Sopenharmony_ci SkScalar shift = 0; 423cb93a386Sopenharmony_ci if (this->size()) { 424cb93a386Sopenharmony_ci shift += space / PARAM_TWO; 425cb93a386Sopenharmony_ci } 426cb93a386Sopenharmony_ci for (size_t i = 0; i < this->size(); ++i) { 427cb93a386Sopenharmony_ci fPositions[i].fX += shift; 428cb93a386Sopenharmony_ci fHalfLetterspacings[i] = space / PARAM_TWO; 429cb93a386Sopenharmony_ci shift += space; 430cb93a386Sopenharmony_ci } 431cb93a386Sopenharmony_ci if (this->size()) { 432cb93a386Sopenharmony_ci shift -= space / PARAM_TWO; 433cb93a386Sopenharmony_ci } 434cb93a386Sopenharmony_ci fPositions[this->size()].fX += shift; 435cb93a386Sopenharmony_ci fAdvance.fX += shift; 436cb93a386Sopenharmony_ci return shift; 437cb93a386Sopenharmony_ci} 438cb93a386Sopenharmony_ci 439cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 440cb93a386Sopenharmony_ciSkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) { 441cb93a386Sopenharmony_ci // Offset all the glyphs in the cluster 442cb93a386Sopenharmony_ci SkScalar shift = 0; 443cb93a386Sopenharmony_ci for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) { 444cb93a386Sopenharmony_ci fPositions[i].fX += shift; 445cb93a386Sopenharmony_ci fHalfLetterspacings[i] = space / PARAM_TWO; 446cb93a386Sopenharmony_ci shift += space; 447cb93a386Sopenharmony_ci } 448cb93a386Sopenharmony_ci if (this->size() == cluster->endPos()) { 449cb93a386Sopenharmony_ci // To make calculations easier 450cb93a386Sopenharmony_ci fPositions[cluster->endPos()].fX += shift; 451cb93a386Sopenharmony_ci fHalfLetterspacings[cluster->endPos()] = space / PARAM_TWO; 452cb93a386Sopenharmony_ci } 453cb93a386Sopenharmony_ci // Increment the run width 454cb93a386Sopenharmony_ci fAdvance.fX += shift; 455cb93a386Sopenharmony_ci // Increment the cluster width 456cb93a386Sopenharmony_ci cluster->space(shift); 457cb93a386Sopenharmony_ci cluster->setHalfLetterSpacing(space / PARAM_TWO); 458cb93a386Sopenharmony_ci 459cb93a386Sopenharmony_ci return shift; 460cb93a386Sopenharmony_ci} 461cb93a386Sopenharmony_ci#else 462cb93a386Sopenharmony_ciSkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) { 463cb93a386Sopenharmony_ci // Offset all the glyphs in the cluster 464cb93a386Sopenharmony_ci SkScalar shift = 0; 465cb93a386Sopenharmony_ci for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) { 466cb93a386Sopenharmony_ci fPositions[i].fX += shift; 467cb93a386Sopenharmony_ci shift += space; 468cb93a386Sopenharmony_ci } 469cb93a386Sopenharmony_ci if (this->size() == cluster->endPos()) { 470cb93a386Sopenharmony_ci // To make calculations easier 471cb93a386Sopenharmony_ci fPositions[cluster->endPos()].fX += shift; 472cb93a386Sopenharmony_ci } 473cb93a386Sopenharmony_ci // Increment the run width 474cb93a386Sopenharmony_ci fAdvance.fX += shift; 475cb93a386Sopenharmony_ci // Increment the cluster width 476cb93a386Sopenharmony_ci cluster->space(shift); 477cb93a386Sopenharmony_ci cluster->setHalfLetterSpacing(space / 2); 478cb93a386Sopenharmony_ci 479cb93a386Sopenharmony_ci return shift; 480cb93a386Sopenharmony_ci} 481cb93a386Sopenharmony_ci#endif 482cb93a386Sopenharmony_ci 483cb93a386Sopenharmony_civoid Run::shift(const Cluster* cluster, SkScalar offset) { 484cb93a386Sopenharmony_ci if (offset == 0) { 485cb93a386Sopenharmony_ci return; 486cb93a386Sopenharmony_ci } 487cb93a386Sopenharmony_ci 488cb93a386Sopenharmony_ci for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) { 489cb93a386Sopenharmony_ci fPositions[i].fX += offset; 490cb93a386Sopenharmony_ci } 491cb93a386Sopenharmony_ci if (this->size() == cluster->endPos()) { 492cb93a386Sopenharmony_ci // To make calculations easier 493cb93a386Sopenharmony_ci fPositions[cluster->endPos()].fX += offset; 494cb93a386Sopenharmony_ci } 495cb93a386Sopenharmony_ci} 496cb93a386Sopenharmony_ci 497cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 498cb93a386Sopenharmony_civoid Run::extendClusterWidth(Cluster* cluster, SkScalar space) { 499cb93a386Sopenharmony_ci addSpacesAtTheEnd(space, cluster); 500cb93a386Sopenharmony_ci for (size_t pos = cluster->endPos(); pos < fPositions.size(); pos++) { 501cb93a386Sopenharmony_ci fPositions[pos].fX += space; 502cb93a386Sopenharmony_ci } 503cb93a386Sopenharmony_ci} 504cb93a386Sopenharmony_ci#endif 505cb93a386Sopenharmony_ci 506cb93a386Sopenharmony_civoid Run::updateMetrics(InternalLineMetrics* endlineMetrics) { 507cb93a386Sopenharmony_ci 508cb93a386Sopenharmony_ci SkASSERT(isPlaceholder()); 509cb93a386Sopenharmony_ci auto placeholderStyle = this->placeholderStyle(); 510cb93a386Sopenharmony_ci // Difference between the placeholder baseline and the line bottom 511cb93a386Sopenharmony_ci SkScalar baselineAdjustment = 0; 512cb93a386Sopenharmony_ci switch (placeholderStyle->fBaseline) { 513cb93a386Sopenharmony_ci case TextBaseline::kAlphabetic: 514cb93a386Sopenharmony_ci break; 515cb93a386Sopenharmony_ci 516cb93a386Sopenharmony_ci case TextBaseline::kIdeographic: 517cb93a386Sopenharmony_ci baselineAdjustment = endlineMetrics->deltaBaselines() / 2; 518cb93a386Sopenharmony_ci break; 519cb93a386Sopenharmony_ci } 520cb93a386Sopenharmony_ci 521cb93a386Sopenharmony_ci auto height = placeholderStyle->fHeight; 522cb93a386Sopenharmony_ci auto offset = placeholderStyle->fBaselineOffset; 523cb93a386Sopenharmony_ci 524cb93a386Sopenharmony_ci fFontMetrics.fLeading = 0; 525cb93a386Sopenharmony_ci switch (placeholderStyle->fAlignment) { 526cb93a386Sopenharmony_ci case PlaceholderAlignment::kBaseline: 527cb93a386Sopenharmony_ci fFontMetrics.fAscent = baselineAdjustment - height - offset; 528cb93a386Sopenharmony_ci fFontMetrics.fDescent = baselineAdjustment - offset; 529cb93a386Sopenharmony_ci break; 530cb93a386Sopenharmony_ci 531cb93a386Sopenharmony_ci case PlaceholderAlignment::kAboveBaseline: 532cb93a386Sopenharmony_ci fFontMetrics.fAscent = baselineAdjustment - height; 533cb93a386Sopenharmony_ci fFontMetrics.fDescent = baselineAdjustment; 534cb93a386Sopenharmony_ci break; 535cb93a386Sopenharmony_ci 536cb93a386Sopenharmony_ci case PlaceholderAlignment::kBelowBaseline: 537cb93a386Sopenharmony_ci fFontMetrics.fAscent = baselineAdjustment; 538cb93a386Sopenharmony_ci fFontMetrics.fDescent = baselineAdjustment + height; 539cb93a386Sopenharmony_ci break; 540cb93a386Sopenharmony_ci 541cb93a386Sopenharmony_ci case PlaceholderAlignment::kTop: 542cb93a386Sopenharmony_ci fFontMetrics.fAscent = endlineMetrics->ascent(); 543cb93a386Sopenharmony_ci fFontMetrics.fDescent = height + fFontMetrics.fAscent; 544cb93a386Sopenharmony_ci break; 545cb93a386Sopenharmony_ci 546cb93a386Sopenharmony_ci case PlaceholderAlignment::kBottom: 547cb93a386Sopenharmony_ci fFontMetrics.fDescent = endlineMetrics->descent(); 548cb93a386Sopenharmony_ci fFontMetrics.fAscent = fFontMetrics.fDescent - height; 549cb93a386Sopenharmony_ci break; 550cb93a386Sopenharmony_ci 551cb93a386Sopenharmony_ci case PlaceholderAlignment::kMiddle: 552cb93a386Sopenharmony_ci auto mid = (endlineMetrics->ascent() + endlineMetrics->descent()) / PARAM_TWO; 553cb93a386Sopenharmony_ci fFontMetrics.fDescent = mid + height / PARAM_TWO; 554cb93a386Sopenharmony_ci fFontMetrics.fAscent = mid - height / PARAM_TWO; 555cb93a386Sopenharmony_ci break; 556cb93a386Sopenharmony_ci } 557cb93a386Sopenharmony_ci 558cb93a386Sopenharmony_ci this->calculateMetrics(); 559cb93a386Sopenharmony_ci 560cb93a386Sopenharmony_ci // Make sure the placeholder can fit the line 561cb93a386Sopenharmony_ci endlineMetrics->add(this); 562cb93a386Sopenharmony_ci} 563cb93a386Sopenharmony_ci 564cb93a386Sopenharmony_ciSkScalar Cluster::sizeToChar(TextIndex ch) const { 565cb93a386Sopenharmony_ci if (ch < fTextRange.start || ch >= fTextRange.end) { 566cb93a386Sopenharmony_ci return 0; 567cb93a386Sopenharmony_ci } 568cb93a386Sopenharmony_ci auto shift = ch - fTextRange.start; 569cb93a386Sopenharmony_ci auto ratio = shift * 1.0 / fTextRange.width(); 570cb93a386Sopenharmony_ci 571cb93a386Sopenharmony_ci return SkDoubleToScalar(fWidth * ratio); 572cb93a386Sopenharmony_ci} 573cb93a386Sopenharmony_ci 574cb93a386Sopenharmony_ciSkScalar Cluster::sizeFromChar(TextIndex ch) const { 575cb93a386Sopenharmony_ci if (ch < fTextRange.start || ch >= fTextRange.end) { 576cb93a386Sopenharmony_ci return 0; 577cb93a386Sopenharmony_ci } 578cb93a386Sopenharmony_ci auto shift = fTextRange.end - ch - 1; 579cb93a386Sopenharmony_ci auto ratio = shift * 1.0 / fTextRange.width(); 580cb93a386Sopenharmony_ci 581cb93a386Sopenharmony_ci return SkDoubleToScalar(fWidth * ratio); 582cb93a386Sopenharmony_ci} 583cb93a386Sopenharmony_ci 584cb93a386Sopenharmony_cisize_t Cluster::roundPos(SkScalar s) const { 585cb93a386Sopenharmony_ci auto ratio = (s * 1.0) / fWidth; 586cb93a386Sopenharmony_ci return sk_double_floor2int(ratio * size()); 587cb93a386Sopenharmony_ci} 588cb93a386Sopenharmony_ci 589cb93a386Sopenharmony_ciSkScalar Cluster::trimmedWidth(size_t pos) const { 590cb93a386Sopenharmony_ci // Find the width until the pos and return the min between trimmedWidth and the width(pos) 591cb93a386Sopenharmony_ci // We don't have to take in account cluster shift since it's the same for 0 and for pos 592cb93a386Sopenharmony_ci auto& run = fOwner->run(fRunIndex); 593cb93a386Sopenharmony_ci SkScalar delta = getHalfLetterSpacing() - run.halfLetterspacing(pos); 594cb93a386Sopenharmony_ci return std::min(run.positionX(pos) - run.positionX(fStart) + delta, fWidth); 595cb93a386Sopenharmony_ci} 596cb93a386Sopenharmony_ci 597cb93a386Sopenharmony_ciSkScalar Run::positionX(size_t pos) const { 598cb93a386Sopenharmony_ci return posX(pos) + (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY) + 599cb93a386Sopenharmony_ci (fAutoSpacings.empty() ? 0 : fAutoSpacings[pos].fY); 600cb93a386Sopenharmony_ci} 601cb93a386Sopenharmony_ci 602cb93a386Sopenharmony_ciSkScalar Run::posX(size_t index) const { 603cb93a386Sopenharmony_ci if (index < fPositions.size()) { 604cb93a386Sopenharmony_ci return fPositions[index].fX; 605cb93a386Sopenharmony_ci } 606cb93a386Sopenharmony_ci LOGE("index:%{public}zu,size:%{public}zu", index, fPositions.size()); 607cb93a386Sopenharmony_ci if (fPositions.empty()) { 608cb93a386Sopenharmony_ci return 0.0f; 609cb93a386Sopenharmony_ci } 610cb93a386Sopenharmony_ci return fPositions[fPositions.size() - 1].fX; 611cb93a386Sopenharmony_ci} 612cb93a386Sopenharmony_ci 613cb93a386Sopenharmony_ciPlaceholderStyle* Run::placeholderStyle() const { 614cb93a386Sopenharmony_ci if (isPlaceholder()) { 615cb93a386Sopenharmony_ci return &fOwner->placeholders()[fPlaceholderIndex].fStyle; 616cb93a386Sopenharmony_ci } else { 617cb93a386Sopenharmony_ci return nullptr; 618cb93a386Sopenharmony_ci } 619cb93a386Sopenharmony_ci} 620cb93a386Sopenharmony_ci 621cb93a386Sopenharmony_cibool Run::isResolved() const { 622cb93a386Sopenharmony_ci for (auto& glyph :fGlyphs) { 623cb93a386Sopenharmony_ci if (glyph == 0) { 624cb93a386Sopenharmony_ci return false; 625cb93a386Sopenharmony_ci } 626cb93a386Sopenharmony_ci } 627cb93a386Sopenharmony_ci return true; 628cb93a386Sopenharmony_ci} 629cb93a386Sopenharmony_ci 630cb93a386Sopenharmony_ciRun* Cluster::runOrNull() const { 631cb93a386Sopenharmony_ci if (fRunIndex >= fOwner->runs().size()) { 632cb93a386Sopenharmony_ci return nullptr; 633cb93a386Sopenharmony_ci } 634cb93a386Sopenharmony_ci return &fOwner->run(fRunIndex); 635cb93a386Sopenharmony_ci} 636cb93a386Sopenharmony_ci 637cb93a386Sopenharmony_ciRun& Cluster::run() const { 638cb93a386Sopenharmony_ci SkASSERT(fRunIndex < fOwner->runs().size()); 639cb93a386Sopenharmony_ci return fOwner->run(fRunIndex); 640cb93a386Sopenharmony_ci} 641cb93a386Sopenharmony_ci 642cb93a386Sopenharmony_ci#ifndef USE_SKIA_TXT 643cb93a386Sopenharmony_ciSkFont Cluster::font() const { 644cb93a386Sopenharmony_ci#else 645cb93a386Sopenharmony_ciRSFont Cluster::font() const { 646cb93a386Sopenharmony_ci#endif 647cb93a386Sopenharmony_ci SkASSERT(fRunIndex < fOwner->runs().size()); 648cb93a386Sopenharmony_ci return fOwner->run(fRunIndex).font(); 649cb93a386Sopenharmony_ci} 650cb93a386Sopenharmony_ci 651cb93a386Sopenharmony_cibool Cluster::isSoftBreak() const { 652cb93a386Sopenharmony_ci return fOwner->codeUnitHasProperty(fTextRange.end, 653cb93a386Sopenharmony_ci SkUnicode::CodeUnitFlags::kSoftLineBreakBefore); 654cb93a386Sopenharmony_ci} 655cb93a386Sopenharmony_ci 656cb93a386Sopenharmony_cibool Cluster::isGraphemeBreak() const { 657cb93a386Sopenharmony_ci return fOwner->codeUnitHasProperty(fTextRange.end, SkUnicode::CodeUnitFlags::kGraphemeStart); 658cb93a386Sopenharmony_ci} 659cb93a386Sopenharmony_ci} // namespace textlayout 660cb93a386Sopenharmony_ci} // namespace skia 661