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(&paragraph);
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(&paragraph)) {
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(&paragraph);
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(&paragraph);
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