Move glyph cache manager into font-client-plugin-cache-handler 02/283802/5
authorEunki, Hong <eunkiki.hong@samsung.com>
Thu, 3 Nov 2022 05:50:30 +0000 (14:50 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Thu, 3 Nov 2022 10:04:41 +0000 (19:04 +0900)
Currently, glyph cache manager owned by each font-face-cache-item.
That mean, we can hold glyph cache as
{The number of font type} x {The number of font size} x 128(default).

This patch make we hold GlyphCacheManager hold as singletone.
So we can cache the glyph maximum 128(default) not relative with
the number of font type & size.

Change-Id: I85e3cec3d160a57496e839f0b87a24655861f981
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.cpp
dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.h
dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp
dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp
dali/internal/text/text-abstraction/plugin/font-face-cache-item.h
dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.cpp
dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h
dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp
dali/internal/text/text-abstraction/plugin/lru-cache-container.h

index acaec1d..61e7a47 100644 (file)
@@ -23,6 +23,7 @@
 #include <fontconfig/fontconfig.h>
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
 #include <dali/devel-api/adaptor-framework/image-loading.h>
 #include <dali/internal/text/text-abstraction/font-client-impl.h>
 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
@@ -61,6 +62,28 @@ extern Dali::Integration::Log::Filter* gFontClientLogFilter;
 
 namespace
 {
+/**
+ * @brief Maximum size of glyph cache per each font face.
+ */
+constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX         = 128;
+constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u;
+
+constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
+
+/**
+ * @brief Get maximum size of glyph cache size from environment.
+ * If not settuped, default as 128.
+ * @note This value fixed when we call it first time.
+ * @return The max size of glyph cache.
+ */
+inline size_t GetMaxNumberOfGlyphCache()
+{
+  using Dali::EnvironmentVariable::GetEnvironmentVariable;
+  static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV);
+  static auto number       = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX;
+  return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number;
+}
+
 } // namespace
 
 namespace Dali::TextAbstraction::Internal
@@ -249,6 +272,7 @@ FontClient::Plugin::CacheHandler::CacheHandler()
   mFontDescriptionSizeCache(),
   mEllipsisCache(),
   mEmbeddedItemCache(),
+  mGlyphCacheManager(new GlyphCacheManager(GetMaxNumberOfGlyphCache())),
   mLatestFoundFontDescription(),
   mLatestFoundFontDescriptionId(0u),
   mLatestFoundCacheKey(0, 0),
@@ -264,6 +288,9 @@ FontClient::Plugin::CacheHandler::~CacheHandler()
 
 void FontClient::Plugin::CacheHandler::ClearCache()
 {
+  // delete cached glyph informations before clear mFontFaceCache.
+  mGlyphCacheManager->ClearCache();
+
   mDefaultFontDescription = FontDescription();
 
   mSystemFonts.clear();
index 740ac51..20c5995 100644 (file)
@@ -20,6 +20,7 @@
 
 // INTERNAL INCLUDES
 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
+#include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
 
 namespace Dali::TextAbstraction::Internal
 {
@@ -378,6 +379,12 @@ public: // Find & Cache
    */
   GlyphIndex CacheEmbeddedItem(EmbeddedItem&& embeddedItem);
 
+public: // Other public API
+  GlyphCacheManager* GetGlyphCacheManager() const
+  {
+    return mGlyphCacheManager.get();
+  }
+
 private:
   CacheHandler(const CacheHandler&) = delete;
   CacheHandler& operator=(const CacheHandler&) = delete;
@@ -404,7 +411,9 @@ public:                                    // Cache container list
   std::vector<PixelBufferCacheItem> mPixelBufferCache;  ///< Caches the pixel buffer of a url.
   std::vector<EmbeddedItem>         mEmbeddedItemCache; ///< Cache embedded items.
 
-private:                                         // Member value
+private:                                                 // Member value
+  std::unique_ptr<GlyphCacheManager> mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs.
+
   FontDescription   mLatestFoundFontDescription; ///< Latest found font description and id in FindValidatedFont()
   FontDescriptionId mLatestFoundFontDescriptionId;
 
index 6715e54..bc62cba 100644 (file)
@@ -1082,7 +1082,7 @@ FontId FontClient::Plugin::CreateFont(const FontPath& path,
         const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
 
         // Create the FreeType font face item to cache.
-        FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
+        FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
 
         fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
       }
@@ -1124,7 +1124,7 @@ FontId FontClient::Plugin::CreateFont(const FontPath& path,
                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
 
         // Create the FreeType font face item to cache.
-        FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics);
+        FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics);
 
         fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
       }
