diff --git a/include/minikin/FontCollection.h b/include/minikin/FontCollection.h index a3396277ecbcd242cf423a65f30cd95f70fc62fa..d5fc5a11bd2c10fa38006bd94c29b3562b07bfd6 100644 --- a/include/minikin/FontCollection.h +++ b/include/minikin/FontCollection.h @@ -26,8 +26,6 @@ namespace minikin { -class FontLanguages; - class FontCollection { public: explicit FontCollection(const std::vector>& typefaces); @@ -75,16 +73,15 @@ private: void init(const std::vector>& typefaces); const std::shared_ptr& getFamilyForChar(uint32_t ch, uint32_t vs, - const FontLanguages& styleLanguages, int variant) const; + uint32_t langListId, int variant) const; - uint32_t calcFamilyScore(uint32_t ch, uint32_t vs, int variant, - const FontLanguages& styleLanguages, + uint32_t calcFamilyScore(uint32_t ch, uint32_t vs, int variant, uint32_t langListId, const std::shared_ptr& fontFamily) const; uint32_t calcCoverageScore(uint32_t ch, uint32_t vs, const std::shared_ptr& fontFamily) const; - static uint32_t calcLanguageMatchingScore(const FontLanguages& styleLanguages, + static uint32_t calcLanguageMatchingScore(uint32_t userLangListId, const FontFamily& fontFamily); static uint32_t calcVariantMatchingScore(int variant, const FontFamily& fontFamily); diff --git a/include/minikin/FontFamily.h b/include/minikin/FontFamily.h index a44b9c312ff4922c8171a08c027668b9baf3d0a8..4186c2baabba7020bec47b5ed46a40388cab110c 100644 --- a/include/minikin/FontFamily.h +++ b/include/minikin/FontFamily.h @@ -31,7 +31,6 @@ namespace minikin { class MinikinFont; -class FontLanguages; // FontStyle represents all style information needed to select an actual font // from a collection. The implementation is packed into two 32-bit words @@ -186,7 +185,6 @@ private: int mVariant; std::vector mFonts; std::unordered_set mSupportedAxes; - std::unique_ptr mLanguages; SparseBitSet mCoverage; bool mHasVSTable; diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp index e8c05d7d77f904b49bb582fe4fb69cf6047bd705..a3d2d974e0e9a5cb818c6bc5776e9addc28c319a 100644 --- a/libs/minikin/FontCollection.cpp +++ b/libs/minikin/FontCollection.cpp @@ -91,7 +91,7 @@ FontCollection::FontCollection(const vector>& typefa } void FontCollection::init(const vector>& typefaces) { - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); mId = sNextId++; vector lastChar; size_t nTypefaces = typefaces.size(); @@ -171,8 +171,7 @@ const uint32_t kFirstFontScore = UINT32_MAX; // base character. // - kFirstFontScore: When the font is the first font family in the collection and it supports the // given character or variation sequence. -uint32_t FontCollection::calcFamilyScore(uint32_t ch, uint32_t vs, int variant, - const FontLanguages& styleLanguages, +uint32_t FontCollection::calcFamilyScore(uint32_t ch, uint32_t vs, int variant, uint32_t langListId, const std::shared_ptr& fontFamily) const { const uint32_t coverageScore = calcCoverageScore(ch, vs, fontFamily); @@ -181,7 +180,7 @@ uint32_t FontCollection::calcFamilyScore(uint32_t ch, uint32_t vs, int variant, return coverageScore; } - const uint32_t languageScore = calcLanguageMatchingScore(styleLanguages, *fontFamily); + const uint32_t languageScore = calcLanguageMatchingScore(langListId, *fontFamily); const uint32_t variantScore = calcVariantMatchingScore(variant, *fontFamily); // Subscores are encoded into 31 bits representation to meet the subscore priority. @@ -223,7 +222,7 @@ uint32_t FontCollection::calcCoverageScore(uint32_t ch, uint32_t vs, } if (vs == EMOJI_STYLE_VS || vs == TEXT_STYLE_VS) { - const FontLanguages& langs = getFontLanguagesFromCacheLocked(fontFamily->langId()); + const FontLanguages& langs = FontLanguageListCache::getById(fontFamily->langId()); bool hasEmojiFlag = false; for (size_t i = 0; i < langs.size(); ++i) { if (langs[i].getEmojiStyle() == FontLanguage::EMSTYLE_EMOJI) { @@ -260,13 +259,14 @@ uint32_t FontCollection::calcCoverageScore(uint32_t ch, uint32_t vs, // Here, m is the maximum number of languages to be compared, and s(i) is the i-th language's // matching score. The possible values of s(i) are 0, 1, 2, 3 and 4. uint32_t FontCollection::calcLanguageMatchingScore( - const FontLanguages& styleLanguages, const FontFamily& fontFamily) { - const FontLanguages& fontLanguages = getFontLanguagesFromCacheLocked(fontFamily.langId()); + uint32_t userLangListId, const FontFamily& fontFamily) { + const FontLanguages& langList = FontLanguageListCache::getById(userLangListId); + const FontLanguages& fontLanguages = FontLanguageListCache::getById(fontFamily.langId()); - const size_t maxCompareNum = std::min(styleLanguages.size(), FONT_LANGUAGES_LIMIT); + const size_t maxCompareNum = std::min(langList.size(), FONT_LANGUAGES_LIMIT); uint32_t score = 0; for (size_t i = 0; i < maxCompareNum; ++i) { - score = score * 5u + styleLanguages[i].calcScoreFor(fontLanguages); + score = score * 5u + langList[i].calcScoreFor(fontLanguages); } return score; } @@ -284,7 +284,7 @@ uint32_t FontCollection::calcVariantMatchingScore(int variant, const FontFamily& // 3. Highest score wins, with ties resolved to the first font. // This method never returns nullptr. const std::shared_ptr& FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs, - const FontLanguages& styleLanguages, int variant) const { + uint32_t langListId, int variant) const { if (ch >= mMaxChar) { return mFamilies[0]; } @@ -303,7 +303,7 @@ const std::shared_ptr& FontCollection::getFamilyForChar(uint32_t ch, for (size_t i = range.start; i < range.end; i++) { const std::shared_ptr& family = vs == 0 ? mFamilies[mFamilyVec[i]] : mFamilies[i]; - const uint32_t score = calcFamilyScore(ch, vs, variant, styleLanguages, family); + const uint32_t score = calcFamilyScore(ch, vs, variant, langListId, family); if (score == kFirstFontScore) { // If the first font family supports the given character or variation sequence, always // use it. @@ -323,7 +323,7 @@ const std::shared_ptr& FontCollection::getFamilyForChar(uint32_t ch, if (U_SUCCESS(errorCode) && len > 0) { int off = 0; U16_NEXT_UNSAFE(decomposed, off, ch); - return getFamilyForChar(ch, vs, styleLanguages, variant); + return getFamilyForChar(ch, vs, langListId, variant); } } return mFamilies[0]; @@ -368,7 +368,7 @@ bool FontCollection::hasVariationSelector(uint32_t baseCodepoint, return false; } - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); // Currently mRanges can not be used here since it isn't aware of the variation sequence. for (size_t i = 0; i < mVSFamilyVec.size(); i++) { @@ -381,7 +381,8 @@ bool FontCollection::hasVariationSelector(uint32_t baseCodepoint, // for emoji + U+FE0E case since we have special fallback rule for the sequence. if (isEmojiStyleVSBase(baseCodepoint) && variationSelector == TEXT_STYLE_VS) { for (size_t i = 0; i < mFamilies.size(); ++i) { - if (!mFamilies[i]->isColorEmojiFamily() && mFamilies[i]->hasGlyph(baseCodepoint, 0)) { + if (!mFamilies[i]->isColorEmojiFamily() && variationSelector == TEXT_STYLE_VS && + mFamilies[i]->hasGlyph(baseCodepoint, 0)) { return true; } } @@ -392,8 +393,7 @@ bool FontCollection::hasVariationSelector(uint32_t baseCodepoint, void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style, vector* result) const { - const FontLanguages& styleLanguages = - getFontLanguagesFromCacheLocked(style.getLanguageListId()); + const uint32_t langListId = style.getLanguageListId(); int variant = style.getVariant(); const FontFamily* lastFamily = nullptr; Run* run = NULL; @@ -433,7 +433,7 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty if (!shouldContinueRun) { const std::shared_ptr& family = getFamilyForChar( - ch, isVariationSelector(nextCh) ? nextCh : 0, styleLanguages, variant); + ch, isVariationSelector(nextCh) ? nextCh : 0, langListId, variant); if (utf16Pos == 0 || family.get() != lastFamily) { size_t start = utf16Pos; // Workaround for combining marks and emoji modifiers until we implement diff --git a/libs/minikin/FontFamily.cpp b/libs/minikin/FontFamily.cpp index e434f246893c01074fb4379534784cc2fbc3c294..4097e8fb239c56553edfdcc646673d007e26bd68 100644 --- a/libs/minikin/FontFamily.cpp +++ b/libs/minikin/FontFamily.cpp @@ -41,7 +41,7 @@ using std::vector; namespace minikin { FontStyle::FontStyle(int variant, int weight, bool italic) - : FontStyle(kEmptyLanguageListId, variant, weight, italic) { + : FontStyle(FontLanguageListCache::kEmptyListId, variant, weight, italic) { } FontStyle::FontStyle(uint32_t languageListId, int variant, int weight, bool italic) @@ -56,8 +56,8 @@ android::hash_t FontStyle::hash() const { // static uint32_t FontStyle::registerLanguageList(const std::string& languages) { - ScopedLock _l(gLock); - return putLanguageListToCacheLocked(languages); + android::AutoMutex _l(gMinikinLock); + return FontLanguageListCache::getId(languages); } // static @@ -76,7 +76,7 @@ Font::Font(std::shared_ptr&& typeface, FontStyle style) } void Font::loadAxes() { - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); const uint32_t fvarTag = MinikinFont::MakeTag('f', 'v', 'a', 'r'); HbBlob fvarTable(getFontTable(typeface.get(), fvarTag)); if (fvarTable.size() == 0) { @@ -104,7 +104,7 @@ FontFamily::FontFamily(std::vector&& fonts) : FontFamily(0 /* variant */, } FontFamily::FontFamily(int variant, std::vector&& fonts) - : FontFamily(kEmptyLanguageListId, variant, std::move(fonts)) { + : FontFamily(FontLanguageListCache::kEmptyListId, variant, std::move(fonts)) { } FontFamily::FontFamily(uint32_t langId, int variant, std::vector&& fonts) @@ -118,7 +118,7 @@ FontFamily::FontFamily(std::vector&& fonts, const uint8_t* acceleratorTabl FontFamily::FontFamily(int variant, std::vector&& fonts, const uint8_t* acceleratorTable, size_t tableSize) - : FontFamily(kEmptyLanguageListId, variant, std::move(fonts), acceleratorTable, + : FontFamily(FontLanguageListCache::kEmptyListId, variant, std::move(fonts), acceleratorTable, tableSize) { } @@ -133,7 +133,7 @@ FontFamily::~FontFamily() { bool FontFamily::analyzeStyle(const std::shared_ptr& typeface, int* weight, bool* italic) { - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2'); HbBlob os2Table(getFontTable(typeface.get(), os2Tag)); if (os2Table.get() == nullptr) return false; @@ -178,7 +178,7 @@ FakedFont FontFamily::getClosestMatch(FontStyle style) const { } bool FontFamily::isColorEmojiFamily() const { - const FontLanguages& languageList = getFontLanguagesFromCacheLocked(mLangId); + const FontLanguages& languageList = FontLanguageListCache::getById(mLangId); for (size_t i = 0; i < languageList.size(); ++i) { if (languageList[i].getEmojiStyle() == FontLanguage::EMSTYLE_EMOJI) { return true; @@ -188,7 +188,7 @@ bool FontFamily::isColorEmojiFamily() const { } void FontFamily::computeCoverage() { - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); const FontStyle defaultStyle; const MinikinFont* typeface = getClosestMatch(defaultStyle).font; const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p'); @@ -206,7 +206,7 @@ void FontFamily::computeCoverage() { } bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const { - assertLocked(gLock); + assertMinikinLocked(); if (variationSelector != 0 && !mHasVSTable) { // Early exit if the variation selector is specified but the font doesn't have a cmap format // 14 subtable. diff --git a/libs/minikin/FontLanguageListCache.cpp b/libs/minikin/FontLanguageListCache.cpp index 5323e580cec1be4d1585a8b4d14fbdd9a3586a25..f1e14f0a66d56d8fc21a26fef089bf54081e0935 100644 --- a/libs/minikin/FontLanguageListCache.cpp +++ b/libs/minikin/FontLanguageListCache.cpp @@ -20,8 +20,6 @@ #include #include -#include -#include #include @@ -30,6 +28,8 @@ namespace minikin { +const uint32_t FontLanguageListCache::kEmptyListId; + // Returns the text length of output. static size_t toLanguageTag(char* output, size_t outSize, const std::string& locale) { output[0] = '\0'; @@ -111,61 +111,46 @@ static std::vector parseLanguageList(const std::string& input) { return result; } -class FontLanguageListCache { -public: - FontLanguageListCache() { - // Insert an empty language list for mapping default language list to kEmptyListId. - // The default language list has only one FontLanguage and it is the unsupported language. - mLanguageLists.emplace(kEmptyLanguageListId, FontLanguages()); - mLanguageListLookupTable.emplace("", kEmptyLanguageListId); - } - - uint32_t put(const std::string& languages) { - assertLocked(gLock); - - const auto& it = mLanguageListLookupTable.find(languages); - if (it != mLanguageListLookupTable.end()) { - return it->second; - } - - // Given language list is not in cache. Insert it and return newly assigned ID. - const uint32_t nextId = mLanguageLists.size(); - FontLanguages fontLanguages(parseLanguageList(languages)); - if (fontLanguages.empty()) { - mLanguageListLookupTable.emplace(languages, kEmptyLanguageListId); - return kEmptyLanguageListId; - } - mLanguageLists.emplace(nextId, std::move(fontLanguages)); - mLanguageListLookupTable.emplace(languages, nextId); - return nextId; +// static +uint32_t FontLanguageListCache::getId(const std::string& languages) { + FontLanguageListCache* inst = FontLanguageListCache::getInstance(); + std::unordered_map::const_iterator it = + inst->mLanguageListLookupTable.find(languages); + if (it != inst->mLanguageListLookupTable.end()) { + return it->second; } - const FontLanguages& get(uint32_t id) { - assertLocked(gLock); - - return mLanguageLists[id]; + // Given language list is not in cache. Insert it and return newly assigned ID. + const uint32_t nextId = inst->mLanguageLists.size(); + FontLanguages fontLanguages(parseLanguageList(languages)); + if (fontLanguages.empty()) { + return kEmptyListId; } - -private: - std::unordered_map mLanguageLists; - - // A map from string representation of the font language list to the ID. - std::unordered_map mLanguageListLookupTable; -}; - -static FontLanguageListCache& getInstance() { - static FontLanguageListCache instance; - return instance; + inst->mLanguageLists.push_back(std::move(fontLanguages)); + inst->mLanguageListLookupTable.insert(std::make_pair(languages, nextId)); + return nextId; } // static -uint32_t putLanguageListToCacheLocked(const std::string& languages) { - return getInstance().put(languages); +const FontLanguages& FontLanguageListCache::getById(uint32_t id) { + FontLanguageListCache* inst = FontLanguageListCache::getInstance(); + LOG_ALWAYS_FATAL_IF(id >= inst->mLanguageLists.size(), "Lookup by unknown language list ID."); + return inst->mLanguageLists[id]; } // static -const FontLanguages& getFontLanguagesFromCacheLocked(uint32_t id) { - return getInstance().get(id); +FontLanguageListCache* FontLanguageListCache::getInstance() { + assertMinikinLocked(); + static FontLanguageListCache* instance = nullptr; + if (instance == nullptr) { + instance = new FontLanguageListCache(); + + // Insert an empty language list for mapping default language list to kEmptyListId. + // The default language list has only one FontLanguage and it is the unsupported language. + instance->mLanguageLists.push_back(FontLanguages()); + instance->mLanguageListLookupTable.insert(std::make_pair("", kEmptyListId)); + } + return instance; } } // namespace minikin diff --git a/libs/minikin/FontLanguageListCache.h b/libs/minikin/FontLanguageListCache.h index 2eafab915180c0c70c49cd6d80a17cf67b5bc8c1..9bf156f9de7e969bf8232a72ae896658b0ccfa5d 100644 --- a/libs/minikin/FontLanguageListCache.h +++ b/libs/minikin/FontLanguageListCache.h @@ -17,21 +17,39 @@ #ifndef MINIKIN_FONT_LANGUAGE_LIST_CACHE_H #define MINIKIN_FONT_LANGUAGE_LIST_CACHE_H +#include + +#include #include "FontLanguage.h" namespace minikin { -// A special ID for the empty language list. -// This value must be 0 since the empty language list is inserted into mLanguageLists by default. -const uint32_t kEmptyLanguageListId = 0; +class FontLanguageListCache { +public: + // A special ID for the empty language list. + // This value must be 0 since the empty language list is inserted into mLanguageLists by + // default. + const static uint32_t kEmptyListId = 0; + + // Returns language list ID for the given string representation of FontLanguages. + // Caller should acquire a lock before calling the method. + static uint32_t getId(const std::string& languages); + + // Caller should acquire a lock before calling the method. + static const FontLanguages& getById(uint32_t id); + +private: + FontLanguageListCache() {} // Singleton + ~FontLanguageListCache() {} + + // Caller should acquire a lock before calling the method. + static FontLanguageListCache* getInstance(); -// Looks up from internal cache and returns associated ID if FontLanguages constructed from given -// string is already registered. If it is new to internal cache, put it to internal cache and -// returns newly assigned ID. -uint32_t putLanguageListToCacheLocked(const std::string& languages); + std::vector mLanguageLists; -// Returns FontLanguages associated with given ID. -const FontLanguages& getFontLanguagesFromCacheLocked(uint32_t id); + // A map from string representation of the font language list to the ID. + std::unordered_map mLanguageListLookupTable; +}; } // namespace minikin diff --git a/libs/minikin/HbFontCache.cpp b/libs/minikin/HbFontCache.cpp index 79aebe931e5c58e61b90746659117919fb7ff2e4..af3d783bc84825ead218325e0f41a65d2fb3b481 100644 --- a/libs/minikin/HbFontCache.cpp +++ b/libs/minikin/HbFontCache.cpp @@ -62,80 +62,70 @@ private: android::LruCache mCache; }; -class HbFontCacheHolder { -public: - HbFontCacheHolder() {} - - hb_font_t* getCachedFont(const MinikinFont* minikinFont) { - assertLocked(gLock); - - // TODO: get rid of nullFaceFont - static hb_font_t* nullFaceFont = nullptr; - if (minikinFont == nullptr) { - if (nullFaceFont == nullptr) { - nullFaceFont = hb_font_create(nullptr); - } - return hb_font_reference(nullFaceFont); - } - - const int32_t fontId = minikinFont->GetUniqueId(); - hb_font_t* font = mFontCache.get(fontId); - if (font != nullptr) { - return hb_font_reference(font); - } - - hb_face_t* face; - const void* buf = minikinFont->GetFontData(); - size_t size = minikinFont->GetFontSize(); - hb_blob_t* blob = hb_blob_create(reinterpret_cast(buf), size, - HB_MEMORY_MODE_READONLY, nullptr, nullptr); - face = hb_face_create(blob, minikinFont->GetFontIndex()); - hb_blob_destroy(blob); - - hb_font_t* parent_font = hb_font_create(face); - hb_ot_font_set_funcs(parent_font); - - unsigned int upem = hb_face_get_upem(face); - hb_font_set_scale(parent_font, upem, upem); - - font = hb_font_create_sub_font(parent_font); - hb_font_destroy(parent_font); - hb_face_destroy(face); - mFontCache.put(fontId, font); - return hb_font_reference(font); - } - - void clearFontCache() { - assertLocked(gLock); - mFontCache.clear(); +HbFontCache* getFontCacheLocked() { + assertMinikinLocked(); + static HbFontCache* cache = nullptr; + if (cache == nullptr) { + cache = new HbFontCache(); } - - void removeFont(const MinikinFont* minikinFont) { - assertLocked(gLock); - mFontCache.remove(minikinFont->GetUniqueId()); - } - -private: - HbFontCache mFontCache; -}; - -static HbFontCacheHolder& getInstance() { - static HbFontCacheHolder instance; - return instance; + return cache; } void purgeHbFontCacheLocked() { - getInstance().clearFontCache(); + assertMinikinLocked(); + getFontCacheLocked()->clear(); } void purgeHbFontLocked(const MinikinFont* minikinFont) { - getInstance().removeFont(minikinFont); + assertMinikinLocked(); + const int32_t fontId = minikinFont->GetUniqueId(); + getFontCacheLocked()->remove(fontId); } // Returns a new reference to a hb_font_t object, caller is // responsible for calling hb_font_destroy() on it. hb_font_t* getHbFontLocked(const MinikinFont* minikinFont) { - return getInstance().getCachedFont(minikinFont); + assertMinikinLocked(); + // TODO: get rid of nullFaceFont + static hb_font_t* nullFaceFont = nullptr; + if (minikinFont == nullptr) { + if (nullFaceFont == nullptr) { + nullFaceFont = hb_font_create(nullptr); + } + return hb_font_reference(nullFaceFont); + } + + HbFontCache* fontCache = getFontCacheLocked(); + const int32_t fontId = minikinFont->GetUniqueId(); + hb_font_t* font = fontCache->get(fontId); + if (font != nullptr) { + return hb_font_reference(font); + } + + hb_face_t* face; + const void* buf = minikinFont->GetFontData(); + size_t size = minikinFont->GetFontSize(); + hb_blob_t* blob = hb_blob_create(reinterpret_cast(buf), size, + HB_MEMORY_MODE_READONLY, nullptr, nullptr); + face = hb_face_create(blob, minikinFont->GetFontIndex()); + hb_blob_destroy(blob); + + hb_font_t* parent_font = hb_font_create(face); + hb_ot_font_set_funcs(parent_font); + + unsigned int upem = hb_face_get_upem(face); + hb_font_set_scale(parent_font, upem, upem); + + font = hb_font_create_sub_font(parent_font); + std::vector variations; + for (const FontVariation& variation : minikinFont->GetAxes()) { + variations.push_back({variation.axisTag, variation.value}); + } + hb_font_set_variations(font, variations.data(), variations.size()); + hb_font_destroy(parent_font); + hb_face_destroy(face); + fontCache->put(fontId, font); + return hb_font_reference(font); } } // namespace minikin diff --git a/libs/minikin/Layout.cpp b/libs/minikin/Layout.cpp index d85ddd79a35f4af9d1d8ffe9a34efb253c0da4cb..743cb763aeaf338b7835556348fba6692edb13ee 100644 --- a/libs/minikin/Layout.cpp +++ b/libs/minikin/Layout.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -200,7 +201,7 @@ static unsigned int disabledDecomposeCompatibility(hb_unicode_funcs_t*, hb_codep return 0; } -class LayoutEngine { +class LayoutEngine : public ::android::Singleton { public: LayoutEngine() { unicodeFunctions = hb_unicode_funcs_create(hb_icu_get_unicode_funcs()); @@ -211,26 +212,8 @@ public: hb_buffer_set_unicode_funcs(hbBuffer, unicodeFunctions); } - Layout* getCachedLayout(LayoutCacheKey& key, LayoutContext* ctx, - const std::shared_ptr& collection) { - assertLocked(gLock); - return layoutCache.get(key, ctx, collection); - } - - void clearLayoutCache() { - assertLocked(gLock); - layoutCache.clear(); - } - hb_buffer_t* hbBuffer; hb_unicode_funcs_t* unicodeFunctions; - - static LayoutEngine& getInstance() { - static LayoutEngine instance; - return instance; - } - -private: LayoutCache layoutCache; }; @@ -305,7 +288,8 @@ static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* /* hbFont */, void* } hb_font_funcs_t* getHbFontFuncs(bool forColorBitmapFont) { - assertLocked(gLock); + assertMinikinLocked(); + static hb_font_funcs_t* hbFuncs = nullptr; static hb_font_funcs_t* hbFuncsForColorBitmap = nullptr; @@ -601,7 +585,8 @@ BidiText::BidiText(const uint16_t* buf, size_t start, size_t count, size_t bufSi void Layout::doLayout(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags, const FontStyle &style, const MinikinPaint &paint) { - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); + LayoutContext ctx; ctx.style = style; ctx.paint = paint; @@ -619,7 +604,8 @@ void Layout::doLayout(const uint16_t* buf, size_t start, size_t count, size_t bu float Layout::measureText(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags, const FontStyle &style, const MinikinPaint &paint, const std::shared_ptr& collection, float* advances) { - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); + LayoutContext ctx; ctx.style = style; ctx.paint = paint; @@ -691,6 +677,7 @@ float Layout::doLayoutRunCached(const uint16_t* buf, size_t start, size_t count, float Layout::doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize, bool isRtl, LayoutContext* ctx, size_t bufStart, const std::shared_ptr& collection, Layout* layout, float* advances) { + LayoutCache& cache = LayoutEngine::getInstance().layoutCache; LayoutCacheKey key(collection, ctx->paint, ctx->style, buf, start, count, bufSize, isRtl); float wordSpacing = count == 1 && isWordSpace(buf[start]) ? ctx->paint.wordSpacing : 0; @@ -707,7 +694,7 @@ float Layout::doLayoutWord(const uint16_t* buf, size_t start, size_t count, size } advance = layoutForWord.getAdvance(); } else { - Layout* layoutForWord = LayoutEngine::getInstance().getCachedLayout(key, ctx, collection); + Layout* layoutForWord = cache.get(key, ctx, collection); if (layout) { layout->appendLayout(layoutForWord, bufStart, wordSpacing); } @@ -960,7 +947,7 @@ void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t hb_buffer_set_script(buffer, script); hb_buffer_set_direction(buffer, isRtl? HB_DIRECTION_RTL : HB_DIRECTION_LTR); const FontLanguages& langList = - getFontLanguagesFromCacheLocked(ctx->style.getLanguageListId()); + FontLanguageListCache::getById(ctx->style.getLanguageListId()); if (langList.size() != 0) { const FontLanguage* hbLanguage = &langList[0]; for (size_t i = 0; i < langList.size(); ++i) { @@ -1162,9 +1149,17 @@ void Layout::getBounds(MinikinRect* bounds) const { } void Layout::purgeCaches() { - ScopedLock _l(gLock); - LayoutEngine::getInstance().clearLayoutCache(); + android::AutoMutex _l(gMinikinLock); + LayoutCache& layoutCache = LayoutEngine::getInstance().layoutCache; + layoutCache.clear(); purgeHbFontCacheLocked(); } } // namespace minikin + +// Unable to define the static data member outside of android. +// TODO: introduce our own Singleton to drop android namespace. +namespace android { +ANDROID_SINGLETON_STATIC_INSTANCE(minikin::LayoutEngine); +} // namespace android + diff --git a/libs/minikin/MinikinFont.cpp b/libs/minikin/MinikinFont.cpp index f8e0d5b29002aacefa3d9147011db98faa931380..6bf6a4ae3b72820b8d23985ef33cf095c04b9c1b 100644 --- a/libs/minikin/MinikinFont.cpp +++ b/libs/minikin/MinikinFont.cpp @@ -21,7 +21,7 @@ namespace minikin { MinikinFont::~MinikinFont() { - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); purgeHbFontLocked(this); } diff --git a/libs/minikin/MinikinInternal.cpp b/libs/minikin/MinikinInternal.cpp index 1d06e03aa68057015ab9226e78aee23ca0e934b1..e766dce992dc980291f73833784243ede0a39c53 100644 --- a/libs/minikin/MinikinInternal.cpp +++ b/libs/minikin/MinikinInternal.cpp @@ -25,8 +25,13 @@ namespace minikin { -std::mutex gMutex; -std::unique_lock gLock(gMutex, std::defer_lock); +android::Mutex gMinikinLock; + +void assertMinikinLocked() { +#ifdef ENABLE_RACE_DETECTION + LOG_ALWAYS_FATAL_IF(gMinikinLock.tryLock() == 0); +#endif +} bool isEmoji(uint32_t c) { // U+2695 U+2640 U+2642 are not in emoji category in Unicode 9 but they are now emoji category. @@ -81,7 +86,7 @@ bool isEmojiBase(uint32_t c) { } hb_blob_t* getFontTable(const MinikinFont* minikinFont, uint32_t tag) { - assertLocked(gLock); + assertMinikinLocked(); hb_font_t* font = getHbFontLocked(minikinFont); hb_face_t* face = hb_font_get_face(font); hb_blob_t* blob = hb_face_reference_table(face, tag); diff --git a/libs/minikin/MinikinInternal.h b/libs/minikin/MinikinInternal.h index c374cb4656ec8de5ef216189dd96db82f638fcbf..365f20cc0df43b55d3abc8b0bad9300cac4c3fad 100644 --- a/libs/minikin/MinikinInternal.h +++ b/libs/minikin/MinikinInternal.h @@ -21,27 +21,20 @@ #include -#include -#include +#include -#include +#include namespace minikin { // All external Minikin interfaces are designed to be thread-safe. -// Presently, that's implemented by a global lock, and having all external interfaces take that -// lock. -extern std::unique_lock gLock; -typedef std::unique_lock> ScopedLock; - -#ifdef ENABLE_RACE_DETECTION -inline void assertLocked(const std::unique_lock& lock) { - LOG_ALWAYS_FATAL_IF(!lock.owns_lock()); -} -#else -inline void assertLocked(const std::unique_lock&) {} -#endif +// Presently, that's implemented by through a global lock, and having +// all external interfaces take that lock. + +extern android::Mutex gMinikinLock; +// Aborts if gMinikinLock is not acquired. Do nothing on the release build. +void assertMinikinLocked(); // Returns true if c is emoji. bool isEmoji(uint32_t c); diff --git a/tests/perftests/FontCollection.cpp b/tests/perftests/FontCollection.cpp index 304708f6c771c0864b7fab56f8a09d725c5916cd..6f9d636ca306cad1b4182a11f9a9c4985fcc6eef 100644 --- a/tests/perftests/FontCollection.cpp +++ b/tests/perftests/FontCollection.cpp @@ -77,7 +77,7 @@ static void BM_FontCollection_itemize(benchmark::State& state) { std::vector result; FontStyle style(FontStyle::registerLanguageList(ITEMIZE_TEST_CASES[testIndex].languageTag)); - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); while (state.KeepRunning()) { result.clear(); collection->itemize(buffer, utf16_length, style, &result); diff --git a/tests/unittest/FontCollectionItemizeTest.cpp b/tests/unittest/FontCollectionItemizeTest.cpp index 557458b80bd1d5e83722aa4dee46d2822b373c32..aa142cccdcd4a6ba58158e63a69826b6e8566bb4 100644 --- a/tests/unittest/FontCollectionItemizeTest.cpp +++ b/tests/unittest/FontCollectionItemizeTest.cpp @@ -60,7 +60,7 @@ void itemize(const std::shared_ptr& collection, const char* str, result->clear(); ParseUnicode(buf, BUF_SIZE, str, &len, NULL); - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); collection->itemize(buf, len, style, result); } @@ -72,8 +72,8 @@ const std::string& getFontPath(const FontCollection::Run& run) { // Utility function to obtain FontLanguages from string. const FontLanguages& registerAndGetFontLanguages(const std::string& lang_string) { - ScopedLock _l(gLock); - return getFontLanguagesFromCacheLocked(putLanguageListToCacheLocked(lang_string)); + android::AutoMutex _l(gMinikinLock); + return FontLanguageListCache::getById(FontLanguageListCache::getId(lang_string)); } TEST_F(FontCollectionItemizeTest, itemize_latin) { diff --git a/tests/unittest/FontFamilyTest.cpp b/tests/unittest/FontFamilyTest.cpp index 81f595694caf6a0d414e137d98bf47cca6fbf457..5285f2642b471c4d53f1edfd20f39e4f4068906f 100644 --- a/tests/unittest/FontFamilyTest.cpp +++ b/tests/unittest/FontFamilyTest.cpp @@ -30,15 +30,15 @@ typedef ICUTestBase FontLanguagesTest; typedef ICUTestBase FontLanguageTest; static const FontLanguages& createFontLanguages(const std::string& input) { - ScopedLock _l(gLock); - uint32_t langId = putLanguageListToCacheLocked(input); - return getFontLanguagesFromCacheLocked(langId); + android::AutoMutex _l(gMinikinLock); + uint32_t langId = FontLanguageListCache::getId(input); + return FontLanguageListCache::getById(langId); } static FontLanguage createFontLanguage(const std::string& input) { - ScopedLock _l(gLock); - uint32_t langId = putLanguageListToCacheLocked(input); - return getFontLanguagesFromCacheLocked(langId)[0]; + android::AutoMutex _l(gMinikinLock); + uint32_t langId = FontLanguageListCache::getId(input); + return FontLanguageListCache::getById(langId)[0]; } static FontLanguage createFontLanguageWithoutICUSanitization(const std::string& input) { @@ -533,7 +533,7 @@ TEST_F(FontFamilyTest, hasVariationSelectorTest) { std::shared_ptr family( new FontFamily(std::vector{ Font(minikinFont, FontStyle()) })); - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); const uint32_t kVS1 = 0xFE00; const uint32_t kVS2 = 0xFE01; @@ -586,7 +586,7 @@ TEST_F(FontFamilyTest, hasVSTableTest) { new MinikinFontForTest(testCase.fontPath)); std::shared_ptr family(new FontFamily( std::vector{ Font(minikinFont, FontStyle()) })); - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); EXPECT_EQ(testCase.hasVSTable, family->hasVSTable()); } } diff --git a/tests/unittest/FontLanguageListCacheTest.cpp b/tests/unittest/FontLanguageListCacheTest.cpp index bd82e0f994766aaca8e7054dab68e8c5e23e2c23..81d84a8a288b797cecd46f51daaa0d4d56120d3d 100644 --- a/tests/unittest/FontLanguageListCacheTest.cpp +++ b/tests/unittest/FontLanguageListCacheTest.cpp @@ -31,43 +31,40 @@ TEST_F(FontLanguageListCacheTest, getId) { EXPECT_NE(0UL, FontStyle::registerLanguageList("jp")); EXPECT_NE(0UL, FontStyle::registerLanguageList("en,zh-Hans")); - ScopedLock _l(gLock); - - EXPECT_EQ(0UL, putLanguageListToCacheLocked("")); - - EXPECT_EQ(putLanguageListToCacheLocked("en"), putLanguageListToCacheLocked("en")); - EXPECT_NE(putLanguageListToCacheLocked("en"), putLanguageListToCacheLocked("jp")); - - EXPECT_EQ(putLanguageListToCacheLocked("en,zh-Hans"), - putLanguageListToCacheLocked("en,zh-Hans")); - EXPECT_NE(putLanguageListToCacheLocked("en,zh-Hans"), - putLanguageListToCacheLocked("zh-Hans,en")); - EXPECT_NE(putLanguageListToCacheLocked("en,zh-Hans"), - putLanguageListToCacheLocked("jp")); - EXPECT_NE(putLanguageListToCacheLocked("en,zh-Hans"), - putLanguageListToCacheLocked("en")); - EXPECT_NE(putLanguageListToCacheLocked("en,zh-Hans"), - putLanguageListToCacheLocked("en,zh-Hant")); + android::AutoMutex _l(gMinikinLock); + EXPECT_EQ(0UL, FontLanguageListCache::getId("")); + + EXPECT_EQ(FontLanguageListCache::getId("en"), FontLanguageListCache::getId("en")); + EXPECT_NE(FontLanguageListCache::getId("en"), FontLanguageListCache::getId("jp")); + + EXPECT_EQ(FontLanguageListCache::getId("en,zh-Hans"), + FontLanguageListCache::getId("en,zh-Hans")); + EXPECT_NE(FontLanguageListCache::getId("en,zh-Hans"), + FontLanguageListCache::getId("zh-Hans,en")); + EXPECT_NE(FontLanguageListCache::getId("en,zh-Hans"), + FontLanguageListCache::getId("jp")); + EXPECT_NE(FontLanguageListCache::getId("en,zh-Hans"), + FontLanguageListCache::getId("en")); + EXPECT_NE(FontLanguageListCache::getId("en,zh-Hans"), + FontLanguageListCache::getId("en,zh-Hant")); } TEST_F(FontLanguageListCacheTest, getById) { - ScopedLock _l(gLock); - - uint32_t enLangId = putLanguageListToCacheLocked("en"); - uint32_t jpLangId = putLanguageListToCacheLocked("jp"); - FontLanguage english = getFontLanguagesFromCacheLocked(enLangId)[0]; - FontLanguage japanese = getFontLanguagesFromCacheLocked(jpLangId)[0]; + android::AutoMutex _l(gMinikinLock); + uint32_t enLangId = FontLanguageListCache::getId("en"); + uint32_t jpLangId = FontLanguageListCache::getId("jp"); + FontLanguage english = FontLanguageListCache::getById(enLangId)[0]; + FontLanguage japanese = FontLanguageListCache::getById(jpLangId)[0]; - const FontLanguages& defLangs = getFontLanguagesFromCacheLocked(0); + const FontLanguages& defLangs = FontLanguageListCache::getById(0); EXPECT_TRUE(defLangs.empty()); - const FontLanguages& langs = getFontLanguagesFromCacheLocked( - putLanguageListToCacheLocked("en")); + const FontLanguages& langs = FontLanguageListCache::getById(FontLanguageListCache::getId("en")); ASSERT_EQ(1UL, langs.size()); EXPECT_EQ(english, langs[0]); - const FontLanguages& langs2 = getFontLanguagesFromCacheLocked( - putLanguageListToCacheLocked("en,jp")); + const FontLanguages& langs2 = + FontLanguageListCache::getById(FontLanguageListCache::getId("en,jp")); ASSERT_EQ(2UL, langs2.size()); EXPECT_EQ(english, langs2[0]); EXPECT_EQ(japanese, langs2[1]); diff --git a/tests/unittest/HbFontCacheTest.cpp b/tests/unittest/HbFontCacheTest.cpp index 7691b8421e988166571de1b4820d16963a945a7e..a5581a27b885843dfab2347d4106c535f59628e8 100644 --- a/tests/unittest/HbFontCacheTest.cpp +++ b/tests/unittest/HbFontCacheTest.cpp @@ -18,6 +18,7 @@ #include #include +#include #include @@ -32,7 +33,7 @@ namespace minikin { class HbFontCacheTest : public testing::Test { public: virtual void TearDown() { - ScopedLock _l(gLock); + android::AutoMutex _l(gMinikinLock); purgeHbFontCacheLocked(); } }; @@ -47,8 +48,7 @@ TEST_F(HbFontCacheTest, getHbFontLockedTest) { std::shared_ptr fontC( new MinikinFontForTest(kTestFontDir "BoldItalic.ttf")); - ScopedLock _l(gLock); - + android::AutoMutex _l(gMinikinLock); // Never return NULL. EXPECT_NE(nullptr, getHbFontLocked(fontA.get())); EXPECT_NE(nullptr, getHbFontLocked(fontB.get())); @@ -70,8 +70,7 @@ TEST_F(HbFontCacheTest, purgeCacheTest) { std::shared_ptr minikinFont( new MinikinFontForTest(kTestFontDir "Regular.ttf")); - ScopedLock _l(gLock); - + android::AutoMutex _l(gMinikinLock); hb_font_t* font = getHbFontLocked(minikinFont.get()); ASSERT_NE(nullptr, font);