| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef MINIKIN_LAYOUT_CACHE_H |
| #define MINIKIN_LAYOUT_CACHE_H |
| |
| #include "minikin/LayoutCore.h" |
| |
| #include <mutex> |
| |
| #include <utils/LruCache.h> |
| |
| #include "minikin/FontCollection.h" |
| #include "minikin/Hasher.h" |
| #include "minikin/MinikinPaint.h" |
| |
| #ifdef _WIN32 |
| #include <io.h> |
| #endif |
| |
| namespace minikin { |
| const uint32_t LENGTH_LIMIT_CACHE = 128; |
| // Layout cache datatypes |
| class LayoutCacheKey { |
| public: |
| LayoutCacheKey(const U16StringPiece& text, const Range& range, const MinikinPaint& paint, |
| bool dir, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen) |
| : mChars(text.data()), |
| mNchars(text.size()), |
| mStart(range.getStart()), |
| mCount(range.getLength()), |
| mId(paint.font->getId()), |
| mStyle(paint.fontStyle), |
| mSize(paint.size), |
| mScaleX(paint.scaleX), |
| mSkewX(paint.skewX), |
| mLetterSpacing(paint.letterSpacing), |
| mWordSpacing(paint.wordSpacing), |
| mFontFlags(paint.fontFlags), |
| mLocaleListId(paint.localeListId), |
| mFamilyVariant(paint.familyVariant), |
| mStartHyphen(startHyphen), |
| mEndHyphen(endHyphen), |
| mIsRtl(dir), |
| mHash(computeHash()) {} |
| |
| bool operator==(const LayoutCacheKey& o) const { |
| return mId == o.mId && mStart == o.mStart && mCount == o.mCount && mStyle == o.mStyle && |
| mSize == o.mSize && mScaleX == o.mScaleX && mSkewX == o.mSkewX && |
| mLetterSpacing == o.mLetterSpacing && mWordSpacing == o.mWordSpacing && |
| mFontFlags == o.mFontFlags && mLocaleListId == o.mLocaleListId && |
| mFamilyVariant == o.mFamilyVariant && mStartHyphen == o.mStartHyphen && |
| mEndHyphen == o.mEndHyphen && mIsRtl == o.mIsRtl && mNchars == o.mNchars && |
| !memcmp(mChars, o.mChars, mNchars * sizeof(uint16_t)); |
| } |
| |
| android::hash_t hash() const { return mHash; } |
| |
| void copyText() { |
| uint16_t* charsCopy = new uint16_t[mNchars]; |
| memcpy(charsCopy, mChars, mNchars * sizeof(uint16_t)); |
| mChars = charsCopy; |
| } |
| void freeText() { |
| delete[] mChars; |
| mChars = NULL; |
| } |
| |
| uint32_t getMemoryUsage() const { return sizeof(LayoutCacheKey) + sizeof(uint16_t) * mNchars; } |
| |
| private: |
| const uint16_t* mChars; |
| size_t mNchars; |
| size_t mStart; |
| size_t mCount; |
| uint32_t mId; // for the font collection |
| FontStyle mStyle; |
| float mSize; |
| float mScaleX; |
| float mSkewX; |
| float mLetterSpacing; |
| float mWordSpacing; |
| int32_t mFontFlags; |
| uint32_t mLocaleListId; |
| FamilyVariant mFamilyVariant; |
| StartHyphenEdit mStartHyphen; |
| EndHyphenEdit mEndHyphen; |
| bool mIsRtl; |
| // Note: any fields added to MinikinPaint must also be reflected here. |
| // TODO: language matching (possibly integrate into style) |
| android::hash_t mHash; |
| |
| android::hash_t computeHash() const { |
| return Hasher() |
| .update(mId) |
| .update(mStart) |
| .update(mCount) |
| .update(mStyle.identifier()) |
| .update(mSize) |
| .update(mScaleX) |
| .update(mSkewX) |
| .update(mLetterSpacing) |
| .update(mWordSpacing) |
| .update(mFontFlags) |
| .update(mLocaleListId) |
| .update(static_cast<uint8_t>(mFamilyVariant)) |
| .update(packHyphenEdit(mStartHyphen, mEndHyphen)) |
| .update(mIsRtl) |
| .updateShorts(mChars, mNchars) |
| .hash(); |
| } |
| }; |
| |
| class LayoutCache : private android::OnEntryRemoved<LayoutCacheKey, LayoutPiece*> { |
| public: |
| void clear() { |
| std::lock_guard<std::mutex> lock(mMutex); |
| mCache.clear(); |
| } |
| |
| // Do not use LayoutCache inside the callback function, otherwise dead-lock may happen. |
| template <typename F> |
| void getOrCreate(const U16StringPiece& text, const Range& range, const MinikinPaint& paint, |
| bool dir, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen, F& f) { |
| LayoutCacheKey key(text, range, paint, dir, startHyphen, endHyphen); |
| if (paint.skipCache() || range.getLength() >= LENGTH_LIMIT_CACHE) { |
| f(LayoutPiece(text, range, dir, paint, startHyphen, endHyphen), paint); |
| return; |
| } |
| { |
| std::lock_guard<std::mutex> lock(mMutex); |
| LayoutPiece* layout = mCache.get(key); |
| if (layout != nullptr) { |
| f(*layout, paint); |
| return; |
| } |
| } |
| // Doing text layout takes long time, so releases the mutex during doing layout. |
| // Don't care even if we do the same layout in other thred. |
| key.copyText(); |
| std::unique_ptr<LayoutPiece> layout = |
| std::make_unique<LayoutPiece>(text, range, dir, paint, startHyphen, endHyphen); |
| f(*layout, paint); |
| { |
| std::lock_guard<std::mutex> lock(mMutex); |
| mCache.put(key, layout.release()); |
| } |
| } |
| |
| static LayoutCache& getInstance() { |
| static LayoutCache cache(kMaxEntries); |
| return cache; |
| } |
| |
| protected: |
| LayoutCache(uint32_t maxEntries) : mCache(maxEntries) { |
| mCache.setOnEntryRemovedListener(this); |
| } |
| |
| uint32_t getCacheSize() { |
| std::lock_guard<std::mutex> lock(mMutex); |
| return mCache.size(); |
| } |
| |
| private: |
| // callback for OnEntryRemoved |
| void operator()(LayoutCacheKey& key, LayoutPiece*& value) { |
| key.freeText(); |
| delete value; |
| } |
| |
| android::LruCache<LayoutCacheKey, LayoutPiece*> mCache GUARDED_BY(mMutex); |
| |
| // static const size_t kMaxEntries = LruCache<LayoutCacheKey, Layout*>::kUnlimitedCapacity; |
| |
| // TODO: eviction based on memory footprint; for now, we just use a constant |
| // number of strings |
| static const size_t kMaxEntries = 5000; |
| |
| std::mutex mMutex; |
| }; |
| |
| inline android::hash_t hash_type(const LayoutCacheKey& key) { |
| return key.hash(); |
| } |
| |
| } // namespace minikin |
| #endif // MINIKIN_LAYOUT_CACHE_H |