index 5ece316..90fbb02 100644 (file)
@@ -41,28 +41,6 @@ const float POINTS_PER_INCH = 72.f;
 constexpr float MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE = 1.5f;
 
 /**
- * @brief Maximum size of glyph cache per each font face.
- */
-constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX         = 128;
-constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u;
-
-constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
-
-/**
- * @brief Get maximum size of glyph cache size from environment.
- * If not settuped, default as 128.
- * @note This value fixed when we call it first time.
- * @return The max size of glyph cache.
- */
-inline const size_t GetMaxNumberOfGlyphCache()
-{
-  using Dali::EnvironmentVariable::GetEnvironmentVariable;
-  static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV);
-  static auto number       = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX;
-  return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number;
-}
-
-/**
  * @brief Behavior about cache the rendered glyph cache.
  */
 constexpr bool DEFAULT_ENABLE_CACHE_RENDERED_GLYPH = true;
@@ -74,7 +52,7 @@ constexpr auto ENABLE_CACHE_RENDERED_GLYPH_ENV     = "DALI_ENABLE_CACHE_RENDERED
  * @note This value fixed when we call it first time.
  * @return True if we allow to cache rendered glyph.
  */
-inline const bool EnableCacheRenderedGlyph()
+inline bool EnableCacheRenderedGlyph()
 {
   using Dali::EnvironmentVariable::GetEnvironmentVariable;
   static auto numberString = GetEnvironmentVariable(ENABLE_CACHE_RENDERED_GLYPH_ENV);
@@ -100,7 +78,7 @@ constexpr auto RENDERED_GLYPH_COMPRESS_POLICY_ENV = "DALI_RENDERED_GLYPH_COMPRES
  * @note This value fixed when we call it first time.
  * @return SPEED if value start with 's' or 'S'. MEMORY if value start with 'm' or 'M'. otherwise, use default
  */
-inline const GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy()
+inline GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy()
 {
   using Dali::EnvironmentVariable::GetEnvironmentVariable;
   static auto policyString = GetEnvironmentVariable(RENDERED_GLYPH_COMPRESS_POLICY_ENV);
@@ -115,13 +93,14 @@ inline const GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPo
 
 FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
                                      FT_Face            ftFace,
+                                     GlyphCacheManager* glyphCacheManager,
                                      const FontPath&    path,
                                      PointSize26Dot6    requestedPointSize,
                                      FaceIndex          face,
                                      const FontMetrics& metrics)
 : mFreeTypeLibrary(freeTypeLibrary),
   mFreeTypeFace(ftFace),
-  mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())),
+  mGlyphCacheManager(glyphCacheManager),
   mHarfBuzzProxyFont(),
   mPath(path),
   mRequestedPointSize(requestedPointSize),
@@ -140,6 +119,7 @@ FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
 
 FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
                                      FT_Face            ftFace,
+                                     GlyphCacheManager* glyphCacheManager,
                                      const FontPath&    path,
                                      PointSize26Dot6    requestedPointSize,
                                      FaceIndex          face,
@@ -150,7 +130,7 @@ FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
                                      bool               hasColorTables)
 : mFreeTypeLibrary(freeTypeLibrary),
   mFreeTypeFace(ftFace),
-  mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())),
+  mGlyphCacheManager(glyphCacheManager),
   mHarfBuzzProxyFont(),
   mPath(path),
   mRequestedPointSize(requestedPointSize),
@@ -173,7 +153,7 @@ FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs)
 : mFreeTypeLibrary(rhs.mFreeTypeLibrary)
 {
   mFreeTypeFace       = rhs.mFreeTypeFace;
-  mGlyphCacheManager  = std::move(rhs.mGlyphCacheManager);
+  mGlyphCacheManager  = rhs.mGlyphCacheManager;
   mHarfBuzzProxyFont  = std::move(rhs.mHarfBuzzProxyFont);
   mPath               = std::move(rhs.mPath);
   mRequestedPointSize = rhs.mRequestedPointSize;
@@ -188,17 +168,19 @@ FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs)
   mIsFixedSizeBitmap  = rhs.mIsFixedSizeBitmap;
   mHasColorTables     = rhs.mHasColorTables;
 
-  rhs.mFreeTypeFace = nullptr;
+  rhs.mFreeTypeFace      = nullptr;
+  rhs.mGlyphCacheManager = nullptr;
 }
 
 FontFaceCacheItem::~FontFaceCacheItem()
 {
-  // delete glyph cache manager before free face.
+  // delete cached glyph informations before free face.
   if(mGlyphCacheManager)
   {
-    mGlyphCacheManager.reset();
+    mGlyphCacheManager->RemoveGlyphFromFace(mFreeTypeFace);
   }
 
+  // delete harfbuzz proxy font before free face.
   if(mHarfBuzzProxyFont)
   {
     mHarfBuzzProxyFont.reset();
@@ -245,7 +227,7 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe
   if(mIsFixedSizeBitmap)
   {
     FT_Select_Size(mFreeTypeFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
-    mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, glyphData, error);
+    mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, glyphData, error);
 
     if(FT_Err_Ok == error)
     {
@@ -287,7 +269,7 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe
 
           // TODO : If dpiVertical value changed, this resize feature will be break down.
           // Otherwise, this glyph will be resized only one times.
-          mGlyphCacheManager->ResizeBitmapGlyph(glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast<uint32_t>(glyphInfo.width), static_cast<uint32_t>(glyphInfo.height));
+          mGlyphCacheManager->ResizeBitmapGlyph(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast<uint32_t>(glyphInfo.width), static_cast<uint32_t>(glyphInfo.height));
         }
       }
     }
