• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "minikin/LayoutCore.h"
21 
22 #include <mutex>
23 
24 #include <utils/LruCache.h>
25 
26 #include "minikin/FontCollection.h"
27 #include "minikin/Hasher.h"
28 #include "minikin/MinikinPaint.h"
29 
30 #ifdef _WIN32
31 #include <io.h>
32 #endif
33 
34 namespace minikin {
35 const uint32_t LENGTH_LIMIT_CACHE = 128;
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()),
42               mNchars(text.size()),
43               mStart(range.getStart()),
44               mCount(range.getLength()),
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               mFontFlags(paint.fontFlags),
53               mLocaleListId(paint.localeListId),
54               mFamilyVariant(paint.familyVariant),
55               mStartHyphen(startHyphen),
56               mEndHyphen(endHyphen),
57               mIsRtl(dir),
58               mHash(computeHash()) {}
59 
60     bool operator==(const LayoutCacheKey& o) const {
61         return mId == o.mId && mStart == o.mStart && mCount == o.mCount && mStyle == o.mStyle &&
62                mSize == o.mSize && mScaleX == o.mScaleX && mSkewX == o.mSkewX &&
63                mLetterSpacing == o.mLetterSpacing && mWordSpacing == o.mWordSpacing &&
64                mFontFlags == o.mFontFlags && mLocaleListId == o.mLocaleListId &&
65                mFamilyVariant == o.mFamilyVariant && mStartHyphen == o.mStartHyphen &&
66                mEndHyphen == o.mEndHyphen && mIsRtl == o.mIsRtl && mNchars == o.mNchars &&
67                !memcmp(mChars, o.mChars, mNchars * sizeof(uint16_t));
68     }
69 
hash()70     android::hash_t hash() const { return mHash; }
71 
copyText()72     void copyText() {
73         uint16_t* charsCopy = new uint16_t[mNchars];
74         memcpy(charsCopy, mChars, mNchars * sizeof(uint16_t));
75         mChars = charsCopy;
76     }
freeText()77     void freeText() {
78         delete[] mChars;
79         mChars = NULL;
80     }
81 
getMemoryUsage()82     uint32_t getMemoryUsage() const { return sizeof(LayoutCacheKey) + sizeof(uint16_t) * mNchars; }
83 
84 private:
85     const uint16_t* mChars;
86     uint32_t mNchars;
87     uint32_t mStart;
88     uint32_t mCount;
89     uint32_t mId;  // for the font collection
90     FontStyle mStyle;
91     float mSize;
92     float mScaleX;
93     float mSkewX;
94     float mLetterSpacing;
95     float mWordSpacing;
96     int32_t mFontFlags;
97     uint32_t mLocaleListId;
98     FamilyVariant mFamilyVariant;
99     StartHyphenEdit mStartHyphen;
100     EndHyphenEdit mEndHyphen;
101     bool mIsRtl;
102     // Note: any fields added to MinikinPaint must also be reflected here.
103     // TODO: language matching (possibly integrate into style)
104     android::hash_t mHash;
105 
computeHash()106     android::hash_t computeHash() const {
107         return Hasher()
108                 .update(mId)
109                 .update(mStart)
110                 .update(mCount)
111                 .update(mStyle.identifier())
112                 .update(mSize)
113                 .update(mScaleX)
114                 .update(mSkewX)
115                 .update(mLetterSpacing)
116                 .update(mWordSpacing)
117                 .update(mFontFlags)
118                 .update(mLocaleListId)
119                 .update(static_cast<uint8_t>(mFamilyVariant))
120                 .update(packHyphenEdit(mStartHyphen, mEndHyphen))
121                 .update(mIsRtl)
122                 .updateShorts(mChars, mNchars)
123                 .hash();
124     }
125 };
126 
127 class LayoutCache : private android::OnEntryRemoved<LayoutCacheKey, LayoutPiece*> {
128 public:
clear()129     void clear() {
130         std::lock_guard<std::mutex> lock(mMutex);
131         mCache.clear();
132     }
133 
134     // Do not use LayoutCache inside the callback function, otherwise dead-lock may happen.
135     template <typename F>
getOrCreate(const U16StringPiece & text,const Range & range,const MinikinPaint & paint,bool dir,StartHyphenEdit startHyphen,EndHyphenEdit endHyphen,F & f)136     void getOrCreate(const U16StringPiece& text, const Range& range, const MinikinPaint& paint,
137                      bool dir, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen, F& f) {
138         LayoutCacheKey key(text, range, paint, dir, startHyphen, endHyphen);
139         if (paint.skipCache() || range.getLength() >= LENGTH_LIMIT_CACHE) {
140             f(LayoutPiece(text, range, dir, paint, startHyphen, endHyphen), paint);
141             return;
142         }
143         {
144             std::lock_guard<std::mutex> lock(mMutex);
145             LayoutPiece* layout = mCache.get(key);
146             if (layout != nullptr) {
147                 f(*layout, paint);
148                 return;
149             }
150         }
151         // Doing text layout takes long time, so releases the mutex during doing layout.
152         // Don't care even if we do the same layout in other thred.
153         key.copyText();
154         std::unique_ptr<LayoutPiece> layout =
155                 std::make_unique<LayoutPiece>(text, range, dir, paint, startHyphen, endHyphen);
156         f(*layout, paint);
157         {
158             std::lock_guard<std::mutex> lock(mMutex);
159             mCache.put(key, layout.release());
160         }
161     }
162 
getInstance()163     static LayoutCache& getInstance() {
164         static LayoutCache cache(kMaxEntries);
165         return cache;
166     }
167 
168 protected:
LayoutCache(uint32_t maxEntries)169     LayoutCache(uint32_t maxEntries) : mCache(maxEntries) {
170         mCache.setOnEntryRemovedListener(this);
171     }
172 
getCacheSize()173     uint32_t getCacheSize() {
174         std::lock_guard<std::mutex> lock(mMutex);
175         return mCache.size();
176     }
177 
178 private:
179     // callback for OnEntryRemoved
operator()180     void operator()(LayoutCacheKey& key, LayoutPiece*& value) {
181         key.freeText();
182         delete value;
183     }
184 
185     android::LruCache<LayoutCacheKey, LayoutPiece*> mCache GUARDED_BY(mMutex);
186 
187     // static const size_t kMaxEntries = LruCache<LayoutCacheKey, Layout*>::kUnlimitedCapacity;
188 
189     // TODO: eviction based on memory footprint; for now, we just use a constant
190     // number of strings
191     static const size_t kMaxEntries = 5000;
192 
193     std::mutex mMutex;
194 };
195 
hash_type(const LayoutCacheKey & key)196 inline android::hash_t hash_type(const LayoutCacheKey& key) {
197     return key.hash();
198 }
199 
200 }  // namespace minikin
201 #endif  // MINIKIN_LAYOUT_CACHE_H
202