1cb93a386Sopenharmony_ci// Copyright 2019 Google LLC. 2cb93a386Sopenharmony_ci#include <memory> 3cb93a386Sopenharmony_ci 4cb93a386Sopenharmony_ci#include "modules/skparagraph/include/FontArguments.h" 5cb93a386Sopenharmony_ci#include "modules/skparagraph/include/ParagraphCache.h" 6cb93a386Sopenharmony_ci#include "modules/skparagraph/src/ParagraphImpl.h" 7cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 8cb93a386Sopenharmony_ci#include "utils/text_trace.h" 9cb93a386Sopenharmony_ci#endif 10cb93a386Sopenharmony_ci#include "log.h" 11cb93a386Sopenharmony_ci 12cb93a386Sopenharmony_cinamespace skia { 13cb93a386Sopenharmony_cinamespace textlayout { 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_cinamespace { 16cb93a386Sopenharmony_ci int32_t relax(SkScalar a) { 17cb93a386Sopenharmony_ci // This rounding is done to match Flutter tests. Must be removed.. 18cb93a386Sopenharmony_ci if (SkScalarIsFinite(a)) { 19cb93a386Sopenharmony_ci auto threshold = SkIntToScalar(1 << 12); 20cb93a386Sopenharmony_ci return SkFloat2Bits(SkScalarRoundToScalar(a * threshold)/threshold); 21cb93a386Sopenharmony_ci } else { 22cb93a386Sopenharmony_ci return SkFloat2Bits(a); 23cb93a386Sopenharmony_ci } 24cb93a386Sopenharmony_ci } 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_ci bool exactlyEqual(SkScalar x, SkScalar y) { 27cb93a386Sopenharmony_ci return x == y || (x != x && y != y); 28cb93a386Sopenharmony_ci } 29cb93a386Sopenharmony_ci 30cb93a386Sopenharmony_ci} // namespace 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_ciclass ParagraphCacheKey { 33cb93a386Sopenharmony_cipublic: 34cb93a386Sopenharmony_ci ParagraphCacheKey(const ParagraphImpl* paragraph) 35cb93a386Sopenharmony_ci : fText(paragraph->fText.c_str(), paragraph->fText.size()) 36cb93a386Sopenharmony_ci , fPlaceholders(paragraph->fPlaceholders) 37cb93a386Sopenharmony_ci , fTextStyles(paragraph->fTextStyles) 38cb93a386Sopenharmony_ci , fParagraphStyle(paragraph->paragraphStyle()) { 39cb93a386Sopenharmony_ci fHash = computeHash(paragraph); 40cb93a386Sopenharmony_ci } 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_ci ParagraphCacheKey(const ParagraphCacheKey& other) = default; 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_ci ParagraphCacheKey(ParagraphCacheKey&& other) 45cb93a386Sopenharmony_ci : fText(std::move(other.fText)) 46cb93a386Sopenharmony_ci , fPlaceholders(std::move(other.fPlaceholders)) 47cb93a386Sopenharmony_ci , fTextStyles(std::move(other.fTextStyles)) 48cb93a386Sopenharmony_ci , fParagraphStyle(std::move(other.fParagraphStyle)) 49cb93a386Sopenharmony_ci , fHash(other.fHash) { 50cb93a386Sopenharmony_ci other.fHash = 0; 51cb93a386Sopenharmony_ci } 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_ci // thin constructor suitable only for searching 54cb93a386Sopenharmony_ci explicit ParagraphCacheKey(uint32_t hash) : fHash(hash) {} 55cb93a386Sopenharmony_ci 56cb93a386Sopenharmony_ci bool operator==(const ParagraphCacheKey& other) const; 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci uint32_t hash() const { return fHash; } 59cb93a386Sopenharmony_ci 60cb93a386Sopenharmony_ci const SkString& text() const { return fText; } 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_ciprivate: 63cb93a386Sopenharmony_ci static uint32_t mix(uint32_t hash, uint32_t data); 64cb93a386Sopenharmony_ci uint32_t computeHash(const ParagraphImpl* paragraph) const; 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci SkString fText; 67cb93a386Sopenharmony_ci SkTArray<Placeholder, true> fPlaceholders; 68cb93a386Sopenharmony_ci SkTArray<Block, true> fTextStyles; 69cb93a386Sopenharmony_ci ParagraphStyle fParagraphStyle; 70cb93a386Sopenharmony_ci uint32_t fHash; 71cb93a386Sopenharmony_ci}; 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_ciclass ParagraphCacheValue { 74cb93a386Sopenharmony_cipublic: 75cb93a386Sopenharmony_ci ParagraphCacheValue(ParagraphCacheKey&& key, const ParagraphImpl* paragraph) 76cb93a386Sopenharmony_ci : fKey(std::move(key)) 77cb93a386Sopenharmony_ci , fRuns(paragraph->fRuns) 78cb93a386Sopenharmony_ci , fClusters(paragraph->fClusters) 79cb93a386Sopenharmony_ci , fClustersIndexFromCodeUnit(paragraph->fClustersIndexFromCodeUnit) 80cb93a386Sopenharmony_ci , fCodeUnitProperties(paragraph->fCodeUnitProperties) 81cb93a386Sopenharmony_ci , fWords(paragraph->fWords) 82cb93a386Sopenharmony_ci , fBidiRegions(paragraph->fBidiRegions) 83cb93a386Sopenharmony_ci , fHasLineBreaks(paragraph->fHasLineBreaks) 84cb93a386Sopenharmony_ci , fHasWhitespacesInside(paragraph->fHasWhitespacesInside) 85cb93a386Sopenharmony_ci , fTrailingSpaces(paragraph->fTrailingSpaces) 86cb93a386Sopenharmony_ci , fLayoutRawWidth(paragraph->fLayoutRawWidth) {} 87cb93a386Sopenharmony_ci 88cb93a386Sopenharmony_ci // Input == key 89cb93a386Sopenharmony_ci ParagraphCacheKey fKey; 90cb93a386Sopenharmony_ci 91cb93a386Sopenharmony_ci // Shaped results 92cb93a386Sopenharmony_ci SkTArray<Run, false> fRuns; 93cb93a386Sopenharmony_ci SkTArray<Cluster, true> fClusters; 94cb93a386Sopenharmony_ci SkTArray<size_t, true> fClustersIndexFromCodeUnit; 95cb93a386Sopenharmony_ci // ICU results 96cb93a386Sopenharmony_ci SkTArray<SkUnicode::CodeUnitFlags, true> fCodeUnitProperties; 97cb93a386Sopenharmony_ci std::vector<size_t> fWords; 98cb93a386Sopenharmony_ci std::vector<SkUnicode::BidiRegion> fBidiRegions; 99cb93a386Sopenharmony_ci bool fHasLineBreaks; 100cb93a386Sopenharmony_ci bool fHasWhitespacesInside; 101cb93a386Sopenharmony_ci TextIndex fTrailingSpaces; 102cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 103cb93a386Sopenharmony_ci SkTArray<TextLine, false> fLines; 104cb93a386Sopenharmony_ci SkScalar fHeight; 105cb93a386Sopenharmony_ci SkScalar fWidth; 106cb93a386Sopenharmony_ci SkScalar fMaxIntrinsicWidth; 107cb93a386Sopenharmony_ci SkScalar fMinIntrinsicWidth; 108cb93a386Sopenharmony_ci SkScalar fAlphabeticBaseline; 109cb93a386Sopenharmony_ci SkScalar fIdeographicBaseline; 110cb93a386Sopenharmony_ci SkScalar fLongestLine; 111cb93a386Sopenharmony_ci SkScalar fLongestLineWithIndent; 112cb93a386Sopenharmony_ci bool fExceededMaxLines; 113cb93a386Sopenharmony_ci // criteria to apply the layout cache, should presumably hash it, same 114cb93a386Sopenharmony_ci // hash could be used to check if the entry has cached layout available 115cb93a386Sopenharmony_ci LineBreakStrategy linebreakStrategy; 116cb93a386Sopenharmony_ci WordBreakType wordBreakType; 117cb93a386Sopenharmony_ci std::vector<SkScalar> indents; 118cb93a386Sopenharmony_ci SkScalar fLayoutRawWidth; 119cb93a386Sopenharmony_ci size_t maxlines; 120cb93a386Sopenharmony_ci bool hasEllipsis; 121cb93a386Sopenharmony_ci EllipsisModal ellipsisModal; 122cb93a386Sopenharmony_ci#endif 123cb93a386Sopenharmony_ci}; 124cb93a386Sopenharmony_ci 125cb93a386Sopenharmony_ciuint32_t ParagraphCacheKey::mix(uint32_t hash, uint32_t data) { 126cb93a386Sopenharmony_ci hash += data; 127cb93a386Sopenharmony_ci hash += (hash << 10); 128cb93a386Sopenharmony_ci hash ^= (hash >> 6); 129cb93a386Sopenharmony_ci return hash; 130cb93a386Sopenharmony_ci} 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ciuint32_t ParagraphCacheKey::computeHash(const ParagraphImpl* paragraph) const { 133cb93a386Sopenharmony_ciuint32_t hash = 0; 134cb93a386Sopenharmony_ci for (auto& ph : fPlaceholders) { 135cb93a386Sopenharmony_ci if (ph.fRange.width() == 0) { 136cb93a386Sopenharmony_ci continue; 137cb93a386Sopenharmony_ci } 138cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(ph.fRange)); 139cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fHeight))); 140cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fWidth))); 141cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(ph.fStyle.fAlignment)); 142cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(ph.fStyle.fBaseline)); 143cb93a386Sopenharmony_ci if (ph.fStyle.fAlignment == PlaceholderAlignment::kBaseline) { 144cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fBaselineOffset))); 145cb93a386Sopenharmony_ci } 146cb93a386Sopenharmony_ci } 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci for (auto& ts : fTextStyles) { 149cb93a386Sopenharmony_ci if (ts.fStyle.isPlaceholder()) { 150cb93a386Sopenharmony_ci continue; 151cb93a386Sopenharmony_ci } 152cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getLetterSpacing()))); 153cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getWordSpacing()))); 154cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(ts.fStyle.getLocale())); 155cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getHeight()))); 156cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getBaselineShift()))); 157cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getHalfLeading()))); 158cb93a386Sopenharmony_ci for (auto& ff : ts.fStyle.getFontFamilies()) { 159cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(ff)); 160cb93a386Sopenharmony_ci } 161cb93a386Sopenharmony_ci for (auto& ff : ts.fStyle.getFontFeatures()) { 162cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(ff.fValue)); 163cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(ff.fName)); 164cb93a386Sopenharmony_ci } 165cb93a386Sopenharmony_ci hash = mix(hash, std::hash<std::optional<FontArguments>>()(ts.fStyle.getFontArguments())); 166cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(ts.fStyle.getFontStyle())); 167cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getFontSize()))); 168cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(ts.fRange)); 169cb93a386Sopenharmony_ci } 170cb93a386Sopenharmony_ci 171cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(fParagraphStyle.getHeight()))); 172cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(fParagraphStyle.getTextDirection())); 173cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(fParagraphStyle.getReplaceTabCharacters() ? 1 : 0)); 174cb93a386Sopenharmony_ci 175cb93a386Sopenharmony_ci auto& strutStyle = fParagraphStyle.getStrutStyle(); 176cb93a386Sopenharmony_ci if (strutStyle.getStrutEnabled()) { 177cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(strutStyle.getHeight()))); 178cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(strutStyle.getLeading()))); 179cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(relax(strutStyle.getFontSize()))); 180cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(strutStyle.getHeightOverride())); 181cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(strutStyle.getFontStyle())); 182cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(strutStyle.getForceStrutHeight())); 183cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(strutStyle.getHalfLeading())); 184cb93a386Sopenharmony_ci for (auto& ff : strutStyle.getFontFamilies()) { 185cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(ff)); 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci } 188cb93a386Sopenharmony_ci 189cb93a386Sopenharmony_ci hash = mix(hash, SkGoodHash()(fText)); 190cb93a386Sopenharmony_ci return hash; 191cb93a386Sopenharmony_ci} 192cb93a386Sopenharmony_ci 193cb93a386Sopenharmony_ciuint32_t ParagraphCache::KeyHash::operator()(const ParagraphCacheKey& key) const { 194cb93a386Sopenharmony_ci return key.hash(); 195cb93a386Sopenharmony_ci} 196cb93a386Sopenharmony_ci 197cb93a386Sopenharmony_cibool ParagraphCacheKey::operator==(const ParagraphCacheKey& other) const { 198cb93a386Sopenharmony_ci if (fText.size() != other.fText.size()) { 199cb93a386Sopenharmony_ci return false; 200cb93a386Sopenharmony_ci } 201cb93a386Sopenharmony_ci if (fPlaceholders.size() != other.fPlaceholders.size()) { 202cb93a386Sopenharmony_ci return false; 203cb93a386Sopenharmony_ci } 204cb93a386Sopenharmony_ci if (fText != other.fText) { 205cb93a386Sopenharmony_ci return false; 206cb93a386Sopenharmony_ci } 207cb93a386Sopenharmony_ci if (fTextStyles.size() != other.fTextStyles.size()) { 208cb93a386Sopenharmony_ci return false; 209cb93a386Sopenharmony_ci } 210cb93a386Sopenharmony_ci 211cb93a386Sopenharmony_ci // There is no need to compare default paragraph styles - they are included into fTextStyles 212cb93a386Sopenharmony_ci if (!exactlyEqual(fParagraphStyle.getHeight(), other.fParagraphStyle.getHeight())) { 213cb93a386Sopenharmony_ci return false; 214cb93a386Sopenharmony_ci } 215cb93a386Sopenharmony_ci if (fParagraphStyle.getTextDirection() != other.fParagraphStyle.getTextDirection()) { 216cb93a386Sopenharmony_ci return false; 217cb93a386Sopenharmony_ci } 218cb93a386Sopenharmony_ci 219cb93a386Sopenharmony_ci if (!(fParagraphStyle.getStrutStyle() == other.fParagraphStyle.getStrutStyle())) { 220cb93a386Sopenharmony_ci return false; 221cb93a386Sopenharmony_ci } 222cb93a386Sopenharmony_ci 223cb93a386Sopenharmony_ci if (!(fParagraphStyle.getReplaceTabCharacters() == other.fParagraphStyle.getReplaceTabCharacters())) { 224cb93a386Sopenharmony_ci return false; 225cb93a386Sopenharmony_ci } 226cb93a386Sopenharmony_ci 227cb93a386Sopenharmony_ci for (size_t i = 0; i < fTextStyles.size(); ++i) { 228cb93a386Sopenharmony_ci auto& tsa = fTextStyles[i]; 229cb93a386Sopenharmony_ci auto& tsb = other.fTextStyles[i]; 230cb93a386Sopenharmony_ci if (tsa.fStyle.isPlaceholder()) { 231cb93a386Sopenharmony_ci continue; 232cb93a386Sopenharmony_ci } 233cb93a386Sopenharmony_ci if (!(tsa.fStyle.equalsByFonts(tsb.fStyle))) { 234cb93a386Sopenharmony_ci return false; 235cb93a386Sopenharmony_ci } 236cb93a386Sopenharmony_ci if (tsa.fRange.width() != tsb.fRange.width()) { 237cb93a386Sopenharmony_ci return false; 238cb93a386Sopenharmony_ci } 239cb93a386Sopenharmony_ci if (tsa.fRange.start != tsb.fRange.start) { 240cb93a386Sopenharmony_ci return false; 241cb93a386Sopenharmony_ci } 242cb93a386Sopenharmony_ci } 243cb93a386Sopenharmony_ci for (size_t i = 0; i < fPlaceholders.size(); ++i) { 244cb93a386Sopenharmony_ci auto& tsa = fPlaceholders[i]; 245cb93a386Sopenharmony_ci auto& tsb = other.fPlaceholders[i]; 246cb93a386Sopenharmony_ci if (tsa.fRange.width() == 0 && tsb.fRange.width() == 0) { 247cb93a386Sopenharmony_ci continue; 248cb93a386Sopenharmony_ci } 249cb93a386Sopenharmony_ci if (!(tsa.fStyle.equals(tsb.fStyle))) { 250cb93a386Sopenharmony_ci return false; 251cb93a386Sopenharmony_ci } 252cb93a386Sopenharmony_ci if (tsa.fRange.width() != tsb.fRange.width()) { 253cb93a386Sopenharmony_ci return false; 254cb93a386Sopenharmony_ci } 255cb93a386Sopenharmony_ci if (tsa.fRange.start != tsb.fRange.start) { 256cb93a386Sopenharmony_ci return false; 257cb93a386Sopenharmony_ci } 258cb93a386Sopenharmony_ci } 259cb93a386Sopenharmony_ci 260cb93a386Sopenharmony_ci return true; 261cb93a386Sopenharmony_ci} 262cb93a386Sopenharmony_ci 263cb93a386Sopenharmony_cistruct ParagraphCache::Entry { 264cb93a386Sopenharmony_ci 265cb93a386Sopenharmony_ci Entry(ParagraphCacheValue* value) : fValue(value) {} 266cb93a386Sopenharmony_ci std::unique_ptr<ParagraphCacheValue> fValue; 267cb93a386Sopenharmony_ci}; 268cb93a386Sopenharmony_ci 269cb93a386Sopenharmony_ciParagraphCache::ParagraphCache() 270cb93a386Sopenharmony_ci : fChecker([](ParagraphImpl* impl, const char*, bool){ }) 271cb93a386Sopenharmony_ci , fLRUCacheMap(kMaxEntries) 272cb93a386Sopenharmony_ci , fCacheIsOn(true) 273cb93a386Sopenharmony_ci , fLastCachedValue(nullptr) 274cb93a386Sopenharmony_ci#ifdef PARAGRAPH_CACHE_STATS 275cb93a386Sopenharmony_ci , fTotalRequests(0) 276cb93a386Sopenharmony_ci , fCacheMisses(0) 277cb93a386Sopenharmony_ci , fHashMisses(0) 278cb93a386Sopenharmony_ci#endif 279cb93a386Sopenharmony_ci{ } 280cb93a386Sopenharmony_ci 281cb93a386Sopenharmony_ciParagraphCache::~ParagraphCache() { } 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_civoid ParagraphCache::updateTo(ParagraphImpl* paragraph, const Entry* entry) { 284cb93a386Sopenharmony_ci 285cb93a386Sopenharmony_ci paragraph->fRuns.reset(); 286cb93a386Sopenharmony_ci paragraph->fRuns = entry->fValue->fRuns; 287cb93a386Sopenharmony_ci paragraph->fClusters = entry->fValue->fClusters; 288cb93a386Sopenharmony_ci paragraph->fClustersIndexFromCodeUnit = entry->fValue->fClustersIndexFromCodeUnit; 289cb93a386Sopenharmony_ci paragraph->fCodeUnitProperties = entry->fValue->fCodeUnitProperties; 290cb93a386Sopenharmony_ci paragraph->fWords = entry->fValue->fWords; 291cb93a386Sopenharmony_ci paragraph->fBidiRegions = entry->fValue->fBidiRegions; 292cb93a386Sopenharmony_ci paragraph->fHasLineBreaks = entry->fValue->fHasLineBreaks; 293cb93a386Sopenharmony_ci paragraph->fHasWhitespacesInside = entry->fValue->fHasWhitespacesInside; 294cb93a386Sopenharmony_ci paragraph->fTrailingSpaces = entry->fValue->fTrailingSpaces; 295cb93a386Sopenharmony_ci for (auto& run : paragraph->fRuns) { 296cb93a386Sopenharmony_ci run.setOwner(paragraph); 297cb93a386Sopenharmony_ci } 298cb93a386Sopenharmony_ci for (auto& cluster : paragraph->fClusters) { 299cb93a386Sopenharmony_ci cluster.setOwner(paragraph); 300cb93a386Sopenharmony_ci } 301cb93a386Sopenharmony_ci paragraph->hash() = entry->fValue->fKey.hash(); 302cb93a386Sopenharmony_ci} 303cb93a386Sopenharmony_ci 304cb93a386Sopenharmony_civoid ParagraphCache::printStatistics() { 305cb93a386Sopenharmony_ci SkDebugf("--- Paragraph Cache ---\n"); 306cb93a386Sopenharmony_ci SkDebugf("Total requests: %d\n", fTotalRequests); 307cb93a386Sopenharmony_ci SkDebugf("Cache misses: %d\n", fCacheMisses); 308cb93a386Sopenharmony_ci SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? 100.f * fCacheMisses / fTotalRequests : 0.f); 309cb93a386Sopenharmony_ci int cacheHits = fTotalRequests - fCacheMisses; 310cb93a386Sopenharmony_ci SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f); 311cb93a386Sopenharmony_ci SkDebugf("---------------------\n"); 312cb93a386Sopenharmony_ci} 313cb93a386Sopenharmony_ci 314cb93a386Sopenharmony_civoid ParagraphCache::abandon() { 315cb93a386Sopenharmony_ci this->reset(); 316cb93a386Sopenharmony_ci} 317cb93a386Sopenharmony_ci 318cb93a386Sopenharmony_civoid ParagraphCache::reset() { 319cb93a386Sopenharmony_ci SkAutoMutexExclusive lock(fParagraphMutex); 320cb93a386Sopenharmony_ci#ifdef PARAGRAPH_CACHE_STATS 321cb93a386Sopenharmony_ci fTotalRequests = 0; 322cb93a386Sopenharmony_ci fCacheMisses = 0; 323cb93a386Sopenharmony_ci fHashMisses = 0; 324cb93a386Sopenharmony_ci#endif 325cb93a386Sopenharmony_ci fLRUCacheMap.reset(); 326cb93a386Sopenharmony_ci fLastCachedValue = nullptr; 327cb93a386Sopenharmony_ci} 328cb93a386Sopenharmony_ci 329cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 330cb93a386Sopenharmony_cibool ParagraphCache::useCachedLayout(const ParagraphImpl& paragraph, const ParagraphCacheValue* value) { 331cb93a386Sopenharmony_ci if (value && value->indents == paragraph.fIndents && 332cb93a386Sopenharmony_ci paragraph.getLineBreakStrategy() == value->linebreakStrategy && 333cb93a386Sopenharmony_ci paragraph.getWordBreakType() == value->wordBreakType && 334cb93a386Sopenharmony_ci abs(paragraph.fLayoutRawWidth - value->fLayoutRawWidth) < 1.f && 335cb93a386Sopenharmony_ci paragraph.fParagraphStyle.getMaxLines() == value->maxlines && 336cb93a386Sopenharmony_ci paragraph.fParagraphStyle.ellipsized() == value->hasEllipsis && 337cb93a386Sopenharmony_ci paragraph.fParagraphStyle.getEllipsisMod() == value->ellipsisModal && 338cb93a386Sopenharmony_ci paragraph.fText.size() == value->fKey.text().size()) { 339cb93a386Sopenharmony_ci return true; 340cb93a386Sopenharmony_ci } 341cb93a386Sopenharmony_ci return false; 342cb93a386Sopenharmony_ci} 343cb93a386Sopenharmony_ci 344cb93a386Sopenharmony_civoid ParagraphCache::SetStoredLayout(ParagraphImpl& paragraph) { 345cb93a386Sopenharmony_ci SkAutoMutexExclusive lock(fParagraphMutex); 346cb93a386Sopenharmony_ci auto key = ParagraphCacheKey(¶graph); 347cb93a386Sopenharmony_ci std::unique_ptr<Entry>* entry = fLRUCacheMap.find(key); 348cb93a386Sopenharmony_ci 349cb93a386Sopenharmony_ci if (entry && *entry) { 350cb93a386Sopenharmony_ci if (auto value = (*entry)->fValue.get()) { 351cb93a386Sopenharmony_ci SetStoredLayoutImpl(paragraph, value); 352cb93a386Sopenharmony_ci } 353cb93a386Sopenharmony_ci } else { 354cb93a386Sopenharmony_ci if (auto value = cacheLayout(¶graph)) { 355cb93a386Sopenharmony_ci SetStoredLayoutImpl(paragraph, value); 356cb93a386Sopenharmony_ci } 357cb93a386Sopenharmony_ci } 358cb93a386Sopenharmony_ci} 359cb93a386Sopenharmony_ci 360cb93a386Sopenharmony_civoid ParagraphCache::SetStoredLayoutImpl(ParagraphImpl& paragraph, ParagraphCacheValue* value) { 361cb93a386Sopenharmony_ci if (paragraph.fRuns.size() == value->fRuns.size()) { 362cb93a386Sopenharmony_ci // update PlaceholderRun metrics cache value for placeholder alignment 363cb93a386Sopenharmony_ci for (size_t idx = 0; idx < value->fRuns.size(); ++idx) { 364cb93a386Sopenharmony_ci if (!value->fRuns[idx].isPlaceholder()) { 365cb93a386Sopenharmony_ci continue; 366cb93a386Sopenharmony_ci } 367cb93a386Sopenharmony_ci value->fRuns[idx].fFontMetrics = paragraph.fRuns[idx].fFontMetrics; 368cb93a386Sopenharmony_ci value->fRuns[idx].fCorrectAscent = paragraph.fRuns[idx].fCorrectAscent; 369cb93a386Sopenharmony_ci value->fRuns[idx].fCorrectDescent = paragraph.fRuns[idx].fCorrectDescent; 370cb93a386Sopenharmony_ci } 371cb93a386Sopenharmony_ci } 372cb93a386Sopenharmony_ci value->fLines.reset(); 373cb93a386Sopenharmony_ci value->indents.clear(); 374cb93a386Sopenharmony_ci 375cb93a386Sopenharmony_ci for (auto& line : paragraph.fLines) { 376cb93a386Sopenharmony_ci value->fLines.emplace_back(line.CloneSelf()); 377cb93a386Sopenharmony_ci } 378cb93a386Sopenharmony_ci paragraph.getSize(value->fHeight, value->fWidth, value->fLongestLine); 379cb93a386Sopenharmony_ci value->fLongestLineWithIndent = paragraph.getLongestLineWithIndent(); 380cb93a386Sopenharmony_ci paragraph.getIntrinsicSize(value->fMaxIntrinsicWidth, value->fMinIntrinsicWidth, 381cb93a386Sopenharmony_ci value->fAlphabeticBaseline, value->fIdeographicBaseline, 382cb93a386Sopenharmony_ci value->fExceededMaxLines); 383cb93a386Sopenharmony_ci for (auto& indent : value->indents) { 384cb93a386Sopenharmony_ci value->indents.push_back(indent); 385cb93a386Sopenharmony_ci } 386cb93a386Sopenharmony_ci value->linebreakStrategy = paragraph.getLineBreakStrategy(); 387cb93a386Sopenharmony_ci value->wordBreakType = paragraph.getWordBreakType(); 388cb93a386Sopenharmony_ci value->fLayoutRawWidth = paragraph.fLayoutRawWidth; 389cb93a386Sopenharmony_ci value->maxlines = paragraph.fParagraphStyle.getMaxLines(); 390cb93a386Sopenharmony_ci value->hasEllipsis = paragraph.fParagraphStyle.ellipsized(); 391cb93a386Sopenharmony_ci value->ellipsisModal = paragraph.fParagraphStyle.getEllipsisMod(); 392cb93a386Sopenharmony_ci} 393cb93a386Sopenharmony_ci 394cb93a386Sopenharmony_cibool ParagraphCache::GetStoredLayout(ParagraphImpl& paragraph) { 395cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 396cb93a386Sopenharmony_ci TEXT_TRACE_FUNC(); 397cb93a386Sopenharmony_ci#endif 398cb93a386Sopenharmony_ci SkAutoMutexExclusive lock(fParagraphMutex); 399cb93a386Sopenharmony_ci auto key = ParagraphCacheKey(¶graph); 400cb93a386Sopenharmony_ci std::unique_ptr<Entry>* entry = fLRUCacheMap.find(key); 401cb93a386Sopenharmony_ci if (!entry || !*entry) { 402cb93a386Sopenharmony_ci return false; 403cb93a386Sopenharmony_ci } 404cb93a386Sopenharmony_ci ParagraphCacheValue* value = (*entry)->fValue.get(); 405cb93a386Sopenharmony_ci if (!value) { 406cb93a386Sopenharmony_ci return false; 407cb93a386Sopenharmony_ci } 408cb93a386Sopenharmony_ci // Check if we have a match, that should be pretty much only lentgh and wrapping modes 409cb93a386Sopenharmony_ci // if the paragraph and text style match otherwise 410cb93a386Sopenharmony_ci if (!useCachedLayout(paragraph, value)) { 411cb93a386Sopenharmony_ci return false; 412cb93a386Sopenharmony_ci } 413cb93a386Sopenharmony_ci // Need to ensure we have sufficient info for restoring 414cb93a386Sopenharmony_ci // need some additionaö metrics 415cb93a386Sopenharmony_ci if (value->fLines.empty()) { 416cb93a386Sopenharmony_ci return false; 417cb93a386Sopenharmony_ci } 418cb93a386Sopenharmony_ci if (paragraph.fRuns.size() == value->fRuns.size()) { 419cb93a386Sopenharmony_ci // get PlaceholderRun metrics for placeholder alignment 420cb93a386Sopenharmony_ci for (size_t idx = 0; idx < value->fRuns.size(); ++idx) { 421cb93a386Sopenharmony_ci if (!value->fRuns[idx].isPlaceholder()) { 422cb93a386Sopenharmony_ci continue; 423cb93a386Sopenharmony_ci } 424cb93a386Sopenharmony_ci paragraph.fRuns[idx].fFontMetrics = value->fRuns[idx].fFontMetrics; 425cb93a386Sopenharmony_ci paragraph.fRuns[idx].fCorrectAscent = value->fRuns[idx].fCorrectAscent; 426cb93a386Sopenharmony_ci paragraph.fRuns[idx].fCorrectDescent = value->fRuns[idx].fCorrectDescent; 427cb93a386Sopenharmony_ci } 428cb93a386Sopenharmony_ci } 429cb93a386Sopenharmony_ci paragraph.fLines.reset(); 430cb93a386Sopenharmony_ci for (auto& line : value->fLines) { 431cb93a386Sopenharmony_ci paragraph.fLines.emplace_back(line.CloneSelf()); 432cb93a386Sopenharmony_ci paragraph.fLines.back().setParagraphImpl(¶graph); 433cb93a386Sopenharmony_ci } 434cb93a386Sopenharmony_ci paragraph.setSize(value->fHeight, value->fWidth, value->fLongestLine); 435cb93a386Sopenharmony_ci paragraph.setLongestLineWithIndent(value->fLongestLineWithIndent); 436cb93a386Sopenharmony_ci paragraph.setIntrinsicSize(value->fMaxIntrinsicWidth, value->fMinIntrinsicWidth, 437cb93a386Sopenharmony_ci value->fAlphabeticBaseline, value->fIdeographicBaseline, 438cb93a386Sopenharmony_ci value->fExceededMaxLines); 439cb93a386Sopenharmony_ci return true; 440cb93a386Sopenharmony_ci} 441cb93a386Sopenharmony_ci#endif 442cb93a386Sopenharmony_ci 443cb93a386Sopenharmony_cibool ParagraphCache::findParagraph(ParagraphImpl* paragraph) { 444cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 445cb93a386Sopenharmony_ci TEXT_TRACE_FUNC(); 446cb93a386Sopenharmony_ci#endif 447cb93a386Sopenharmony_ci if (!fCacheIsOn) { 448cb93a386Sopenharmony_ci return false; 449cb93a386Sopenharmony_ci } 450cb93a386Sopenharmony_ci#ifdef PARAGRAPH_CACHE_STATS 451cb93a386Sopenharmony_ci ++fTotalRequests; 452cb93a386Sopenharmony_ci#endif 453cb93a386Sopenharmony_ci SkAutoMutexExclusive lock(fParagraphMutex); 454cb93a386Sopenharmony_ci ParagraphCacheKey key(paragraph); 455cb93a386Sopenharmony_ci std::unique_ptr<Entry>* entry = fLRUCacheMap.find(key); 456cb93a386Sopenharmony_ci 457cb93a386Sopenharmony_ci if (!entry) { 458cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT 459cb93a386Sopenharmony_ci LOGD("ParagraphCache: cache miss, hash-%{public}d", key.hash()); 460cb93a386Sopenharmony_ci#endif 461cb93a386Sopenharmony_ci // We have a cache miss 462cb93a386Sopenharmony_ci#ifdef PARAGRAPH_CACHE_STATS 463cb93a386Sopenharmony_ci ++fCacheMisses; 464cb93a386Sopenharmony_ci#endif 465cb93a386Sopenharmony_ci fChecker(paragraph, "missingParagraph", true); 466cb93a386Sopenharmony_ci return false; 467cb93a386Sopenharmony_ci } 468cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT 469cb93a386Sopenharmony_ci LOGD("ParagraphCache: cache hit, hash-%{public}d", key.hash()); 470cb93a386Sopenharmony_ci#endif 471cb93a386Sopenharmony_ci updateTo(paragraph, entry->get()); 472cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 473cb93a386Sopenharmony_ci#ifdef USE_UNSAFE_CACHED_VALUE 474cb93a386Sopenharmony_ci fLastCachedValue = entry->get()->fValue.get(); 475cb93a386Sopenharmony_ci#endif 476cb93a386Sopenharmony_ci paragraph->hash() = key.hash(); 477cb93a386Sopenharmony_ci#endif 478cb93a386Sopenharmony_ci fChecker(paragraph, "foundParagraph", true); 479cb93a386Sopenharmony_ci return true; 480cb93a386Sopenharmony_ci} 481cb93a386Sopenharmony_ci 482cb93a386Sopenharmony_cibool ParagraphCache::updateParagraph(ParagraphImpl* paragraph) { 483cb93a386Sopenharmony_ci if (!fCacheIsOn) { 484cb93a386Sopenharmony_ci return false; 485cb93a386Sopenharmony_ci } 486cb93a386Sopenharmony_ci#ifdef PARAGRAPH_CACHE_STATS 487cb93a386Sopenharmony_ci ++fTotalRequests; 488cb93a386Sopenharmony_ci#endif 489cb93a386Sopenharmony_ci SkAutoMutexExclusive lock(fParagraphMutex); 490cb93a386Sopenharmony_ci 491cb93a386Sopenharmony_ci ParagraphCacheKey key(paragraph); 492cb93a386Sopenharmony_ci std::unique_ptr<Entry>* entry = fLRUCacheMap.find(key); 493cb93a386Sopenharmony_ci if (!entry) { 494cb93a386Sopenharmony_ci // isTooMuchMemoryWasted(paragraph) not needed for now 495cb93a386Sopenharmony_ci if (isPossiblyTextEditing(paragraph)) { 496cb93a386Sopenharmony_ci // Skip this paragraph 497cb93a386Sopenharmony_ci return false; 498cb93a386Sopenharmony_ci } 499cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 500cb93a386Sopenharmony_ci paragraph->hash() = key.hash(); 501cb93a386Sopenharmony_ci#endif 502cb93a386Sopenharmony_ci ParagraphCacheValue* value = new ParagraphCacheValue(std::move(key), paragraph); 503cb93a386Sopenharmony_ci fLRUCacheMap.insert(value->fKey, std::make_unique<Entry>(value)); 504cb93a386Sopenharmony_ci fChecker(paragraph, "addedParagraph", true); 505cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 506cb93a386Sopenharmony_ci#ifdef USE_UNSAFE_CACHED_VALUE 507cb93a386Sopenharmony_ci fLastCachedValue = value; 508cb93a386Sopenharmony_ci#endif 509cb93a386Sopenharmony_ci#else 510cb93a386Sopenharmony_ci fLastCachedValue = value; 511cb93a386Sopenharmony_ci#endif 512cb93a386Sopenharmony_ci return true; 513cb93a386Sopenharmony_ci } else { 514cb93a386Sopenharmony_ci // We do not have to update the paragraph 515cb93a386Sopenharmony_ci return false; 516cb93a386Sopenharmony_ci } 517cb93a386Sopenharmony_ci} 518cb93a386Sopenharmony_ci 519cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT 520cb93a386Sopenharmony_ci// caller needs to hold fParagraphMutex 521cb93a386Sopenharmony_ciParagraphCacheValue* ParagraphCache::cacheLayout(ParagraphImpl* paragraph) { 522cb93a386Sopenharmony_ci if (!fCacheIsOn) { 523cb93a386Sopenharmony_ci return nullptr; 524cb93a386Sopenharmony_ci } 525cb93a386Sopenharmony_ci#ifdef PARAGRAPH_CACHE_STATS 526cb93a386Sopenharmony_ci ++fTotalRequests; 527cb93a386Sopenharmony_ci#endif 528cb93a386Sopenharmony_ci 529cb93a386Sopenharmony_ci ParagraphCacheKey key(paragraph); 530cb93a386Sopenharmony_ci std::unique_ptr<Entry>* entry = fLRUCacheMap.find(key); 531cb93a386Sopenharmony_ci if (!entry) { 532cb93a386Sopenharmony_ci // isTooMuchMemoryWasted(paragraph) not needed for now 533cb93a386Sopenharmony_ci if (isPossiblyTextEditing(paragraph)) { 534cb93a386Sopenharmony_ci // Skip this paragraph 535cb93a386Sopenharmony_ci return nullptr; 536cb93a386Sopenharmony_ci } 537cb93a386Sopenharmony_ci paragraph->hash() = key.hash(); 538cb93a386Sopenharmony_ci ParagraphCacheValue* value = new ParagraphCacheValue(std::move(key), paragraph); 539cb93a386Sopenharmony_ci fLRUCacheMap.insert(value->fKey, std::make_unique<Entry>(value)); 540cb93a386Sopenharmony_ci fChecker(paragraph, "addedParagraph", true); 541cb93a386Sopenharmony_ci#ifdef USE_UNSAFE_CACHED_VALUE 542cb93a386Sopenharmony_ci fLastCachedValue = value; 543cb93a386Sopenharmony_ci#endif 544cb93a386Sopenharmony_ci return value; 545cb93a386Sopenharmony_ci } else { 546cb93a386Sopenharmony_ci // Paragraph&layout already cached 547cb93a386Sopenharmony_ci return nullptr; 548cb93a386Sopenharmony_ci } 549cb93a386Sopenharmony_ci} 550cb93a386Sopenharmony_ci#endif 551cb93a386Sopenharmony_ci 552cb93a386Sopenharmony_ci// Special situation: (very) long paragraph that is close to the last formatted paragraph 553cb93a386Sopenharmony_ci#define NOCACHE_PREFIX_LENGTH 40 554cb93a386Sopenharmony_cibool ParagraphCache::isPossiblyTextEditing(ParagraphImpl* paragraph) { 555cb93a386Sopenharmony_ci if (fLastCachedValue == nullptr) { 556cb93a386Sopenharmony_ci return false; 557cb93a386Sopenharmony_ci } 558cb93a386Sopenharmony_ci 559cb93a386Sopenharmony_ci auto& lastText = fLastCachedValue->fKey.text(); 560cb93a386Sopenharmony_ci auto& text = paragraph->fText; 561cb93a386Sopenharmony_ci 562cb93a386Sopenharmony_ci if ((lastText.size() < NOCACHE_PREFIX_LENGTH) || (text.size() < NOCACHE_PREFIX_LENGTH)) { 563cb93a386Sopenharmony_ci // Either last text or the current are too short 564cb93a386Sopenharmony_ci return false; 565cb93a386Sopenharmony_ci } 566cb93a386Sopenharmony_ci 567cb93a386Sopenharmony_ci if (std::strncmp(lastText.c_str(), text.c_str(), NOCACHE_PREFIX_LENGTH) == 0) { 568cb93a386Sopenharmony_ci // Texts have the same starts 569cb93a386Sopenharmony_ci return true; 570cb93a386Sopenharmony_ci } 571cb93a386Sopenharmony_ci 572cb93a386Sopenharmony_ci if (std::strncmp(lastText.c_str() + lastText.size() - NOCACHE_PREFIX_LENGTH, &text[text.size() - NOCACHE_PREFIX_LENGTH], NOCACHE_PREFIX_LENGTH) == 0) { 573cb93a386Sopenharmony_ci // Texts have the same ends 574cb93a386Sopenharmony_ci return true; 575cb93a386Sopenharmony_ci } 576cb93a386Sopenharmony_ci 577cb93a386Sopenharmony_ci // It does not look like editing the text 578cb93a386Sopenharmony_ci return false; 579cb93a386Sopenharmony_ci} 580cb93a386Sopenharmony_ci} // namespace textlayout 581cb93a386Sopenharmony_ci} // namespace skia 582