@@ -303,7 +285,7 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe
     // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
     // i.e. with the SNum-3R font.
     // @todo: add an option to use the FT_LOAD_DEFAULT if required?
-    mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, error);
+    mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, error);
 
     // Keep the width of the glyph before doing the software emboldening.
     // It will be used to calculate a scale factor to be applied to the
@@ -332,7 +314,7 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe
       {
         // Get dummy glyph data without embolden.
         GlyphCacheManager::GlyphCacheData dummyData;
-        if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error))
+        if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error))
         {
           // If the glyph is emboldened by software, the advance is multiplied by a
           // scale factor to make it slightly bigger.
@@ -397,7 +379,7 @@ void FontFaceCacheItem::CreateBitmap(
     // @todo: add an option to use the FT_LOAD_DEFAULT if required?
     loadFlag = FT_LOAD_NO_AUTOHINT;
   }
-  mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, glyphData, error);
+  mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, glyphData, error);
 
   if(FT_Err_Ok == error)
   {
@@ -502,10 +484,10 @@ void FontFaceCacheItem::CreateBitmap(
           // Note : We will call this API once per each glyph.
           if(ableUseCachedRenderedGlyph)
           {
-            mGlyphCacheManager->CacheRenderedGlyphBuffer(glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
+            mGlyphCacheManager->CacheRenderedGlyphBuffer(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
 
             GlyphCacheManager::GlyphCacheData dummyData;
-            mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, dummyData, error);
+            mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, dummyData, error);
 
             if(DALI_LIKELY(FT_Err_Ok == error && dummyData.mRenderedBuffer))
             {
@@ -560,7 +542,7 @@ bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
   if(mHasColorTables)
   {
     GlyphCacheManager::GlyphCacheData dummyData;
-    mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_COLOR, false, dummyData, error);
+    mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR, false, dummyData, error);
   }
 #endif
   return FT_Err_Ok == error;
@@ -615,7 +597,7 @@ HarfBuzzFontHandle FontFaceCacheItem::GetHarfBuzzFont(const uint32_t& horizontal
   // Create new harfbuzz font only first time or DPI changed.
   if(DALI_UNLIKELY(!mHarfBuzzProxyFont || mHarfBuzzProxyFont->mHorizontalDpi != horizontalDpi || mHarfBuzzProxyFont->mVerticalDpi != verticalDpi))
   {
-    mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager.get()));
+    mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager));
   }
   return mHarfBuzzProxyFont->GetHarfBuzzFont();
 }
index c6bc1b8..10608d7 100644 (file)
@@ -43,6 +43,7 @@ struct FontFaceCacheItem : public FontCacheItemInterface
 {
   FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
                     FT_Face            ftFace,
+                    GlyphCacheManager* glyphCacheManager,
                     const FontPath&    path,
                     PointSize26Dot6    requestedPointSize,
                     FaceIndex          face,
@@ -50,6 +51,7 @@ struct FontFaceCacheItem : public FontCacheItemInterface
 
   FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
                     FT_Face            ftFace,
+                    GlyphCacheManager* glyphCacheManager,
                     const FontPath&    path,
                     PointSize26Dot6    requestedPointSize,
                     FaceIndex          face,
@@ -132,7 +134,7 @@ public:
   const FT_Library& mFreeTypeLibrary; ///< A handle to a FreeType library instance.
   FT_Face           mFreeTypeFace;    ///< The FreeType face.
 
-  std::unique_ptr<GlyphCacheManager> mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs.
+  GlyphCacheManager*                 mGlyphCacheManager; ///< The reference of Glyph cache manager. Owned from font-client-plugin-cache-handler.
   std::unique_ptr<HarfBuzzProxyFont> mHarfBuzzProxyFont; ///< The harfbuzz font. It will store harfbuzz relate data.
 
   FontPath        mPath;                  ///< The path to the font file name.
index 1ced0b5..a241635 100644 (file)
@@ -36,9 +36,8 @@ namespace
 constexpr uint32_t THRESHOLD_WIDTH_FOR_RLE4_COMPRESSION = 8; // The smallest width of glyph that we use RLE4 method.
 } // namespace
 
