xref: /aosp_15_r20/external/skia/modules/skparagraph/src/ParagraphCache.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker // Copyright 2019 Google LLC.
2*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
3*c8dee2aaSAndroid Build Coastguard Worker 
4*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skparagraph/include/FontArguments.h"
5*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skparagraph/include/ParagraphCache.h"
6*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skparagraph/src/ParagraphImpl.h"
7*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkFloatBits.h"
8*c8dee2aaSAndroid Build Coastguard Worker 
9*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker namespace skia {
12*c8dee2aaSAndroid Build Coastguard Worker namespace textlayout {
13*c8dee2aaSAndroid Build Coastguard Worker 
14*c8dee2aaSAndroid Build Coastguard Worker namespace {
relax(SkScalar a)15*c8dee2aaSAndroid Build Coastguard Worker     int32_t relax(SkScalar a) {
16*c8dee2aaSAndroid Build Coastguard Worker         // This rounding is done to match Flutter tests. Must be removed..
17*c8dee2aaSAndroid Build Coastguard Worker         if (SkIsFinite(a)) {
18*c8dee2aaSAndroid Build Coastguard Worker           auto threshold = SkIntToScalar(1 << 12);
19*c8dee2aaSAndroid Build Coastguard Worker           return SkFloat2Bits(SkScalarRoundToScalar(a * threshold)/threshold);
20*c8dee2aaSAndroid Build Coastguard Worker         } else {
21*c8dee2aaSAndroid Build Coastguard Worker           return SkFloat2Bits(a);
22*c8dee2aaSAndroid Build Coastguard Worker         }
23*c8dee2aaSAndroid Build Coastguard Worker     }
24*c8dee2aaSAndroid Build Coastguard Worker 
exactlyEqual(SkScalar x,SkScalar y)25*c8dee2aaSAndroid Build Coastguard Worker     bool exactlyEqual(SkScalar x, SkScalar y) {
26*c8dee2aaSAndroid Build Coastguard Worker         return x == y || (x != x && y != y);
27*c8dee2aaSAndroid Build Coastguard Worker     }
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
30*c8dee2aaSAndroid Build Coastguard Worker 
31*c8dee2aaSAndroid Build Coastguard Worker class ParagraphCacheKey {
32*c8dee2aaSAndroid Build Coastguard Worker public:
ParagraphCacheKey(const ParagraphImpl * paragraph)33*c8dee2aaSAndroid Build Coastguard Worker     ParagraphCacheKey(const ParagraphImpl* paragraph)
34*c8dee2aaSAndroid Build Coastguard Worker         : fText(paragraph->fText.c_str(), paragraph->fText.size())
35*c8dee2aaSAndroid Build Coastguard Worker         , fPlaceholders(paragraph->fPlaceholders)
36*c8dee2aaSAndroid Build Coastguard Worker         , fTextStyles(paragraph->fTextStyles)
37*c8dee2aaSAndroid Build Coastguard Worker         , fParagraphStyle(paragraph->paragraphStyle()) {
38*c8dee2aaSAndroid Build Coastguard Worker         fHash = computeHash();
39*c8dee2aaSAndroid Build Coastguard Worker     }
40*c8dee2aaSAndroid Build Coastguard Worker 
41*c8dee2aaSAndroid Build Coastguard Worker     ParagraphCacheKey(const ParagraphCacheKey& other) = default;
42*c8dee2aaSAndroid Build Coastguard Worker 
ParagraphCacheKey(ParagraphCacheKey && other)43*c8dee2aaSAndroid Build Coastguard Worker     ParagraphCacheKey(ParagraphCacheKey&& other)
44*c8dee2aaSAndroid Build Coastguard Worker         : fText(std::move(other.fText))
45*c8dee2aaSAndroid Build Coastguard Worker         , fPlaceholders(std::move(other.fPlaceholders))
46*c8dee2aaSAndroid Build Coastguard Worker         , fTextStyles(std::move(other.fTextStyles))
47*c8dee2aaSAndroid Build Coastguard Worker         , fParagraphStyle(std::move(other.fParagraphStyle))
48*c8dee2aaSAndroid Build Coastguard Worker         , fHash(other.fHash) {
49*c8dee2aaSAndroid Build Coastguard Worker         other.fHash = 0;
50*c8dee2aaSAndroid Build Coastguard Worker     }
51*c8dee2aaSAndroid Build Coastguard Worker 
52*c8dee2aaSAndroid Build Coastguard Worker     bool operator==(const ParagraphCacheKey& other) const;
53*c8dee2aaSAndroid Build Coastguard Worker 
hash() const54*c8dee2aaSAndroid Build Coastguard Worker     uint32_t hash() const { return fHash; }
55*c8dee2aaSAndroid Build Coastguard Worker 
text() const56*c8dee2aaSAndroid Build Coastguard Worker     const SkString& text() const { return fText; }
57*c8dee2aaSAndroid Build Coastguard Worker 
58*c8dee2aaSAndroid Build Coastguard Worker private:
59*c8dee2aaSAndroid Build Coastguard Worker     static uint32_t mix(uint32_t hash, uint32_t data);
60*c8dee2aaSAndroid Build Coastguard Worker     uint32_t computeHash() const;
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker     SkString fText;
63*c8dee2aaSAndroid Build Coastguard Worker     TArray<Placeholder, true> fPlaceholders;
64*c8dee2aaSAndroid Build Coastguard Worker     TArray<Block, true> fTextStyles;
65*c8dee2aaSAndroid Build Coastguard Worker     ParagraphStyle fParagraphStyle;
66*c8dee2aaSAndroid Build Coastguard Worker     uint32_t fHash;
67*c8dee2aaSAndroid Build Coastguard Worker };
68*c8dee2aaSAndroid Build Coastguard Worker 
69*c8dee2aaSAndroid Build Coastguard Worker class ParagraphCacheValue {
70*c8dee2aaSAndroid Build Coastguard Worker public:
ParagraphCacheValue(ParagraphCacheKey && key,const ParagraphImpl * paragraph)71*c8dee2aaSAndroid Build Coastguard Worker     ParagraphCacheValue(ParagraphCacheKey&& key, const ParagraphImpl* paragraph)
72*c8dee2aaSAndroid Build Coastguard Worker         : fKey(std::move(key))
73*c8dee2aaSAndroid Build Coastguard Worker         , fRuns(paragraph->fRuns)
74*c8dee2aaSAndroid Build Coastguard Worker         , fClusters(paragraph->fClusters)
75*c8dee2aaSAndroid Build Coastguard Worker         , fClustersIndexFromCodeUnit(paragraph->fClustersIndexFromCodeUnit)
76*c8dee2aaSAndroid Build Coastguard Worker         , fCodeUnitProperties(paragraph->fCodeUnitProperties)
77*c8dee2aaSAndroid Build Coastguard Worker         , fWords(paragraph->fWords)
78*c8dee2aaSAndroid Build Coastguard Worker         , fBidiRegions(paragraph->fBidiRegions)
79*c8dee2aaSAndroid Build Coastguard Worker         , fHasLineBreaks(paragraph->fHasLineBreaks)
80*c8dee2aaSAndroid Build Coastguard Worker         , fHasWhitespacesInside(paragraph->fHasWhitespacesInside)
81*c8dee2aaSAndroid Build Coastguard Worker         , fTrailingSpaces(paragraph->fTrailingSpaces) { }
82*c8dee2aaSAndroid Build Coastguard Worker 
83*c8dee2aaSAndroid Build Coastguard Worker     // Input == key
84*c8dee2aaSAndroid Build Coastguard Worker     ParagraphCacheKey fKey;
85*c8dee2aaSAndroid Build Coastguard Worker 
86*c8dee2aaSAndroid Build Coastguard Worker     // Shaped results
87*c8dee2aaSAndroid Build Coastguard Worker     TArray<Run, false> fRuns;
88*c8dee2aaSAndroid Build Coastguard Worker     TArray<Cluster, true> fClusters;
89*c8dee2aaSAndroid Build Coastguard Worker     TArray<size_t, true> fClustersIndexFromCodeUnit;
90*c8dee2aaSAndroid Build Coastguard Worker     // ICU results
91*c8dee2aaSAndroid Build Coastguard Worker     TArray<SkUnicode::CodeUnitFlags, true> fCodeUnitProperties;
92*c8dee2aaSAndroid Build Coastguard Worker     std::vector<size_t> fWords;
93*c8dee2aaSAndroid Build Coastguard Worker     std::vector<SkUnicode::BidiRegion> fBidiRegions;
94*c8dee2aaSAndroid Build Coastguard Worker     bool fHasLineBreaks;
95*c8dee2aaSAndroid Build Coastguard Worker     bool fHasWhitespacesInside;
96*c8dee2aaSAndroid Build Coastguard Worker     TextIndex fTrailingSpaces;
97*c8dee2aaSAndroid Build Coastguard Worker };
98*c8dee2aaSAndroid Build Coastguard Worker 
mix(uint32_t hash,uint32_t data)99*c8dee2aaSAndroid Build Coastguard Worker uint32_t ParagraphCacheKey::mix(uint32_t hash, uint32_t data) {
100*c8dee2aaSAndroid Build Coastguard Worker     hash += data;
101*c8dee2aaSAndroid Build Coastguard Worker     hash += (hash << 10);
102*c8dee2aaSAndroid Build Coastguard Worker     hash ^= (hash >> 6);
103*c8dee2aaSAndroid Build Coastguard Worker     return hash;
104*c8dee2aaSAndroid Build Coastguard Worker }
105*c8dee2aaSAndroid Build Coastguard Worker 
computeHash() const106*c8dee2aaSAndroid Build Coastguard Worker uint32_t ParagraphCacheKey::computeHash() const {
107*c8dee2aaSAndroid Build Coastguard Worker     uint32_t hash = 0;
108*c8dee2aaSAndroid Build Coastguard Worker     for (auto& ph : fPlaceholders) {
109*c8dee2aaSAndroid Build Coastguard Worker         if (ph.fRange.width() == 0) {
110*c8dee2aaSAndroid Build Coastguard Worker             continue;
111*c8dee2aaSAndroid Build Coastguard Worker         }
112*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(ph.fRange));
113*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fHeight)));
114*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fWidth)));
115*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(ph.fStyle.fAlignment));
116*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(ph.fStyle.fBaseline));
117*c8dee2aaSAndroid Build Coastguard Worker         if (ph.fStyle.fAlignment == PlaceholderAlignment::kBaseline) {
118*c8dee2aaSAndroid Build Coastguard Worker             hash = mix(hash, SkGoodHash()(relax(ph.fStyle.fBaselineOffset)));
119*c8dee2aaSAndroid Build Coastguard Worker         }
120*c8dee2aaSAndroid Build Coastguard Worker     }
121*c8dee2aaSAndroid Build Coastguard Worker 
122*c8dee2aaSAndroid Build Coastguard Worker     for (auto& ts : fTextStyles) {
123*c8dee2aaSAndroid Build Coastguard Worker         if (ts.fStyle.isPlaceholder()) {
124*c8dee2aaSAndroid Build Coastguard Worker             continue;
125*c8dee2aaSAndroid Build Coastguard Worker         }
126*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getLetterSpacing())));
127*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getWordSpacing())));
128*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(ts.fStyle.getLocale()));
129*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getHeight())));
130*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getBaselineShift())));
131*c8dee2aaSAndroid Build Coastguard Worker         for (auto& ff : ts.fStyle.getFontFamilies()) {
132*c8dee2aaSAndroid Build Coastguard Worker             hash = mix(hash, SkGoodHash()(ff));
133*c8dee2aaSAndroid Build Coastguard Worker         }
134*c8dee2aaSAndroid Build Coastguard Worker         for (auto& ff : ts.fStyle.getFontFeatures()) {
135*c8dee2aaSAndroid Build Coastguard Worker             hash = mix(hash, SkGoodHash()(ff.fValue));
136*c8dee2aaSAndroid Build Coastguard Worker             hash = mix(hash, SkGoodHash()(ff.fName));
137*c8dee2aaSAndroid Build Coastguard Worker         }
138*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, std::hash<std::optional<FontArguments>>()(ts.fStyle.getFontArguments()));
139*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(ts.fStyle.getFontStyle()));
140*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getFontSize())));
141*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(ts.fRange));
142*c8dee2aaSAndroid Build Coastguard Worker     }
143*c8dee2aaSAndroid Build Coastguard Worker 
144*c8dee2aaSAndroid Build Coastguard Worker     hash = mix(hash, SkGoodHash()(relax(fParagraphStyle.getHeight())));
145*c8dee2aaSAndroid Build Coastguard Worker     hash = mix(hash, SkGoodHash()(fParagraphStyle.getTextDirection()));
146*c8dee2aaSAndroid Build Coastguard Worker     hash = mix(hash, SkGoodHash()(fParagraphStyle.getReplaceTabCharacters() ? 1 : 0));
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker     auto& strutStyle = fParagraphStyle.getStrutStyle();
149*c8dee2aaSAndroid Build Coastguard Worker     if (strutStyle.getStrutEnabled()) {
150*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(relax(strutStyle.getHeight())));
151*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(relax(strutStyle.getLeading())));
152*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(relax(strutStyle.getFontSize())));
153*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(strutStyle.getHeightOverride()));
154*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(strutStyle.getFontStyle()));
155*c8dee2aaSAndroid Build Coastguard Worker         hash = mix(hash, SkGoodHash()(strutStyle.getForceStrutHeight()));
156*c8dee2aaSAndroid Build Coastguard Worker         for (auto& ff : strutStyle.getFontFamilies()) {
157*c8dee2aaSAndroid Build Coastguard Worker             hash = mix(hash, SkGoodHash()(ff));
158*c8dee2aaSAndroid Build Coastguard Worker         }
159*c8dee2aaSAndroid Build Coastguard Worker     }
160*c8dee2aaSAndroid Build Coastguard Worker 
161*c8dee2aaSAndroid Build Coastguard Worker     hash = mix(hash, SkGoodHash()(fText));
162*c8dee2aaSAndroid Build Coastguard Worker     return hash;
163*c8dee2aaSAndroid Build Coastguard Worker }
164*c8dee2aaSAndroid Build Coastguard Worker 
operator ()(const ParagraphCacheKey & key) const165*c8dee2aaSAndroid Build Coastguard Worker uint32_t ParagraphCache::KeyHash::operator()(const ParagraphCacheKey& key) const {
166*c8dee2aaSAndroid Build Coastguard Worker     return key.hash();
167*c8dee2aaSAndroid Build Coastguard Worker }
168*c8dee2aaSAndroid Build Coastguard Worker 
operator ==(const ParagraphCacheKey & other) const169*c8dee2aaSAndroid Build Coastguard Worker bool ParagraphCacheKey::operator==(const ParagraphCacheKey& other) const {
170*c8dee2aaSAndroid Build Coastguard Worker     if (fText.size() != other.fText.size()) {
171*c8dee2aaSAndroid Build Coastguard Worker         return false;
172*c8dee2aaSAndroid Build Coastguard Worker     }
173*c8dee2aaSAndroid Build Coastguard Worker     if (fPlaceholders.size() != other.fPlaceholders.size()) {
174*c8dee2aaSAndroid Build Coastguard Worker         return false;
175*c8dee2aaSAndroid Build Coastguard Worker     }
176*c8dee2aaSAndroid Build Coastguard Worker     if (fText != other.fText) {
177*c8dee2aaSAndroid Build Coastguard Worker         return false;
178*c8dee2aaSAndroid Build Coastguard Worker     }
179*c8dee2aaSAndroid Build Coastguard Worker     if (fTextStyles.size() != other.fTextStyles.size()) {
180*c8dee2aaSAndroid Build Coastguard Worker         return false;
181*c8dee2aaSAndroid Build Coastguard Worker     }
182*c8dee2aaSAndroid Build Coastguard Worker 
183*c8dee2aaSAndroid Build Coastguard Worker     // There is no need to compare default paragraph styles - they are included into fTextStyles
184*c8dee2aaSAndroid Build Coastguard Worker     if (!exactlyEqual(fParagraphStyle.getHeight(), other.fParagraphStyle.getHeight())) {
185*c8dee2aaSAndroid Build Coastguard Worker         return false;
186*c8dee2aaSAndroid Build Coastguard Worker     }
187*c8dee2aaSAndroid Build Coastguard Worker     if (fParagraphStyle.getTextDirection() != other.fParagraphStyle.getTextDirection()) {
188*c8dee2aaSAndroid Build Coastguard Worker         return false;
189*c8dee2aaSAndroid Build Coastguard Worker     }
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker     if (!(fParagraphStyle.getStrutStyle() == other.fParagraphStyle.getStrutStyle())) {
192*c8dee2aaSAndroid Build Coastguard Worker         return false;
193*c8dee2aaSAndroid Build Coastguard Worker     }
194*c8dee2aaSAndroid Build Coastguard Worker 
195*c8dee2aaSAndroid Build Coastguard Worker     if (!(fParagraphStyle.getReplaceTabCharacters() == other.fParagraphStyle.getReplaceTabCharacters())) {
196*c8dee2aaSAndroid Build Coastguard Worker         return false;
197*c8dee2aaSAndroid Build Coastguard Worker     }
198*c8dee2aaSAndroid Build Coastguard Worker 
199*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < fTextStyles.size(); ++i) {
200*c8dee2aaSAndroid Build Coastguard Worker         auto& tsa = fTextStyles[i];
201*c8dee2aaSAndroid Build Coastguard Worker         auto& tsb = other.fTextStyles[i];
202*c8dee2aaSAndroid Build Coastguard Worker         if (tsa.fStyle.isPlaceholder()) {
203*c8dee2aaSAndroid Build Coastguard Worker             continue;
204*c8dee2aaSAndroid Build Coastguard Worker         }
205*c8dee2aaSAndroid Build Coastguard Worker         if (!(tsa.fStyle.equalsByFonts(tsb.fStyle))) {
206*c8dee2aaSAndroid Build Coastguard Worker             return false;
207*c8dee2aaSAndroid Build Coastguard Worker         }
208*c8dee2aaSAndroid Build Coastguard Worker         if (tsa.fRange.width() != tsb.fRange.width()) {
209*c8dee2aaSAndroid Build Coastguard Worker             return false;
210*c8dee2aaSAndroid Build Coastguard Worker         }
211*c8dee2aaSAndroid Build Coastguard Worker         if (tsa.fRange.start != tsb.fRange.start) {
212*c8dee2aaSAndroid Build Coastguard Worker             return false;
213*c8dee2aaSAndroid Build Coastguard Worker         }
214*c8dee2aaSAndroid Build Coastguard Worker     }
215*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < fPlaceholders.size(); ++i) {
216*c8dee2aaSAndroid Build Coastguard Worker         auto& tsa = fPlaceholders[i];
217*c8dee2aaSAndroid Build Coastguard Worker         auto& tsb = other.fPlaceholders[i];
218*c8dee2aaSAndroid Build Coastguard Worker         if (tsa.fRange.width() == 0 && tsb.fRange.width() == 0) {
219*c8dee2aaSAndroid Build Coastguard Worker             continue;
220*c8dee2aaSAndroid Build Coastguard Worker         }
221*c8dee2aaSAndroid Build Coastguard Worker         if (!(tsa.fStyle.equals(tsb.fStyle))) {
222*c8dee2aaSAndroid Build Coastguard Worker             return false;
223*c8dee2aaSAndroid Build Coastguard Worker         }
224*c8dee2aaSAndroid Build Coastguard Worker         if (tsa.fRange.width() != tsb.fRange.width()) {
225*c8dee2aaSAndroid Build Coastguard Worker             return false;
226*c8dee2aaSAndroid Build Coastguard Worker         }
227*c8dee2aaSAndroid Build Coastguard Worker         if (tsa.fRange.start != tsb.fRange.start) {
228*c8dee2aaSAndroid Build Coastguard Worker             return false;
229*c8dee2aaSAndroid Build Coastguard Worker         }
230*c8dee2aaSAndroid Build Coastguard Worker     }
231*c8dee2aaSAndroid Build Coastguard Worker 
232*c8dee2aaSAndroid Build Coastguard Worker     return true;
233*c8dee2aaSAndroid Build Coastguard Worker }
234*c8dee2aaSAndroid Build Coastguard Worker 
235*c8dee2aaSAndroid Build Coastguard Worker struct ParagraphCache::Entry {
236*c8dee2aaSAndroid Build Coastguard Worker 
Entryskia::textlayout::ParagraphCache::Entry237*c8dee2aaSAndroid Build Coastguard Worker     Entry(ParagraphCacheValue* value) : fValue(value) {}
238*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<ParagraphCacheValue> fValue;
239*c8dee2aaSAndroid Build Coastguard Worker };
240*c8dee2aaSAndroid Build Coastguard Worker 
ParagraphCache()241*c8dee2aaSAndroid Build Coastguard Worker ParagraphCache::ParagraphCache()
242*c8dee2aaSAndroid Build Coastguard Worker     : fChecker([](ParagraphImpl* impl, const char*, bool){ })
243*c8dee2aaSAndroid Build Coastguard Worker     , fLRUCacheMap(kMaxEntries)
244*c8dee2aaSAndroid Build Coastguard Worker     , fCacheIsOn(true)
245*c8dee2aaSAndroid Build Coastguard Worker     , fLastCachedValue(nullptr)
246*c8dee2aaSAndroid Build Coastguard Worker #ifdef PARAGRAPH_CACHE_STATS
247*c8dee2aaSAndroid Build Coastguard Worker     , fTotalRequests(0)
248*c8dee2aaSAndroid Build Coastguard Worker     , fCacheMisses(0)
249*c8dee2aaSAndroid Build Coastguard Worker     , fHashMisses(0)
250*c8dee2aaSAndroid Build Coastguard Worker #endif
251*c8dee2aaSAndroid Build Coastguard Worker { }
252*c8dee2aaSAndroid Build Coastguard Worker 
~ParagraphCache()253*c8dee2aaSAndroid Build Coastguard Worker ParagraphCache::~ParagraphCache() { }
254*c8dee2aaSAndroid Build Coastguard Worker 
updateTo(ParagraphImpl * paragraph,const Entry * entry)255*c8dee2aaSAndroid Build Coastguard Worker void ParagraphCache::updateTo(ParagraphImpl* paragraph, const Entry* entry) {
256*c8dee2aaSAndroid Build Coastguard Worker 
257*c8dee2aaSAndroid Build Coastguard Worker     paragraph->fRuns.clear();
258*c8dee2aaSAndroid Build Coastguard Worker     paragraph->fRuns = entry->fValue->fRuns;
259*c8dee2aaSAndroid Build Coastguard Worker     paragraph->fClusters = entry->fValue->fClusters;
260*c8dee2aaSAndroid Build Coastguard Worker     paragraph->fClustersIndexFromCodeUnit = entry->fValue->fClustersIndexFromCodeUnit;
261*c8dee2aaSAndroid Build Coastguard Worker     paragraph->fCodeUnitProperties = entry->fValue->fCodeUnitProperties;
262*c8dee2aaSAndroid Build Coastguard Worker     paragraph->fWords = entry->fValue->fWords;
263*c8dee2aaSAndroid Build Coastguard Worker     paragraph->fBidiRegions = entry->fValue->fBidiRegions;
264*c8dee2aaSAndroid Build Coastguard Worker     paragraph->fHasLineBreaks = entry->fValue->fHasLineBreaks;
265*c8dee2aaSAndroid Build Coastguard Worker     paragraph->fHasWhitespacesInside = entry->fValue->fHasWhitespacesInside;
266*c8dee2aaSAndroid Build Coastguard Worker     paragraph->fTrailingSpaces = entry->fValue->fTrailingSpaces;
267*c8dee2aaSAndroid Build Coastguard Worker     for (auto& run : paragraph->fRuns) {
268*c8dee2aaSAndroid Build Coastguard Worker         run.setOwner(paragraph);
269*c8dee2aaSAndroid Build Coastguard Worker     }
270*c8dee2aaSAndroid Build Coastguard Worker     for (auto& cluster : paragraph->fClusters) {
271*c8dee2aaSAndroid Build Coastguard Worker         cluster.setOwner(paragraph);
272*c8dee2aaSAndroid Build Coastguard Worker     }
273*c8dee2aaSAndroid Build Coastguard Worker }
274*c8dee2aaSAndroid Build Coastguard Worker 
printStatistics()275*c8dee2aaSAndroid Build Coastguard Worker void ParagraphCache::printStatistics() {
276*c8dee2aaSAndroid Build Coastguard Worker     SkDebugf("--- Paragraph Cache ---\n");
277*c8dee2aaSAndroid Build Coastguard Worker     SkDebugf("Total requests: %d\n", fTotalRequests);
278*c8dee2aaSAndroid Build Coastguard Worker     SkDebugf("Cache misses: %d\n", fCacheMisses);
279*c8dee2aaSAndroid Build Coastguard Worker     SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? 100.f * fCacheMisses / fTotalRequests : 0.f);
280*c8dee2aaSAndroid Build Coastguard Worker     int cacheHits = fTotalRequests - fCacheMisses;
281*c8dee2aaSAndroid Build Coastguard Worker     SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f);
282*c8dee2aaSAndroid Build Coastguard Worker     SkDebugf("---------------------\n");
283*c8dee2aaSAndroid Build Coastguard Worker }
284*c8dee2aaSAndroid Build Coastguard Worker 
abandon()285*c8dee2aaSAndroid Build Coastguard Worker void ParagraphCache::abandon() {
286*c8dee2aaSAndroid Build Coastguard Worker     this->reset();
287*c8dee2aaSAndroid Build Coastguard Worker }
288*c8dee2aaSAndroid Build Coastguard Worker 
reset()289*c8dee2aaSAndroid Build Coastguard Worker void ParagraphCache::reset() {
290*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMutexExclusive lock(fParagraphMutex);
291*c8dee2aaSAndroid Build Coastguard Worker #ifdef PARAGRAPH_CACHE_STATS
292*c8dee2aaSAndroid Build Coastguard Worker     fTotalRequests = 0;
293*c8dee2aaSAndroid Build Coastguard Worker     fCacheMisses = 0;
294*c8dee2aaSAndroid Build Coastguard Worker     fHashMisses = 0;
295*c8dee2aaSAndroid Build Coastguard Worker #endif
296*c8dee2aaSAndroid Build Coastguard Worker     fLRUCacheMap.reset();
297*c8dee2aaSAndroid Build Coastguard Worker     fLastCachedValue = nullptr;
298*c8dee2aaSAndroid Build Coastguard Worker }
299*c8dee2aaSAndroid Build Coastguard Worker 
findParagraph(ParagraphImpl * paragraph)300*c8dee2aaSAndroid Build Coastguard Worker bool ParagraphCache::findParagraph(ParagraphImpl* paragraph) {
301*c8dee2aaSAndroid Build Coastguard Worker     if (!fCacheIsOn) {
302*c8dee2aaSAndroid Build Coastguard Worker         return false;
303*c8dee2aaSAndroid Build Coastguard Worker     }
304*c8dee2aaSAndroid Build Coastguard Worker #ifdef PARAGRAPH_CACHE_STATS
305*c8dee2aaSAndroid Build Coastguard Worker     ++fTotalRequests;
306*c8dee2aaSAndroid Build Coastguard Worker #endif
307*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMutexExclusive lock(fParagraphMutex);
308*c8dee2aaSAndroid Build Coastguard Worker     ParagraphCacheKey key(paragraph);
309*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Entry>* entry = fLRUCacheMap.find(key);
310*c8dee2aaSAndroid Build Coastguard Worker 
311*c8dee2aaSAndroid Build Coastguard Worker     if (!entry) {
312*c8dee2aaSAndroid Build Coastguard Worker         // We have a cache miss
313*c8dee2aaSAndroid Build Coastguard Worker #ifdef PARAGRAPH_CACHE_STATS
314*c8dee2aaSAndroid Build Coastguard Worker         ++fCacheMisses;
315*c8dee2aaSAndroid Build Coastguard Worker #endif
316*c8dee2aaSAndroid Build Coastguard Worker         fChecker(paragraph, "missingParagraph", true);
317*c8dee2aaSAndroid Build Coastguard Worker         return false;
318*c8dee2aaSAndroid Build Coastguard Worker     }
319*c8dee2aaSAndroid Build Coastguard Worker     updateTo(paragraph, entry->get());
320*c8dee2aaSAndroid Build Coastguard Worker     fChecker(paragraph, "foundParagraph", true);
321*c8dee2aaSAndroid Build Coastguard Worker     return true;
322*c8dee2aaSAndroid Build Coastguard Worker }
323*c8dee2aaSAndroid Build Coastguard Worker 
updateParagraph(ParagraphImpl * paragraph)324*c8dee2aaSAndroid Build Coastguard Worker bool ParagraphCache::updateParagraph(ParagraphImpl* paragraph) {
325*c8dee2aaSAndroid Build Coastguard Worker     if (!fCacheIsOn) {
326*c8dee2aaSAndroid Build Coastguard Worker         return false;
327*c8dee2aaSAndroid Build Coastguard Worker     }
328*c8dee2aaSAndroid Build Coastguard Worker #ifdef PARAGRAPH_CACHE_STATS
329*c8dee2aaSAndroid Build Coastguard Worker     ++fTotalRequests;
330*c8dee2aaSAndroid Build Coastguard Worker #endif
331*c8dee2aaSAndroid Build Coastguard Worker     SkAutoMutexExclusive lock(fParagraphMutex);
332*c8dee2aaSAndroid Build Coastguard Worker 
333*c8dee2aaSAndroid Build Coastguard Worker     ParagraphCacheKey key(paragraph);
334*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Entry>* entry = fLRUCacheMap.find(key);
335*c8dee2aaSAndroid Build Coastguard Worker     if (!entry) {
336*c8dee2aaSAndroid Build Coastguard Worker         // isTooMuchMemoryWasted(paragraph) not needed for now
337*c8dee2aaSAndroid Build Coastguard Worker         if (isPossiblyTextEditing(paragraph)) {
338*c8dee2aaSAndroid Build Coastguard Worker             // Skip this paragraph
339*c8dee2aaSAndroid Build Coastguard Worker             return false;
340*c8dee2aaSAndroid Build Coastguard Worker         }
341*c8dee2aaSAndroid Build Coastguard Worker         ParagraphCacheValue* value = new ParagraphCacheValue(std::move(key), paragraph);
342*c8dee2aaSAndroid Build Coastguard Worker         fLRUCacheMap.insert(value->fKey, std::make_unique<Entry>(value));
343*c8dee2aaSAndroid Build Coastguard Worker         fChecker(paragraph, "addedParagraph", true);
344*c8dee2aaSAndroid Build Coastguard Worker         fLastCachedValue = value;
345*c8dee2aaSAndroid Build Coastguard Worker         return true;
346*c8dee2aaSAndroid Build Coastguard Worker     } else {
347*c8dee2aaSAndroid Build Coastguard Worker         // We do not have to update the paragraph
348*c8dee2aaSAndroid Build Coastguard Worker         return false;
349*c8dee2aaSAndroid Build Coastguard Worker     }
350*c8dee2aaSAndroid Build Coastguard Worker }
351*c8dee2aaSAndroid Build Coastguard Worker 
352*c8dee2aaSAndroid Build Coastguard Worker // Special situation: (very) long paragraph that is close to the last formatted paragraph
353*c8dee2aaSAndroid Build Coastguard Worker #define NOCACHE_PREFIX_LENGTH 40
isPossiblyTextEditing(ParagraphImpl * paragraph)354*c8dee2aaSAndroid Build Coastguard Worker bool ParagraphCache::isPossiblyTextEditing(ParagraphImpl* paragraph) {
355*c8dee2aaSAndroid Build Coastguard Worker     if (fLastCachedValue == nullptr) {
356*c8dee2aaSAndroid Build Coastguard Worker         return false;
357*c8dee2aaSAndroid Build Coastguard Worker     }
358*c8dee2aaSAndroid Build Coastguard Worker 
359*c8dee2aaSAndroid Build Coastguard Worker     auto& lastText = fLastCachedValue->fKey.text();
360*c8dee2aaSAndroid Build Coastguard Worker     auto& text = paragraph->fText;
361*c8dee2aaSAndroid Build Coastguard Worker 
362*c8dee2aaSAndroid Build Coastguard Worker     if ((lastText.size() < NOCACHE_PREFIX_LENGTH) || (text.size() < NOCACHE_PREFIX_LENGTH)) {
363*c8dee2aaSAndroid Build Coastguard Worker         // Either last text or the current are too short
364*c8dee2aaSAndroid Build Coastguard Worker         return false;
365*c8dee2aaSAndroid Build Coastguard Worker     }
366*c8dee2aaSAndroid Build Coastguard Worker 
367*c8dee2aaSAndroid Build Coastguard Worker     if (std::strncmp(lastText.c_str(), text.c_str(), NOCACHE_PREFIX_LENGTH) == 0) {
368*c8dee2aaSAndroid Build Coastguard Worker         // Texts have the same starts
369*c8dee2aaSAndroid Build Coastguard Worker         return true;
370*c8dee2aaSAndroid Build Coastguard Worker     }
371*c8dee2aaSAndroid Build Coastguard Worker 
372*c8dee2aaSAndroid Build Coastguard Worker     if (std::strncmp(lastText.c_str() + lastText.size() - NOCACHE_PREFIX_LENGTH, &text[text.size() - NOCACHE_PREFIX_LENGTH], NOCACHE_PREFIX_LENGTH) == 0) {
373*c8dee2aaSAndroid Build Coastguard Worker         // Texts have the same ends
374*c8dee2aaSAndroid Build Coastguard Worker         return true;
375*c8dee2aaSAndroid Build Coastguard Worker     }
376*c8dee2aaSAndroid Build Coastguard Worker 
377*c8dee2aaSAndroid Build Coastguard Worker     // It does not look like editing the text
378*c8dee2aaSAndroid Build Coastguard Worker     return false;
379*c8dee2aaSAndroid Build Coastguard Worker }
380*c8dee2aaSAndroid Build Coastguard Worker }  // namespace textlayout
381*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skia
382