xref: /aosp_15_r20/frameworks/minikin/include/minikin/LayoutCache.h (revision 834a2baab5fdfc28e9a428ee87c7ea8f6a06a53d)
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef MINIKIN_LAYOUT_CACHE_H
18 #define MINIKIN_LAYOUT_CACHE_H
19 
20 #include <utils/LruCache.h>
21 
22 #include <mutex>
23 
24 #include "minikin/Constants.h"
25 #include "minikin/FontCollection.h"
26 #include "minikin/Hasher.h"
27 #include "minikin/LayoutCore.h"
28 #include "minikin/MinikinPaint.h"
29 #include "minikin/PackedVector.h"
30 
31 #ifdef _WIN32
32 #include <io.h>
33 #endif
34 
35 namespace minikin {
36 // Layout cache datatypes
37 class LayoutCacheKey {
38 public:
LayoutCacheKey(const U16StringPiece & text,const Range & range,const MinikinPaint & paint,bool dir,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen)39     LayoutCacheKey(const U16StringPiece& text, const Range& range, const MinikinPaint& paint,
40                    bool dir, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen)
41             : mChars(text.data(), text.size()),
42               mStart(range.getStart()),
43               mCount(range.getLength()),
44               mFontFlags(paint.fontFlags),
45               mId(paint.font->getId()),
46               mStyle(paint.fontStyle),
47               mSize(paint.size),
48               mScaleX(paint.scaleX),
49               mSkewX(paint.skewX),
50               mLetterSpacing(paint.letterSpacing),
51               mWordSpacing(paint.wordSpacing),
52               mLocaleListId(paint.localeListId),
53               mVerticalText(paint.verticalText),
54               mFamilyVariant(paint.familyVariant),
55               mStartHyphen(startHyphen),
56               mEndHyphen(endHyphen),
57               mIsRtl(dir),
58               mFontFeatureSettings(paint.fontFeatureSettings),
59               mVariationSettings(paint.fontVariationSettings),
60               mHash(computeHash()) {}
61 
62     bool operator==(const LayoutCacheKey& o) const {
63         return mId == o.mId && mStart == o.mStart && mCount == o.mCount && mStyle == o.mStyle &&
64                mSize == o.mSize && mScaleX == o.mScaleX && mSkewX == o.mSkewX &&
65                mLetterSpacing == o.mLetterSpacing && mWordSpacing == o.mWordSpacing &&
66                mFontFlags == o.mFontFlags && mLocaleListId == o.mLocaleListId &&
67                mVerticalText == o.mVerticalText && mFamilyVariant == o.mFamilyVariant &&
68                mStartHyphen == o.mStartHyphen && mEndHyphen == o.mEndHyphen && mIsRtl == o.mIsRtl &&
69                mFontFeatureSettings == o.mFontFeatureSettings && mChars == o.mChars &&
70                mVariationSettings == o.mVariationSettings;
71     }
72 
hash()73     android::hash_t hash() const { return mHash; }
74 
getMemoryUsage()75     uint32_t getMemoryUsage() const {
76         return sizeof(LayoutCacheKey) + sizeof(uint16_t) * mChars.size();
77     }
78 
79 private:
80     PackedVector<uint16_t, 12> mChars;
81     uint8_t mStart;
82     uint8_t mCount;
83     uint8_t mFontFlags;
84     uint32_t mId;  // for the font collection
85     FontStyle mStyle;
86     float mSize;
87     float mScaleX;
88     float mSkewX;
89     float mLetterSpacing;
90     float mWordSpacing;
91     uint32_t mLocaleListId;
92     bool mVerticalText;
93     FamilyVariant mFamilyVariant;
94     StartHyphenEdit mStartHyphen;
95     EndHyphenEdit mEndHyphen;
96     bool mIsRtl;
97     PackedVector<FontFeature> mFontFeatureSettings;
98     VariationSettings mVariationSettings;
99     // Note: any fields added to MinikinPaint must also be reflected here.
100     // TODO: language matching (possibly integrate into style)
101     android::hash_t mHash;
102 
computeHash()103     android::hash_t computeHash() const {
104         return Hasher()
105                 .update(mId)
106                 .update(mStart)
107                 .update(mCount)
108                 .update(mStyle.identifier())
109                 .update(mSize)
110                 .update(mScaleX)
111                 .update(mSkewX)
112                 .update(mLetterSpacing)
113                 .update(mVerticalText)
114                 .update(mWordSpacing)
115                 .update(mFontFlags)
116                 .update(mLocaleListId)
117                 .update(static_cast<uint8_t>(mFamilyVariant))
118                 .update(packHyphenEdit(mStartHyphen, mEndHyphen))
119                 .update(mIsRtl)
120                 .updateShorts(mChars.data(), mChars.size())
121                 .updatePackedVector(mFontFeatureSettings)
122                 .update(mVariationSettings)
123                 .hash();
124     }
125 };
126 
127 // A class holds a layout information and bounding box of it. The bounding box can be invalid if not
128 // calculated.
129 class LayoutSlot {
130 public:
LayoutSlot(LayoutPiece && layout)131     LayoutSlot(LayoutPiece&& layout)
132             : mLayout(std::move(layout)), mBounds(MinikinRect::makeInvalid()) {}
LayoutSlot(LayoutPiece && layout,MinikinRect && bounds)133     LayoutSlot(LayoutPiece&& layout, MinikinRect&& bounds)
134             : mLayout(std::move(layout)), mBounds(std::move(bounds)) {}
LayoutSlot(const LayoutPiece & layout,const MinikinRect & bounds)135     LayoutSlot(const LayoutPiece& layout, const MinikinRect& bounds)
136             : mLayout(layout), mBounds(bounds) {}
137 
138     const LayoutPiece mLayout;
139     MinikinRect mBounds;
140 
141 private:
142     LayoutSlot(const LayoutSlot&) = delete;
143     LayoutSlot& operator=(const LayoutSlot&) = delete;
144     LayoutSlot(LayoutSlot&&) = delete;
145     LayoutSlot& operator=(LayoutSlot&&) = delete;
146 };
147 
148 class LayoutCache : private android::OnEntryRemoved<LayoutCacheKey, LayoutSlot*> {
149 public:
clear()150     void clear() {
151         std::lock_guard<std::mutex> lock(mMutex);
152         mCache.clear();
153     }
154 
155     // Do not use LayoutCache inside the callback function, otherwise dead-lock may happen.
156     template <typename F>
getOrCreate(const U16StringPiece & text,const Range & range,const MinikinPaint & paint,bool dir,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen,bool boundsCalculation,F & f)157     void getOrCreate(const U16StringPiece& text, const Range& range, const MinikinPaint& paint,
158                      bool dir, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
159                      bool boundsCalculation, F& f) {
160         LayoutCacheKey key(text, range, paint, dir, startHyphen, endHyphen);
161         if (range.getLength() >= CHAR_LIMIT_FOR_CACHE) {
162             LayoutPiece piece(text, range, dir, paint, startHyphen, endHyphen);
163             if (boundsCalculation) {
164                 f(piece, paint, LayoutPiece::calculateBounds(piece, paint));
165             } else {
166                 f(piece, paint, MinikinRect::makeInvalid());
167             }
168             return;
169         }
170 
171         LayoutSlot* cachedSlot;
172         {
173             std::lock_guard<std::mutex> lock(mMutex);
174             cachedSlot = mCache.get(key);
175 
176             if (cachedSlot != nullptr) {
177                 if (boundsCalculation && !cachedSlot->mBounds.isValid()) {
178                     MinikinRect bounds = LayoutPiece::calculateBounds(cachedSlot->mLayout, paint);
179                     LayoutPiece lp = cachedSlot->mLayout;
180                     f(lp, paint, bounds);
181                     cachedSlot->mBounds = bounds;
182                 } else {
183                     f(cachedSlot->mLayout, paint, cachedSlot->mBounds);
184                 }
185                 return;
186             }
187         }
188 
189         std::unique_ptr<LayoutSlot> slot;
190         if (boundsCalculation) {
191             LayoutPiece lp = LayoutPiece(text, range, dir, paint, startHyphen, endHyphen);
192             MinikinRect rect = LayoutPiece::calculateBounds(lp, paint);
193 
194             slot = std::make_unique<LayoutSlot>(std::move(lp), std::move(rect));
195         } else {
196             slot = std::make_unique<LayoutSlot>(
197                     LayoutPiece(text, range, dir, paint, startHyphen, endHyphen));
198         }
199 
200         f(slot->mLayout, paint, slot->mBounds);
201         {
202             std::lock_guard<std::mutex> lock(mMutex);
203             mCache.put(key, slot.release());
204         }
205     }
206 
getInstance()207     static LayoutCache& getInstance() {
208         static LayoutCache cache(kMaxEntries);
209         return cache;
210     }
211 
212 protected:
LayoutCache(uint32_t maxEntries)213     LayoutCache(uint32_t maxEntries) : mCache(maxEntries) {
214         mCache.setOnEntryRemovedListener(this);
215     }
216 
getCacheSize()217     uint32_t getCacheSize() {
218         std::lock_guard<std::mutex> lock(mMutex);
219         return mCache.size();
220     }
221 
222 private:
223     // callback for OnEntryRemoved
operator()224     void operator()(LayoutCacheKey&, LayoutSlot*& value) { delete value; }
225 
226     android::LruCache<LayoutCacheKey, LayoutSlot*> mCache GUARDED_BY(mMutex);
227 
228     // static const size_t kMaxEntries = LruCache<LayoutCacheKey, Layout*>::kUnlimitedCapacity;
229 
230     // TODO: eviction based on memory footprint; for now, we just use a constant
231     // number of strings
232     static const size_t kMaxEntries = 5000;
233 
234     std::mutex mMutex;
235 };
236 
hash_type(const LayoutCacheKey & key)237 inline android::hash_t hash_type(const LayoutCacheKey& key) {
238     return key.hash();
239 }
240 
241 }  // namespace minikin
242 #endif  // MINIKIN_LAYOUT_CACHE_H
243