-GlyphCacheManager::GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyphCache)
-: mFreeTypeFace(ftFace),
-  mGlyphCacheMaxSize(maxNumberOfGlyphCache),
+GlyphCacheManager::GlyphCacheManager(std::size_t maxNumberOfGlyphCache)
+: mGlyphCacheMaxSize(maxNumberOfGlyphCache),
   mLRUGlyphCache(mGlyphCacheMaxSize)
 {
   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager Create with maximum size : %d\n", static_cast<int>(mGlyphCacheMaxSize));
@@ -46,17 +45,11 @@ GlyphCacheManager::GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyp
 
 GlyphCacheManager::~GlyphCacheManager()
 {
-  while(!mLRUGlyphCache.IsEmpty())
-  {
-    auto removedData = mLRUGlyphCache.Pop();
-
-    // Release Glyph data resource
-    removedData.ReleaseGlyphData();
-  }
-  mLRUGlyphCache.Clear();
+  ClearCache();
 }
 
 bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
+  const FT_Face    freeTypeFace,
   const GlyphIndex index,
   const FT_Int32   flag,
   const bool       isBoldRequired,
@@ -66,7 +59,7 @@ bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
   // Append some error value here instead of FT_Err_Ok.
   error = static_cast<FT_Error>(-1);
 
-  const GlyphCacheKey key  = GlyphCacheKey(index, flag, isBoldRequired);
+  const GlyphCacheKey key  = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired);
   auto                iter = mLRUGlyphCache.Find(key);
 
   if(iter == mLRUGlyphCache.End())
@@ -82,13 +75,13 @@ bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
       removedData.ReleaseGlyphData();
     }
 
-    const bool loadSuccess = LoadGlyphDataFromIndex(index, flag, isBoldRequired, glyphData, error);
+    const bool loadSuccess = LoadGlyphDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphData, error);
     if(loadSuccess)
     {
       // Copy and cached data.
       mLRUGlyphCache.Push(key, glyphData);
 
-      DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for face : %p, index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", freeTypeFace, index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
     }
 
     return loadSuccess;
@@ -98,36 +91,37 @@ bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
     error = FT_Err_Ok;
 
     // We already notify that we use this glyph. And now, copy cached data.
-    glyphData = iter->element;
+    glyphData = mLRUGlyphCache.GetElement(iter);
 
-    DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
+    DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for face : %p, index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", freeTypeFace, index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
     return true;
   }
 }
 
 bool GlyphCacheManager::LoadGlyphDataFromIndex(
+  const FT_Face    freeTypeFace,
   const GlyphIndex index,
   const FT_Int32   flag,
   const bool       isBoldRequired,
   GlyphCacheData&  glyphData,
   FT_Error&        error)
 {
-  error = FT_Load_Glyph(mFreeTypeFace, index, flag);
+  error = FT_Load_Glyph(freeTypeFace, index, flag);
   if(FT_Err_Ok == error)
   {
-    glyphData.mStyleFlags = mFreeTypeFace->style_flags;
+    glyphData.mStyleFlags = freeTypeFace->style_flags;
 
     const bool isEmboldeningRequired = isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
     if(isEmboldeningRequired)
     {
       // Does the software bold.
-      FT_GlyphSlot_Embolden(mFreeTypeFace->glyph);
+      FT_GlyphSlot_Embolden(freeTypeFace->glyph);
     }
 
-    glyphData.mGlyphMetrics = mFreeTypeFace->glyph->metrics;
+    glyphData.mGlyphMetrics = freeTypeFace->glyph->metrics;
     glyphData.mIsBitmap     = false;
     // Load glyph
-    error = FT_Get_Glyph(mFreeTypeFace->glyph, &glyphData.mGlyph);
+    error = FT_Get_Glyph(freeTypeFace->glyph, &glyphData.mGlyph);
 
     if(glyphData.mGlyph->format == FT_GLYPH_FORMAT_BITMAP)
     {
@@ -137,7 +131,7 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex(
       // Copy rendered bitmap
       // TODO : Is there any way to keep bitmap buffer without copy?
       glyphData.mBitmap  = new FT_Bitmap();
-      *glyphData.mBitmap = mFreeTypeFace->glyph->bitmap;
+      *glyphData.mBitmap = freeTypeFace->glyph->bitmap;
 
       // New allocate buffer
       size_t bufferSize = 0;
@@ -172,7 +166,7 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex(
       {
         glyphData.mIsBitmap       = true;
         glyphData.mBitmap->buffer = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
-        memcpy(glyphData.mBitmap->buffer, mFreeTypeFace->glyph->bitmap.buffer, bufferSize);
+        memcpy(glyphData.mBitmap->buffer, freeTypeFace->glyph->bitmap.buffer, bufferSize);
       }
       else
       {
@@ -195,6 +189,7 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex(
 }
 
 void GlyphCacheManager::ResizeBitmapGlyph(
+  const FT_Face    freeTypeFace,
   const GlyphIndex index,
   const FT_Int32   flag,
   const bool       isBoldRequired,
@@ -208,7 +203,7 @@ void GlyphCacheManager::ResizeBitmapGlyph(
   }
   FT_Error       error;
   GlyphCacheData originGlyphData;
-  if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
+  if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, originGlyphData, error))
   {
     if(DALI_LIKELY(originGlyphData.mIsBitmap && originGlyphData.mBitmap))
     {
@@ -216,10 +211,10 @@ void GlyphCacheManager::ResizeBitmapGlyph(
       if(requiredResize)
       {
         // originalGlyphData is copy data. For change cached information, we should access as iterator.
-        const GlyphCacheKey key  = GlyphCacheKey(index, flag, isBoldRequired);
+        const GlyphCacheKey key  = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired);
         auto                iter = mLRUGlyphCache.Find(key);
 
-        GlyphCacheData& destinationGlpyhData = iter->element;
+        GlyphCacheData& destinationGlpyhData = mLRUGlyphCache.GetElement(iter);
 
         const ImageDimensions inputDimensions(destinationGlpyhData.mBitmap->width, destinationGlpyhData.mBitmap->rows);
         const ImageDimensions desiredDimensions(desiredWidth, desiredHeight);
@@ -297,6 +292,7 @@ void GlyphCacheManager::ResizeBitmapGlyph(
 }
 
 void GlyphCacheManager::CacheRenderedGlyphBuffer(
+  const FT_Face               freeTypeFace,
   const GlyphIndex            index,
   const FT_Int32              flag,
   const bool                  isBoldRequired,
@@ -310,15 +306,15 @@ void GlyphCacheManager::CacheRenderedGlyphBuffer(
   }
   FT_Error       error;
   GlyphCacheData originGlyphData;
-  if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
+  if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, originGlyphData, error))
   {
     if(DALI_LIKELY(!originGlyphData.mIsBitmap && originGlyphData.mRenderedBuffer == nullptr))
     {
       // originalGlyphData is copy data. For change cached information, we should access as iterator.
-      const GlyphCacheKey key  = GlyphCacheKey(index, flag, isBoldRequired);
+      const GlyphCacheKey key  = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired);
       auto                iter = mLRUGlyphCache.Find(key);
 
-      GlyphCacheData& destinationGlpyhData = iter->element;
+      GlyphCacheData& destinationGlpyhData = mLRUGlyphCache.GetElement(iter);
 
       destinationGlpyhData.mRenderedBuffer = new TextAbstraction::FontClient::GlyphBufferData();
       if(DALI_UNLIKELY(!destinationGlpyhData.mRenderedBuffer))
@@ -399,6 +395,60 @@ void GlyphCacheManager::CacheRenderedGlyphBuffer(
   }
 }
 
+void GlyphCacheManager::RemoveGlyphFromFace(const FT_Face freeTypeFace)
+{
+  uint32_t removedItemCount = 0;
+
+  auto endIter = mLRUGlyphCache.End();
+  for(auto iter = mLRUGlyphCache.Begin(); iter != endIter;)
+  {
+    // Check whether this cached item has inputed freeTypeFace as key.
+    auto keyFace = mLRUGlyphCache.GetKey(iter).mFreeTypeFace;
+    if(keyFace == freeTypeFace)
+    {
+      ++removedItemCount;
+      iter = mLRUGlyphCache.Erase(iter);
+    }
+    else
+    {
+      ++iter;
+    }
+  }
+
+  DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::RemoveGlyphFromFace. Remove all cached glyph with face : %p, removed glyph count : %u\n", freeTypeFace, removedItemCount);
+}
+
+void GlyphCacheManager::ClearCache(const std::size_t remainCount)
+{
+  if(remainCount == 0u)
+  {
+    // Release all data memory first.
+    auto endIter = mLRUGlyphCache.End();
+    for(auto iter = mLRUGlyphCache.Begin(); iter != endIter; ++iter)
+    {
+      // Get the reference of data. and release it.
+      auto& removedData = mLRUGlyphCache.GetElement(iter);
+      removedData.ReleaseGlyphData();
+    }
+
+    // Clear all cache.
+    mLRUGlyphCache.Clear();
+  }
+  else
+  {
+    // While the cache count is bigger than remainCount, remove oldest glyph.
+    while(mLRUGlyphCache.Count() > remainCount)
+    {
+      auto removedData = mLRUGlyphCache.Pop();
+
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::ClearCache[%zu / %zu]. Remove oldest cache for glyph : %p\n", mLRUGlyphCache.Count(), remainCount, removedData.mGlyph);
+
+      // Release Glyph data resource
+      removedData.ReleaseGlyphData();
+    }
+  }
+}
+
 void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
 {
   if(mIsBitmap && mBitmap)
@@ -419,7 +469,10 @@ void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
   if(mRenderedBuffer)
   {
     delete mRenderedBuffer;
+    mRenderedBuffer = nullptr;
   }
+
+  mStyleFlags = 0;
 }
 
 } // namespace Dali::TextAbstraction::Internal
index fd0eb05..15f5e45 100644 (file)
@@ -36,7 +36,7 @@ class GlyphCacheManager
 {
 public:
   // Constructor
-  GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyphCache);
+  GlyphCacheManager(std::size_t maxNumberOfGlyphCache);
 
   // Destructor
   ~GlyphCacheManager();
@@ -88,6 +88,7 @@ public:
   /**
    * @brief Load GlyphCacheData from face. The result will be cached.
    *
+   * @param[in] freeTypeFace The freetype face handle.
    * @param[in] index Index of glyph in this face.
    * @param[in] flag Flag when we load the glyph.
    * @param[in] isBoldRequired True if we require some software bold.
@@ -96,6 +97,7 @@ public:
    * @return True if load successfully. False if something error occured.
    */
   bool GetGlyphCacheDataFromIndex(
+    const FT_Face    freeTypeFace,
     const GlyphIndex index,
     const FT_Int32   flag,
     const bool       isBoldRequired,
@@ -106,6 +108,7 @@ public:
    * @brief Load GlyphCacheData from face. The result will not be cached.
    * @note If we call this API, We should release GlyphCacheData manually.
    *
+   * @param[in] freeTypeFace The freetype face handle.
    * @param[in] index Index of glyph in this face.
    * @param[in] flag Flag when we load the glyph.
    * @param[in] isBoldRequired True if we require some software bold.
@@ -114,6 +117,7 @@ public:
    * @return True if load successfully. False if something error occured.
    */
   bool LoadGlyphDataFromIndex(
+    const FT_Face    freeTypeFace,
     const GlyphIndex index,
     const FT_Int32   flag,
     const bool       isBoldRequired,
@@ -124,6 +128,7 @@ public:
    * @brief Resize bitmap glyph. The result will change cached glyph bitmap information.
    * If glyph is not bitmap glyph, nothing happened.
    *
+   * @param[in] freeTypeFace The freetype face handle.
    * @param[in] index Index of glyph in this face.
    * @param[in] flag Flag when we load the glyph.
    * @param[in] isBoldRequired True if we require some software bold.
@@ -131,6 +136,7 @@ public:
    * @param[in] desiredHeight Desired height of bitmap.
    */
   void ResizeBitmapGlyph(
+    const FT_Face    freeTypeFace,
     const GlyphIndex index,
     const FT_Int32   flag,
     const bool       isBoldRequired,
@@ -141,6 +147,7 @@ public:
    * @brief Cache rendered glyph bitmap. The result will change cached glyph information.
    * If glyph is not single color glyph, or we already cached buffer before, nothing happened.
    *
+   * @param[in] freeTypeFace The freetype face handle.
    * @param[in] index Index of glyph in this face.
    * @param[in] flag Flag when we load the glyph.
    * @param[in] isBoldRequired True if we require some software bold.
@@ -148,12 +155,28 @@ public:
    * @param[in] policy Compress behavior policy.
    */
   void CacheRenderedGlyphBuffer(
+    const FT_Face               freeTypeFace,
     const GlyphIndex            index,
     const FT_Int32              flag,
     const bool                  isBoldRequired,
     const FT_Bitmap&            srcBitmap,
     const CompressionPolicyType policy);
 
+  /**
+   * @brief Clear all cached glyph informations which has inputed FreeTypeFace.
+   *
+   * @note This API iterate all cached glyph. Should be called rarely.
+   * @param[in] freeTypeFace The freetype face handle.
+   */
+  void RemoveGlyphFromFace(const FT_Face freeTypeFace);
+
+  /**
+   * @brief Clear all cached glyph informations.
+   *
+   * @param[in] remainCount The number of remained cache items after call this API. Default is 0, clear all items.
+   */
+  void ClearCache(const std::size_t remainCount = 0u);
+
 private:
   // Private struct area.
   /**
@@ -162,25 +185,29 @@ private:
   struct GlyphCacheKey
   {
     GlyphCacheKey()
-    : mIndex(0u),
+    : mFreeTypeFace(nullptr),
+      mIndex(0u),
       mFlag(0),
       mIsBoldRequired(false)
     {
     }
 
-    GlyphCacheKey(const GlyphIndex index, const FT_Int32 flag, const bool boldRequired)
-    : mIndex(index),
+    GlyphCacheKey(const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool boldRequired)
+    : mFreeTypeFace(freeTypeFace),
+      mIndex(index),
       mFlag(flag),
       mIsBoldRequired(boldRequired)
     {
     }
+
+    FT_Face    mFreeTypeFace;
     GlyphIndex mIndex;
     FT_Int32   mFlag;
     bool       mIsBoldRequired : 1;
 
     bool operator==(GlyphCacheKey const& rhs) const noexcept
     {
-      return mIndex == rhs.mIndex && mFlag == rhs.mFlag && mIsBoldRequired == rhs.mIsBoldRequired;
+      return mFreeTypeFace == rhs.mFreeTypeFace && mIndex == rhs.mIndex && mFlag == rhs.mFlag && mIsBoldRequired == rhs.mIsBoldRequired;
     }
   };
 
@@ -191,14 +218,15 @@ private:
   {
     std::size_t operator()(GlyphCacheKey const& key) const noexcept
     {
-      return static_cast<std::size_t>(key.mIndex) ^ static_cast<std::size_t>(key.mFlag) ^ (static_cast<std::size_t>(key.mIsBoldRequired) << 29);
+      return static_cast<std::size_t>(reinterpret_cast<std::uintptr_t>(key.mFreeTypeFace)) ^
+             static_cast<std::size_t>(key.mIndex) ^
+             static_cast<std::size_t>(key.mFlag) ^
+             (static_cast<std::size_t>(key.mIsBoldRequired) << 29);
     }
   };
 
 private:
   // Private member value area.
-  FT_Face mFreeTypeFace; ///< The FreeType face. Owned from font-face-cache-item
-
   std::size_t mGlyphCacheMaxSize; ///< The maximum capacity of glyph cache.
 
   using CacheContainer = LRUCacheContainer<GlyphCacheKey, GlyphCacheData, GlyphCacheKeyHash>;
index 5d62a95..53d3d0f 100644 (file)
@@ -76,7 +76,7 @@ private:
 
 public:
   FT_Face            mFreeTypeFace;      ///< The FreeType face. Owned from font-face-cache-item.
-  GlyphCacheManager* mGlyphCacheManager; ///< Glyph caching system for this harfbuzz font. Owned from font-face-cache-item.
+  GlyphCacheManager* mGlyphCacheManager; ///< Glyph caching system for this harfbuzz font. Owned from font-client-plugin-cache-handler.
 
   hb_font_t* mHarfBuzzFont; ///< Harfbuzz font handle integrated with FT_Face.
 };
@@ -126,7 +126,7 @@ static bool GetGlyphCacheData(void* font_data, const GlyphIndex& glyphIndex, Gly
   if(DALI_LIKELY(impl && impl->mGlyphCacheManager))
   {
     FT_Error error;
-    return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphData, error);
+    return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(impl->mFreeTypeFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphData, error);
   }
   return false;
 }
index 5c84f67..50b57b9 100644 (file)
@@ -46,7 +46,7 @@ class LRUCacheContainer
 {
 public:
   // Constructor
-  LRUCacheContainer(std::size_t maxNumberOfCache = std::numeric_limits<std::size_t>::max())
+  LRUCacheContainer(std::size_t maxNumberOfCache = std::numeric_limits<std::size_t>::max() - 2u)
   : mCacheMaxSize(maxNumberOfCache)
   {
   }
@@ -57,6 +57,9 @@ public:
   LRUCacheContainer(const LRUCacheContainer& rhs) = default;
   LRUCacheContainer(LRUCacheContainer&& rhs)      = default;
 
+  LRUCacheContainer& operator=(const LRUCacheContainer& rhs) = default;
+  LRUCacheContainer& operator=(LRUCacheContainer&& rhs) = default;
+
 public:
   // Public struct area.
   using CacheId          = std::size_t; ///< The id of cached element. It can be used until element poped out.
@@ -71,24 +74,96 @@ public:
    */
   static constexpr CacheId CACHE_FOOTER_ID = std::numeric_limits<std::size_t>::max() - 1u;
 
+public:
+  // iterator.
   /**
-   * @brief Double linked CacheNode that this container used.
+   * @brief Iterator of this LRU cache.
+   *
+   * Add, Get and Clear invalidate the itertator's validation.
+   * Erase and Pop validate the iterator.
+   *
+   * Range based iteration doesn't supported.
    */
-  struct CacheNode
+  struct iterator
   {
-    CacheNode()  = default;
-    ~CacheNode() = default;
+  public:
+    iterator(LRUCacheContainer& owner, const CacheId& id)
+    : owner(owner),
+      id(id)
+    {
+    }
 
-    CacheId     prev{CACHE_FOOTER_ID};
-    CacheId     next{CACHE_HEADER_ID};
-    ElementType element;
+    // copy constructor & assign
+    iterator(const iterator& rhs)
+    : owner(rhs.owner),
+      id(rhs.id)
+    {
+    }
 
-    using CacheIdIterator = typename CacheIdContainer::iterator;
+    iterator& operator=(const iterator& rhs)
+    {
+      this->owner = rhs.owner;
+      this->id    = rhs.id;
+      return *this;
+    }
 
-    CacheIdIterator cacheIdIterator; ///< Note : It only validate until mCacheId rehashing.
-  };
+    // Move constructor & assign
+    iterator(iterator&& rhs)
+    : owner(std::move(rhs.owner)),
+      id(rhs.id)
+    {
+    }
+
+    iterator& operator=(iterator&& rhs)
+    {
+      this->owner = std::move(rhs.owner);
+      this->id    = rhs.id;
+      return *this;
+    }
 
-  using iterator = typename std::vector<CacheNode>::iterator;
+    // Prefix increment
+    iterator& operator++()
+    {
+      id = owner.mData[id].next;
+      return *this;
+    }
+
+    // Postfix increment
+    iterator operator++(int)
+    {
+      iterator temp = *this;
+      ++(*this);
+      return temp;
+    }
+
+    // Prefix decrement
+    iterator& operator--()
+    {
+      id = owner.mData[id].prev;
+      return *this;
+    }
+
+    // Postfix decrement
+    iterator operator--(int)
+    {
+      iterator temp = *this;
+      --(*this);
+      return temp;
+    }
+
+    bool operator==(const iterator& rhs)
+    {
+      return id == rhs.id && (&owner) == (&rhs.owner);
+    }
+    bool operator!=(const iterator& rhs)
+    {
+      return id != rhs.id || (&owner) != (&rhs.owner);
+    }
+
+  public:
+    LRUCacheContainer& owner; // The reference of owner of this iterator.
+    CacheId            id;
+  };
 
 public:
   // Public API area.
@@ -191,7 +266,35 @@ public:
   }
 
   /**
-   * @brief Find an element by the key. It will be marked as recent
+   * @brief Get an key by iterator. It will not be marked as recent
+   *
+   * @param[in] iter The iterator of element
+   * @return A reference of the key
+   * @warning This method don't check iterator validation
+   */
+  const KeyType& GetKey(iterator iter)
+  {
+    const auto id = iter.id;
+
+    return mData[id].cacheIdIterator->first;
+  }
+
+  /**
+   * @brief Get an element by iterator. It will not be marked as recent
+   *
+   * @param[in] iter The iterator of element
+   * @return A reference of the element
+   * @warning This method don't check iterator validation
+   */
+  ElementType& GetElement(iterator iter)
+  {
+    const auto id = iter.id;
+
+    return mData[id].element;
+  }
+
+  /**
+   * @brief Find an element by the key. It will not be marked as recent
    *
    * @param[in] key The key of element
    * @return A iterator of cache node. If key not exist, return End()
@@ -205,11 +308,7 @@ public:
 
     const auto id = mCacheId[key];
 
-    // Mark as recently used.
-    InternalPop(id);
-    InternalInsertAfterHeader(id);
-
-    return Begin() + id;
+    return iterator(*this, id);
   }
 
   /**
@@ -250,12 +349,34 @@ public:
 
   iterator Begin()
   {
-    return mData.begin();
+    return iterator(*this, mLatestId);
   }
 
   iterator End()
   {
-    return mData.end();
+    return iterator(*this, CACHE_FOOTER_ID);
+  }
+
+  /**
+   * @brief Remove cache item by iterator.
+   *
+   * @param[in] iter The iterator what we want to remove.
+   * @return iterator The next iterator after remove
+   * @warning This method don't check iterator validation
+   */
+  iterator Erase(iterator iter)
+  {
+    const auto id     = iter.id;
+    const auto nextId = mData[id].next;
+    InternalPop(id);
+    InternalInsertAfterFooter(id);
+
+    --mNumberOfElements;
+
+    // Erase cache id.
+    mCacheId.erase(mData[id].cacheIdIterator);
+
+    return iterator(*this, nextId);
   }
 
   /**
@@ -269,9 +390,6 @@ public:
   }
 
 private:
-  // Private struct area.
-
-private:
   // Private API area.
 
   /**
@@ -416,6 +534,25 @@ private:
   }
 
 private:
+  // Private struct area.
+  /**
+   * @brief Double linked CacheNode that this container used.
+   */
+  struct CacheNode
+  {
+    CacheNode()  = default;
+    ~CacheNode() = default;
+
+    CacheId     prev{CACHE_FOOTER_ID};
+    CacheId     next{CACHE_HEADER_ID};
+    ElementType element;
+
+    using CacheIdIterator = typename CacheIdContainer::iterator;
+
+    CacheIdIterator cacheIdIterator; ///< Note : It only validate until mCacheId rehashing.
+  };
+
+private:
   // Private member value area.
   std::size_t mCacheMaxSize{0};     ///< The maximum capacity of cache.
   std::size_t mNumberOfElements{0}; ///< The number of elements.