Refactor: font face cache 48/322748/17
authorBowon Ryu <bowon.ryu@samsung.com>
Tue, 15 Apr 2025 09:22:46 +0000 (18:22 +0900)
committerBowon Ryu <bowon.ryu@samsung.com>
Tue, 29 Apr 2025 08:45:48 +0000 (17:45 +0900)
The core change in this patch is to create 1 freetype face per font file.
Previously, we created as many faces as font-face-cache-items.

This was fine for static (seldom changing) apps, but when using a wide variety of point sizes,
it would create a face every time, which used too much memory.

Now we reuse 1 face.
Instead of faces, freetype sizes are cached and activated on demand,
which uses significantly less memory than the previous face cache.

In static apps, the memory usage difference may not be significant,
but in extreme cases where point sizes exceed thousands, it is incomparably efficient.

And now, we can use pre-loaded memory fonts in async text as well via the font file manager!

TODO: Optimize font-face-cache-item.

Change-Id: I38f44e641e00ae7bfcf16891f92ba14d74c9bff9
Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
27 files changed:
dali/devel-api/file.list
dali/devel-api/text-abstraction/font-file-manager.cpp [new file with mode: 0644]
dali/devel-api/text-abstraction/font-file-manager.h [new file with mode: 0644]
dali/devel-api/text-abstraction/shaping.cpp
dali/devel-api/text-abstraction/shaping.h
dali/devel-api/text-abstraction/text-abstraction.h
dali/internal/system/common/environment-variables.h
dali/internal/text/file.list
dali/internal/text/text-abstraction/font-client-impl.cpp
dali/internal/text/text-abstraction/font-client-impl.h
dali/internal/text/text-abstraction/font-file-manager-impl.cpp [new file with mode: 0644]
dali/internal/text/text-abstraction/font-file-manager-impl.h [new file with mode: 0644]
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-client-plugin-impl.h
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/font-face-manager.cpp [new file with mode: 0644]
dali/internal/text/text-abstraction/plugin/font-face-manager.h [new file with mode: 0644]
dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp
dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h
dali/internal/text/text-abstraction/plugin/lru-cache-container.h
dali/internal/text/text-abstraction/shaping-impl.cpp
dali/internal/text/text-abstraction/shaping-impl.h

index df109d979055b2fba0a4d0c910a51de6a2f29578..3fba70984b55fc8f091f40b9737acc0f2ccb0948 100755 (executable)
@@ -182,6 +182,7 @@ SET( devel_api_text_abstraction_src_files
    ${adaptor_devel_api_dir}/text-abstraction/bidirectional-support.cpp
    ${adaptor_devel_api_dir}/text-abstraction/bitmap-font.cpp
    ${adaptor_devel_api_dir}/text-abstraction/font-client.cpp
+   ${adaptor_devel_api_dir}/text-abstraction/font-file-manager.cpp
    ${adaptor_devel_api_dir}/text-abstraction/font-list.cpp
    ${adaptor_devel_api_dir}/text-abstraction/font-metrics.cpp
    ${adaptor_devel_api_dir}/text-abstraction/glyph-buffer-data.cpp
@@ -202,6 +203,7 @@ SET( text_abstraction_header_files
    ${adaptor_devel_api_dir}/text-abstraction/bidirectional-support.h
    ${adaptor_devel_api_dir}/text-abstraction/bitmap-font.h
    ${adaptor_devel_api_dir}/text-abstraction/font-client.h
+   ${adaptor_devel_api_dir}/text-abstraction/font-file-manager.h
    ${adaptor_devel_api_dir}/text-abstraction/font-list.h
    ${adaptor_devel_api_dir}/text-abstraction/font-metrics.h
    ${adaptor_devel_api_dir}/text-abstraction/glyph-buffer-data.h
diff --git a/dali/devel-api/text-abstraction/font-file-manager.cpp b/dali/devel-api/text-abstraction/font-file-manager.cpp
new file mode 100644 (file)
index 0000000..4948f4c
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/devel-api/text-abstraction/font-file-manager.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/text/text-abstraction/font-file-manager-impl.h>
+
+namespace Dali
+{
+namespace TextAbstraction
+{
+FontFileManager::FontFileManager()
+{
+}
+
+FontFileManager::~FontFileManager()
+{
+}
+
+FontFileManager::FontFileManager(Internal::FontFileManager* impl)
+: BaseHandle(impl)
+{
+}
+
+FontFileManager FontFileManager::Get()
+{
+  return Internal::FontFileManager::Get();
+}
+
+FontFileManager::FontFileManager(const FontFileManager& handle) = default;
+
+FontFileManager& FontFileManager::operator=(const FontFileManager& handle) = default;
+
+FontFileManager::FontFileManager(FontFileManager&& handle) noexcept = default;
+
+FontFileManager& FontFileManager::operator=(FontFileManager&& handle) noexcept = default;
+
+bool FontFileManager::FindFontFile(const FontPath& fontPath) const
+{
+  return GetImplementation(*this).FindFontFile(fontPath);
+}
+
+bool FontFileManager::FindFontFile(const FontPath& fontPath, Dali::Any& fontFilePtr, std::streampos& fileSize) const
+{
+  return GetImplementation(*this).FindFontFile(fontPath, fontFilePtr, fileSize);
+}
+
+void FontFileManager::CacheFontFile(const FontPath& fontPath, Dali::Vector<uint8_t>&& fontFileBuffer, const std::streampos& fileSize) const
+{
+  GetImplementation(*this).CacheFontFile(fontPath, std::move(fontFileBuffer), fileSize);
+}
+
+void FontFileManager::ClearCache()
+{
+  GetImplementation(*this).ClearCache();
+}
+
+} // namespace TextAbstraction
+
+} // namespace Dali
diff --git a/dali/devel-api/text-abstraction/font-file-manager.h b/dali/devel-api/text-abstraction/font-file-manager.h
new file mode 100644 (file)
index 0000000..72fbb65
--- /dev/null
@@ -0,0 +1,143 @@
+#ifndef DALI_PLATFORM_TEXT_ABSTRACTION_FONT_FILE_MANAGER_H
+#define DALI_PLATFORM_TEXT_ABSTRACTION_FONT_FILE_MANAGER_H
+
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/any.h>
+#include <dali/public-api/object/base-handle.h>
+
+namespace Dali
+{
+namespace TextAbstraction
+{
+
+namespace Internal DALI_INTERNAL
+{
+class FontFileManager;
+
+} // namespace DALI_INTERNAL
+
+/**
+ * @brief Read font files for freetype memory face use and store them in the cache.
+ */
+class DALI_ADAPTOR_API FontFileManager : public BaseHandle
+{
+public:
+  /**
+   * @brief Create an uninitialized FontFileManager handle.
+   *
+   */
+  FontFileManager();
+
+  /**
+   * @brief Destructor
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   */
+  ~FontFileManager();
+
+  /**
+   * @brief This copy constructor is required for (smart) pointer semantics.
+   *
+   * @param[in] handle A reference to the copied handle.
+   */
+  FontFileManager(const FontFileManager& handle);
+
+  /**
+   * @brief This assignment operator is required for (smart) pointer semantics.
+   *
+   * @param [in] handle  A reference to the copied handle.
+   * @return A reference to this.
+   */
+  FontFileManager& operator=(const FontFileManager& handle);
+
+  /**
+   * @brief This move constructor is required for (smart) pointer semantics.
+   *
+   * @param[in] handle A reference to the moved handle.
+   */
+  FontFileManager(FontFileManager&& handle) noexcept;
+
+  /**
+   * @brief This move assignment operator is required for (smart) pointer semantics.
+   *
+   * @param [in] handle  A reference to the moved handle.
+   * @return A reference to this.
+   */
+  FontFileManager& operator=(FontFileManager&& handle) noexcept;
+
+  /**
+   * @brief This constructor is used by FontFileManager::Get().
+   *
+   * @param[in] implementation A pointer to the internal fontFileManager object.
+   */
+  explicit DALI_INTERNAL FontFileManager(Internal::FontFileManager* implementation);
+
+  /**
+   * @brief Retrieve a handle to the FontFileManager instance.
+   *
+   * @return A handle to the FontFileManager.
+   * @remarks A reference to the singleton instance of FontFileManager.
+   */
+  static FontFileManager Get();
+
+  /**
+   * @brief Checks if font file for the specified font path is cached.
+   *
+   * @param[in] fontPath The font path to check for cached file.
+   *
+   * @return @e true if the font file is cached, otherwise false.
+   */
+  bool FindFontFile(const FontPath& fontPath) const;
+
+  /**
+   * @brief Retrieves font file for the specified font path if it is cached.
+   *
+   * @param[in] fontPath The font path to retrieve the cached file for.
+   * @param[out] fontFilePtr A pointer(uint8_t*) to the cached font file.
+   * @param[out] fileSize The size of the cached font file.
+   *
+   * @return @e true if the font file is cached and retrieved successfully, otherwise false.
+   */
+  bool FindFontFile(const FontPath& fontPath, Dali::Any& fontFilePtr, std::streampos& fileSize) const;
+
+  /**
+   * @brief Caches font file for the specified font path.
+   *
+   * @param[in] fontPath The font path to cache the file for.
+   * @param[in] fontFileBuffer A vector containing the font file to cache.
+   * @param[in] fileSize The size of the font file to cache.
+   */
+  void CacheFontFile(const FontPath& fontPath, Dali::Vector<uint8_t>&& fontFileBuffer, const std::streampos& fileSize) const;
+
+  /**
+   * @brief Clear all cached font files.
+   */
+  void ClearCache();
+};
+
+} // namespace TextAbstraction
+
+} // namespace Dali
+
+#endif // DALI_PLATFORM_TEXT_ABSTRACTION_FONT_FILE_MANAGER_H
index 37f86966e57d9fe5035f9fd9441348f5a3181a38..5fa10f52690643f9bdce908afbb982bdab5aa679 100644 (file)
@@ -54,15 +54,13 @@ Length Shaping::Shape(TextAbstraction::FontClient& fontClient,
                       const Character*       const text,
                       Length                       numberOfCharacters,
                       FontId                       fontId,
-                      Script                       script,
-                      Property::Map*               variationsMapPtr)
+                      Script                       script)
 {
   return GetImplementation(*this).Shape(fontClient,
                                         text,
                                         numberOfCharacters,
                                         fontId,
-                                        script,
-                                        variationsMapPtr);
+                                        script);
 }
 
 void Shaping::GetGlyphs(GlyphInfo*      glyphInfo,
index e41f0de8974cc65b761f02c7467695e439f93c1c..c7fe312a7d5ad8fe0ffcb35e20bf054d87791500 100644 (file)
@@ -109,7 +109,6 @@ public:
    * @param[in] numberOfCharacters The number of characters to be shaped
    * @param[in] fontId The font to be used to shape the text.
    * @param[in] script The text's script.
-   * @param[in] variationsMapPtr The variations used in variable fonts.
    *
    * @return The size of the buffer required to get the shaped text.
    */
@@ -117,8 +116,7 @@ public:
                const Character*       const text,
                Length                       numberOfCharacters,
                FontId                       fontId,
-               Script                       script,
-               Property::Map*               variationsMapPtr = nullptr);
+               Script                       script);
 
   /**
    * Gets the shaped text data.
index 83dde1ff6d9078cc0157f4cd56169abf795c98ba..7bce10ab8798d28fdf759f9d0fab37aeff3250e9 100644 (file)
@@ -21,6 +21,7 @@
 #include <dali/devel-api/text-abstraction/bidirectional-support.h>
 #include <dali/devel-api/text-abstraction/bitmap-font.h>
 #include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/devel-api/text-abstraction/font-file-manager.h>
 #include <dali/devel-api/text-abstraction/font-metrics.h>
 #include <dali/devel-api/text-abstraction/glyph-info.h>
 #include <dali/devel-api/text-abstraction/hyphenation.h>
index 050c1b1f25fad1b950aee416abd049537ff27d10..36b1b62c7bff177fa5f46e1c07983109be2a5f21 100644 (file)
@@ -168,6 +168,9 @@ namespace Adaptor
 
 #define DALI_ENV_ASYNC_MANAGER_LOW_PRIORITY_THREAD_POOL_SIZE "DALI_ASYNC_MANAGER_LOW_PRIORITY_THREAD_POOL_SIZE"
 
+// Face size Cache
+#define DALI_ENV_MAX_NUMBER_OF_FACE_SIZE_CACHE "DALI_FACE_SIZE_CACHE_MAX"
+
 // Glyph Cache
 #define DALI_ENV_MAX_NUMBER_OF_GLYPH_CACHE "DALI_GLYPH_CACHE_MAX"
 
index 96f01d20bf70eb1973ab693c4e64898e59c678b1..4d69a34ed6788765f59d4ea51e5dfe7fbdcad8b1 100644 (file)
@@ -3,6 +3,7 @@
 SET( adaptor_text_common_src_files
     ${adaptor_text_dir}/text-abstraction/bidirectional-support-impl.cpp
     ${adaptor_text_dir}/text-abstraction/font-client-impl.cpp
+    ${adaptor_text_dir}/text-abstraction/font-file-manager-impl.cpp
     ${adaptor_text_dir}/text-abstraction/segmentation-impl.cpp
     ${adaptor_text_dir}/text-abstraction/shaping-impl.cpp
     ${adaptor_text_dir}/text-abstraction/text-renderer-impl.cpp
@@ -14,6 +15,7 @@ SET( adaptor_text_common_src_files
     ${adaptor_text_dir}/text-abstraction/plugin/font-client-plugin-cache-handler.cpp
     ${adaptor_text_dir}/text-abstraction/plugin/font-client-plugin-impl.cpp
     ${adaptor_text_dir}/text-abstraction/plugin/font-face-cache-item.cpp
+    ${adaptor_text_dir}/text-abstraction/plugin/font-face-manager.cpp
     ${adaptor_text_dir}/text-abstraction/plugin/font-face-glyph-cache-manager.cpp
     ${adaptor_text_dir}/text-abstraction/plugin/harfbuzz-proxy-font.cpp
 )
index 6602627b47b6741fa52921506d194a5eae3073f3..2e6beab4ea3e32d5d12010794a0762dd8252405f 100644 (file)
@@ -101,9 +101,12 @@ Dali::TextAbstraction::FontClient FontClient::gPreCreatedFontClient(NULL);
 
 FontClient::FontClient()
 : mPlugin(nullptr),
+  mFontFileManager(),
   mDpiHorizontal(0),
   mDpiVertical(0)
 {
+  // FontFileManager::Get() must be called from the main thread.
+  mFontFileManager = TextAbstraction::FontFileManager::Get();
 }
 
 FontClient::~FontClient()
@@ -741,7 +744,7 @@ void FontClient::CreatePlugin()
   std::scoped_lock lock(gMutex);
   if(!mPlugin)
   {
-    mPlugin = new Plugin(mDpiHorizontal, mDpiVertical);
+    mPlugin = new Plugin(mFontFileManager, mDpiHorizontal, mDpiVertical);
   }
 }
 
index 267cb812e154b929c3099cdf4707fd2917c807c7..0713b14501691472ad9c20b0e433e40b90b6b7e3 100644 (file)
@@ -23,6 +23,7 @@
 
 // INTERNAL INCLUDES
 #include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/devel-api/text-abstraction/font-file-manager.h>
 
 struct FT_FaceRec_;
 
@@ -399,6 +400,8 @@ private:
   struct Plugin;
   Plugin* mPlugin;
 
+  TextAbstraction::FontFileManager mFontFileManager;
+
   // Allows DPI to be set without loading plugin
   unsigned int mDpiHorizontal;
   unsigned int mDpiVertical;
diff --git a/dali/internal/text/text-abstraction/font-file-manager-impl.cpp b/dali/internal/text/text-abstraction/font-file-manager-impl.cpp
new file mode 100644 (file)
index 0000000..e921b7e
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+// CLASS  HEADER
+#include <dali/internal/text/text-abstraction/font-file-manager-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/common/singleton-service.h>
+#include <dali/integration-api/debug.h>
+#include <unordered_map>
+
+namespace
+{
+#if defined(DEBUG_ENABLED)
+Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_FILE_MANAGER");
+#endif
+
+} // namespace
+
+namespace Dali
+{
+namespace TextAbstraction
+{
+namespace Internal
+{
+namespace
+{
+
+} // namespace
+
+// For Font Pre-load.
+Dali::TextAbstraction::FontFileManager FontFileManager::gFontFileManager(nullptr);
+
+struct FontFileManager::Plugin
+{
+  Plugin()
+  : mFontFileCache()
+  {
+  }
+
+  ~Plugin()
+  {
+  }
+
+  bool FindFontFile(const FontPath& fontPath) const
+  {
+    return mFontFileCache.find(fontPath) != mFontFileCache.end();
+  }
+
+  bool FindFontFile(const FontPath& fontPath, Dali::Any& fontFilePtr, std::streampos& fileSize) const
+  {
+    auto it = mFontFileCache.find(fontPath);
+    if(it != mFontFileCache.end())
+    {
+      fontFilePtr = AnyCast<uint8_t*>(it->second.first.Begin());
+      fileSize    = it->second.second;
+      return true;
+    }
+    return false;
+  }
+
+  void CacheFontFile(const FontPath& fontPath, Dali::Vector<uint8_t>&& fontFileBuffer, const std::streampos& fileSize)
+  {
+    mFontFileCache[fontPath] = std::make_pair(std::move(fontFileBuffer), fileSize);
+  }
+
+  void ClearCache()
+  {
+    mFontFileCache.clear();
+  }
+
+  std::unordered_map<FontPath, std::pair<Dali::Vector<uint8_t>, std::streampos>> mFontFileCache; ///< Caches font data with each font path as the key, allowing faster loading of fonts later on.
+};
+
+FontFileManager::FontFileManager()
+ : mPlugin(std::make_unique<Plugin>())
+{
+}
+
+FontFileManager::~FontFileManager()
+{
+}
+
+TextAbstraction::FontFileManager FontFileManager::Get()
+{
+  TextAbstraction::FontFileManager fontFileManagerHandle;
+
+  SingletonService service(SingletonService::Get());
+  if(service)
+  {
+    // Check whether the singleton is already created
+    Dali::BaseHandle handle = service.GetSingleton(typeid(TextAbstraction::FontFileManager));
+    if(handle)
+    {
+      // If so, downcast the handle
+      FontFileManager* impl = dynamic_cast<Internal::FontFileManager*>(handle.GetObjectPtr());
+      fontFileManagerHandle = TextAbstraction::FontFileManager(impl);
+    }
+    else // create and register the object
+    {
+      if(gFontFileManager)
+      {
+        fontFileManagerHandle = gFontFileManager;
+        gFontFileManager.Reset();
+      }
+      else
+      {
+        fontFileManagerHandle = TextAbstraction::FontFileManager(new FontFileManager);
+      }
+      service.Register(typeid(fontFileManagerHandle), fontFileManagerHandle);
+    }
+  }
+  else
+  {
+    if(!gFontFileManager)
+    {
+      // For Font Pre-load, global font client will use this.
+      gFontFileManager = Dali::TextAbstraction::FontFileManager(new FontFileManager);
+    }
+    return gFontFileManager;
+  }
+  return fontFileManagerHandle;
+}
+
+bool FontFileManager::FindFontFile(const FontPath& fontPath) const
+{
+  return mPlugin->FindFontFile(fontPath);
+}
+
+bool FontFileManager::FindFontFile(const FontPath& fontPath, Dali::Any& fontFilePtr, std::streampos& fileSize) const
+{
+  return mPlugin->FindFontFile(fontPath, fontFilePtr, fileSize);
+}
+
+void FontFileManager::CacheFontFile(const FontPath& fontPath, Dali::Vector<uint8_t>&& fontFileBuffer, const std::streampos& fileSize) const
+{
+  mPlugin->CacheFontFile(fontPath, std::move(fontFileBuffer), fileSize);
+}
+
+void FontFileManager::ClearCache()
+{
+  mPlugin->ClearCache();
+}
+
+} // namespace Internal
+
+} // namespace TextAbstraction
+
+} // namespace Dali
diff --git a/dali/internal/text/text-abstraction/font-file-manager-impl.h b/dali/internal/text/text-abstraction/font-file-manager-impl.h
new file mode 100644 (file)
index 0000000..9e73e0d
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef DALI_INTERNAL_TEXT_ABSTRACTION_FONT_FILE_MANAGER_IMPL_H
+#define DALI_INTERNAL_TEXT_ABSTRACTION_FONT_FILE_MANAGER_IMPL_H
+
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-object.h>
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-file-manager.h>
+
+namespace Dali
+{
+namespace TextAbstraction
+{
+namespace Internal
+{
+/**
+ * Implementation of the FontFileManager
+ */
+class FontFileManager : public BaseObject
+{
+public:
+  /**
+   * Constructor
+   */
+  FontFileManager();
+
+  /**
+   * Destructor
+   */
+  ~FontFileManager();
+
+  /**
+   * @copydoc Dali::FontFileManager::Get()
+   */
+  static TextAbstraction::FontFileManager Get();
+
+  /**
+   * @copydoc Dali::FontFileManager::FindFontFile()
+   */
+  bool FindFontFile(const FontPath& fontPath) const;
+
+  /**
+   * @copydoc Dali::FontFileManager::FindFontFile()
+   */
+  bool FindFontFile(const FontPath& fontPath, Dali::Any& fontFilePtr, std::streampos& fileSize) const;
+
+  /**
+   * @copydoc Dali::FontFileManager::CacheFontFile()
+   */
+  void CacheFontFile(const FontPath& fontPath, Dali::Vector<uint8_t>&& fontFileBuffer, const std::streampos& fileSize) const;
+
+  /**
+   * @copydoc Dali::FontFileManager::ClearCache()
+   */
+  void ClearCache();
+
+private:
+  // Undefined copy constructor.
+  FontFileManager(const FontFileManager&);
+
+  // Undefined assignment constructor.
+  FontFileManager& operator=(const FontFileManager&);
+
+  struct Plugin;
+  std::unique_ptr<Plugin> mPlugin;
+
+  static Dali::TextAbstraction::FontFileManager gFontFileManager;
+
+}; // class FontFileManager
+
+} // namespace Internal
+
+} // namespace TextAbstraction
+
+inline static TextAbstraction::Internal::FontFileManager& GetImplementation(TextAbstraction::FontFileManager& fontFileManager)
+{
+  DALI_ASSERT_ALWAYS(fontFileManager && "fontFileManager handle is empty");
+  BaseObject& handle = fontFileManager.GetBaseObject();
+  return static_cast<TextAbstraction::Internal::FontFileManager&>(handle);
+}
+
+inline static const TextAbstraction::Internal::FontFileManager& GetImplementation(const TextAbstraction::FontFileManager& fontFileManager)
+{
+  DALI_ASSERT_ALWAYS(fontFileManager && "fontFileManager handle is empty");
+  const BaseObject& handle = fontFileManager.GetBaseObject();
+  return static_cast<const TextAbstraction::Internal::FontFileManager&>(handle);
+}
+
+} // namespace Dali
+
+#endif // DALI_INTERNAL_TEXT_ABSTRACTION_FONT_FILE_MANAGER_IMPL_H
index c5373377e607eeb79520211a2256b2d21601f910..f0c4b88205960cea5697a70ed819c5eef3aaea30 100644 (file)
@@ -25,7 +25,6 @@
 
 // INTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/environment-variable.h>
-#include <dali/devel-api/adaptor-framework/file-loader.h>
 #include <dali/devel-api/adaptor-framework/image-loading.h>
 #include <dali/internal/system/common/environment-variables.h>
 #include <dali/internal/system/common/logging.h>
@@ -84,18 +83,39 @@ namespace
 {
 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_FONT_PERFORMANCE_MARKER, false);
 
+/**
+ * @brief Maximum size of face size cache.
+ */
+constexpr std::size_t DEFAULT_FACE_SIZE_CACHE_MAX         = 256u;
+constexpr std::size_t MINIMUM_SIZE_OF_FACE_SIZE_CACHE_MAX = 3u;
+
 /**
  * @brief Maximum size of glyph cache per each font face.
  */
-constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX         = 128;
+constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX         = 128u;
 constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u;
 
-constexpr std::size_t DEFAULT_DESCRIPTION_CACHE_MAX   = 1024;
-constexpr std::size_t MINIMUM_SIZE_OF_DESCRIPTION_CACHE_MAX = 3;
+constexpr std::size_t DEFAULT_DESCRIPTION_CACHE_MAX         = 1024u;
+constexpr std::size_t MINIMUM_SIZE_OF_DESCRIPTION_CACHE_MAX = 3u;
 
+constexpr auto MAX_NUMBER_OF_FACE_SIZE_CACHE_ENV = "DALI_FACE_SIZE_CACHE_MAX";
 constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
 constexpr auto MAX_NUMBER_OF_DESCRIPTION_CACHE_ENV = "DALI_DESCRIPTION_CACHE_MAX";
 
+
+/**
+ * @brief Get maximum size of face size cache size from environment.
+ * If not settuped, default as 256.
+ * @note This value fixed when we call it first time.
+ * @return The max size of face size cache.
+ */
+inline size_t GetMaxNumberOfFaceSizeCache()
+{
+  static auto numberString = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_MAX_NUMBER_OF_FACE_SIZE_CACHE);
+  static auto number       = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_FACE_SIZE_CACHE_MAX;
+  return (number < MINIMUM_SIZE_OF_FACE_SIZE_CACHE_MAX) ? MINIMUM_SIZE_OF_FACE_SIZE_CACHE_MAX : number;
+}
+
 /**
  * @brief Get maximum size of glyph cache size from environment.
  * If not settuped, default as 128.
@@ -306,11 +326,10 @@ FontClient::Plugin::CacheHandler::CacheHandler()
   mFontDescriptionCache(),
   mCharacterSetCache(),
   mFontDescriptionSizeCache(GetMaxNumberOfDescriptionCache()),
-  mFontDataCache(),
-  mFontFTFaceCache(),
   mEllipsisCache(),
   mEmbeddedItemCache(),
   mCustomFontDirectories(),
+  mFontFaceManager(new FontFaceManager(GetMaxNumberOfFaceSizeCache())),
   mGlyphCacheManager(new GlyphCacheManager(GetMaxNumberOfGlyphCache())),
   mLatestFoundFontDescription(),
   mLatestFoundFontDescriptionId(0u),
@@ -336,6 +355,9 @@ void FontClient::Plugin::CacheHandler::ClearCache()
   // delete cached glyph informations before clear mFontFaceCache.
   mGlyphCacheManager->ClearCache();
 
+  // delete cached face informations before clear mFontFaceCache.
+  mFontFaceManager->ClearCache();
+
   mDefaultFontDescription = FontDescription();
 
   mSystemFonts.clear();
@@ -360,13 +382,6 @@ void FontClient::Plugin::CacheHandler::ClearCache()
 
   mFontDescriptionSizeCache.Clear();
 
-  mFontDataCache.clear();
-  mFontDataCache.rehash(0);
-
-  ClearFTFaceFromFontFTFaceCache();
-  mFontFTFaceCache.clear();
-  mFontFTFaceCache.rehash(0);
-
   mEllipsisCache.clear();
   mPixelBufferCache.clear();
   mEmbeddedItemCache.clear();
@@ -384,6 +399,9 @@ void FontClient::Plugin::CacheHandler::ClearCacheOnLocaleChanged()
   // delete cached glyph informations before clear mFontFaceCache.
   mGlyphCacheManager->ClearCache();
 
+  // delete cached face informations before clear mFontFaceCache.
+  mFontFaceManager->ClearCache();
+
   mDefaultFontDescription = FontDescription();
 
   mSystemFonts.clear();
@@ -506,14 +524,6 @@ void FontClient::Plugin::CacheHandler::CreateCharacterSet()
   }
 }
 
-void FontClient::Plugin::CacheHandler::ClearFTFaceFromFontFTFaceCache()
-{
-  for(auto& item : mFontFTFaceCache)
-  {
-    FT_Done_Face(item.second);
-  }
-}
-
 // System / Default
 
 void FontClient::Plugin::CacheHandler::InitSystemFonts()
@@ -656,64 +666,6 @@ void FontClient::Plugin::CacheHandler::InitDefaultFontDescription()
   }
 }
 
-// Font
-
-bool FontClient::Plugin::CacheHandler::FindFontData(const std::string& fontPath) const
-{
-  auto it = mFontDataCache.find(fontPath);
-  if(it != mFontDataCache.end())
-  {
-    return true;
-  }
-
-  return false;
-}
-
-bool FontClient::Plugin::CacheHandler::FindFontData(const std::string& fontPath, uint8_t*& fontDataPtr, std::streampos& dataSize) const
-{
-  auto it = mFontDataCache.find(fontPath);
-  if(it != mFontDataCache.end())
-  {
-    fontDataPtr = it->second.first.Begin();
-    dataSize    = it->second.second;
-    return true;
-  }
-
-  return false;
-}
-
-bool FontClient::Plugin::CacheHandler::LoadFontDataFromFile(const std::string& fontPath, Dali::Vector<uint8_t>& fontDataBuffer, std::streampos& dataSize) const
-{
-  if(Dali::FileLoader::ReadFile(fontPath, dataSize, fontDataBuffer, Dali::FileLoader::BINARY))
-  {
-    FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "PreLoad font file buffer : %zu, size : %ld, path : %s\n", fontDataBuffer.Size(), static_cast<long>(dataSize), fontPath.c_str());
-    return true;
-  }
-
-  return false;
-}
-
-void FontClient::Plugin::CacheHandler::CacheFontData(const std::string& fontPath, Dali::Vector<uint8_t>&& fontDataBuffer, std::streampos& dataSize)
-{
-  mFontDataCache[fontPath] = std::make_pair(std::move(fontDataBuffer), dataSize);
-}
-
-bool FontClient::Plugin::CacheHandler::FindFontFace(const std::string& fontPath) const
-{
-  auto it = mFontFTFaceCache.find(fontPath);
-  if(it != mFontFTFaceCache.end())
-  {
-    return true;
-  }
-
-  return false;
-}
-
-void FontClient::Plugin::CacheHandler::CacheFontFace(const std::string& fontPath, FT_Face ftFace)
-{
-  mFontFTFaceCache[fontPath] = std::move(ftFace);
-}
-
 // Validate
 
 bool FontClient::Plugin::CacheHandler::FindValidatedFont(const FontDescription& fontDescription,
@@ -1027,7 +979,7 @@ void FontClient::Plugin::CacheHandler::CacheFontPath(FT_Face ftFace, FontId font
     FcCharSet* characterSet = nullptr;
     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
 
-    const FontCacheIndex fontCacheIndex          = FindFontIdCacheItem(fontId - 1u).index;
+    const FontCacheIndex fontCacheIndex                 = FindFontIdCacheItem(fontId - 1u).index;
     FindFontFaceCacheItem(fontCacheIndex).mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
 
     // Destroys the created patterns.
index 4fffd12e790d2b8b0ff619feeb25bf439ee010dc..1e1e1913470e9a0afc646793dce562b6973901ae 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-manager.h>
 #include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
 
 namespace Dali::TextAbstraction::Internal
@@ -161,11 +162,6 @@ private: // Clear cache private
    */
   void ClearCharacterSet();
 
-  /**
-   * @brief Free the resources allocated in FreeType face cache.
-   */
-  void ClearFTFaceFromFontFTFaceCache();
-
 private:
   /**
    * @brief Crate the charset resouces by default font and Fallback caches.
@@ -193,65 +189,6 @@ public: // Find & Cache
    */
   void InitDefaultFontDescription();
 
-  // Font
-
-  /**
-   * @brief Checks if font data for the specified font path is cached.
-   *
-   * @param[in] fontPath The font path to check for cached data.
-   *
-   * @return @e true if the font data is cached, otherwise false.
-   */
-  bool FindFontData(const std::string& fontPath) const;
-
-  /**
-   * @brief Retrieves font data for the specified font path if it is cached.
-   *
-   * @param[in] fontPath The font path to retrieve the cached data for.
-   * @param[out] fontDataPtr A pointer to the cached font data.
-   * @param[out] dataSize The size of the cached font data.
-   *
-   * @return @e true if the font data is cached and retrieved successfully, otherwise false.
-   */
-  bool FindFontData(const std::string& fontPath, uint8_t*& fontDataPtr, std::streampos& dataSize) const;
-
-  /**
-   * @brief Loads font data from the specified file path.
-   *
-   * @param[in] fontPath The file path to load the font data from.
-   * @param[out] fontDataBuffer A vector containing the loaded font data.
-   * @param[out] dataSize The size of the loaded font data.
-   *
-   * @return @e true if the font data was loaded successfully, otherwise false.
-   */
-  bool LoadFontDataFromFile(const std::string& fontPath, Dali::Vector<uint8_t>& fontDataBuffer, std::streampos& dataSize) const;
-
-  /**
-   * @brief Caches font data for the specified font path.
-   *
-   * @param[in] fontPath The font path to cache the data for.
-   * @param[in] fontDataBuffer A vector containing the font data to cache.
-   * @param[in] dataSize The size of the font data to cache.
-   */
-  void CacheFontData(const std::string& fontPath, Dali::Vector<uint8_t>&& fontDataBuffer, std::streampos& dataSize);
-
-  /**
-   * @brief Checks if FreeType face for the specified font path is cached.
-   *
-   * @param[in] fontPath The font path to check for cached face.
-   *
-   * @return @e true if the font face is cached, otherwise false.
-   */
-  bool FindFontFace(const std::string& fontPath) const;
-
-  /**
-   * @brief Caches FreeType face for the specified font path.
-   *
-   * @param[in] fontPath The font path to cache the face for.
-   * @param[in] ftFace The freetype font face to cache.
-   */
-  void CacheFontFace(const std::string& fontPath, FT_Face ftFace);
-
   // Validate
 
   /**
@@ -500,6 +437,11 @@ public: // Find & Cache
 
 
 public: // Other public API
+  FontFaceManager* GetFontFaceManager() const
+  {
+    return mFontFaceManager.get();
+  }
+
   GlyphCacheManager* GetGlyphCacheManager() const
   {
     return mGlyphCacheManager.get();
@@ -531,17 +473,15 @@ public:                                    // Cache container list
 
   DescriptionCacheContainer mFontDescriptionSizeCache; ///< LRU Cache container of glyph
 
-  std::unordered_map<std::string, std::pair<Dali::Vector<uint8_t>, std::streampos>> mFontDataCache;   ///< Caches font data with each font path as the key, allowing faster loading of fonts later on.
-  std::unordered_map<std::string, FT_Face>                                          mFontFTFaceCache; ///< Caches freetype font face for font pre-load.
-
   std::vector<EllipsisItem>         mEllipsisCache;     ///< Caches ellipsis glyphs for a particular point size.
   std::vector<PixelBufferCacheItem> mPixelBufferCache;  ///< Caches the pixel buffer of a url.
   std::vector<EmbeddedItem>         mEmbeddedItemCache; ///< Cache embedded items.
 
   FontPathList mCustomFontDirectories;  ///< Cache custom font directories to recovery upon reinitialization.
+  std::unique_ptr<FontFaceManager>   mFontFaceManager;   ///< The freetype font face manager. It will cache font face.
+  std::unique_ptr<GlyphCacheManager> mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs.
 
 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 c0dc05e3576e993b3b4eaffdab39a3d78a5343d6..06fbc6b24051814625d070528f7ba60c4c8b3069 100644 (file)
@@ -19,6 +19,7 @@
 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/file-loader.h>
 #include <dali/devel-api/text-abstraction/font-list.h>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/platform-abstraction.h>
@@ -118,22 +119,16 @@ namespace
  * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
  *
  * @param[in/out] ftFace Face type object.
- * @param[in] horizontalDpi The horizontal dpi.
- * @param[in] verticalDpi The vertical dpi.
+ * @param[in] fontFaceManager The fotn face manager.
  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
  * @param[in] requestedPointSize The requested point-size.
  * @return whether the  ftFace's block can fit into atlas
  */
-bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
+bool IsFitIntoAtlas(FT_Face& ftFace, FontFaceManager* fontFaceManager, int& error, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize, const std::size_t variationsHash, const std::vector<FT_Fixed>& freeTypeCoords)
 {
   bool isFit = false;
 
-  error = FT_Set_Char_Size(ftFace,
-                           0,
-                           FT_F26Dot6(requestedPointSize),
-                           horizontalDpi,
-                           verticalDpi);
-
+  error = fontFaceManager->ActivateFace(ftFace, requestedPointSize, variationsHash, freeTypeCoords);
   if(error == FT_Err_Ok)
   {
     //Check width and height of block for requestedPointSize
@@ -148,39 +143,22 @@ bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalD
 }
 
 /**
- * @brief Convert Freetype-type tag to string.
- *
- * @param[in] tag The Freetype variable tag.
- * @param[out] buffer The converted string tag.
- */
-void ConvertTagToString(FT_ULong tag, char buffer[5])
-{
-  // the tag is same format as used in Harfbuzz.
-  buffer[0] = (tag >> 24) & 0xFF;
-  buffer[1] = (tag >> 16) & 0xFF;
-  buffer[2] = (tag >> 8) & 0xFF;
-  buffer[3] = tag & 0xFF;
-  buffer[4] = 0;
-}
-
-/**
- * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
+ * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace .
  *
  * @param[in/out] ftFace Face type object.
- * @param[in] horizontalDpi The horizontal dpi.
- * @param[in] verticalDpi The vertical dpi.
+ * @param[in] fontFaceManager The font face manager.
  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
  * @param[in/out] requestedPointSize The requested point-size.
  * @return FreeType error code. 0 means success when requesting the nominal size (in points).
  */
-int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
+int SearchOnProperPointSize(FT_Face& ftFace, FontFaceManager* fontFaceManager, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize, const std::size_t variationsHash, const std::vector<FT_Fixed>& freeTypeCoords)
 {
   //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
   const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
   bool            canFitInAtlas;
   int             error; // FreeType error code.
 
-  canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
+  canFitInAtlas = IsFitIntoAtlas(ftFace, fontFaceManager, error, maxSizeFitInAtlas, requestedPointSize, variationsHash, freeTypeCoords);
   if(FT_Err_Ok != error)
   {
     return error;
@@ -194,7 +172,7 @@ int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi,
     while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit * exponentialDecrement)
     {
       requestedPointSize -= (pointSizePerOneUnit * exponentialDecrement);
-      canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
+      canFitInAtlas = IsFitIntoAtlas(ftFace, fontFaceManager, error, maxSizeFitInAtlas, requestedPointSize, variationsHash, freeTypeCoords);
       if(FT_Err_Ok != error)
       {
         return error;
@@ -222,7 +200,7 @@ int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi,
     while(minPointSize < maxPointSize)
     {
       requestedPointSize = ((maxPointSize / pointSizePerOneUnit - minPointSize / pointSizePerOneUnit) / 2) * pointSizePerOneUnit + minPointSize;
-      canFitInAtlas      = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
+      canFitInAtlas      = IsFitIntoAtlas(ftFace, fontFaceManager, error, maxSizeFitInAtlas, requestedPointSize, variationsHash, freeTypeCoords);
       if(FT_Err_Ok != error)
       {
         return error;
@@ -250,9 +228,11 @@ int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi,
 
 } // namespace
 
-FontClient::Plugin::Plugin(unsigned int horizontalDpi,
+FontClient::Plugin::Plugin(TextAbstraction::FontFileManager fontFileManager,
+                           unsigned int horizontalDpi,
                            unsigned int verticalDpi)
 : mFreeTypeLibrary(nullptr),
+  mFontFileManager(fontFileManager),
   mDpiHorizontal(horizontalDpi),
   mDpiVertical(verticalDpi),
   mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
@@ -266,6 +246,13 @@ FontClient::Plugin::Plugin(unsigned int horizontalDpi,
     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Init error: %d\n", error);
   }
 
+  mCacheHandler->mFontFaceManager->SetFontFileManager(fontFileManager);
+
+  if(horizontalDpi != 0u && verticalDpi != 0u)
+  {
+    mCacheHandler->mFontFaceManager->SetDpi(horizontalDpi, verticalDpi);
+  }
+
 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
   mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
 #endif
@@ -281,6 +268,8 @@ FontClient::Plugin::~Plugin()
 #endif
 
   FT_Done_FreeType(mFreeTypeLibrary);
+
+  mFontFileManager.ClearCache();
 }
 
 void FontClient::Plugin::ClearCache() const
@@ -298,6 +287,8 @@ void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
 {
   mDpiHorizontal = horizontalDpi;
   mDpiVertical   = verticalDpi;
+
+  mCacheHandler->mFontFaceManager->SetDpi(horizontalDpi, verticalDpi);
 }
 
 void FontClient::Plugin::ResetSystemDefaults() const
@@ -305,68 +296,63 @@ void FontClient::Plugin::ResetSystemDefaults() const
   mCacheHandler->ResetSystemDefaults();
 }
 
-void FontClient::Plugin::CacheFontDataFromFile(const std::string& fontPath) const
+void FontClient::Plugin::CacheFontDataFromFile(const FontPath& fontPath) const
 {
   if(fontPath.empty())
   {
     return;
   }
 
-  if(mCacheHandler->FindFontData(fontPath))
+  if(mFontFileManager.FindFontFile(fontPath))
   {
-    // Font data is already cached, no need to reload
+    // Font file is already cached, no need to reload
     return;
   }
 
-  Dali::Vector<uint8_t> fontDataBuffer;
-  std::streampos        dataSize = 0;
-  if(!mCacheHandler->LoadFontDataFromFile(fontPath, fontDataBuffer, dataSize))
+  Dali::Vector<uint8_t> fontFileBuffer;
+  std::streampos        fileSize = 0;
+
+  if(!Dali::FileLoader::ReadFile(fontPath, fileSize, fontFileBuffer, Dali::FileLoader::BINARY))
   {
-    fontDataBuffer.Clear();
-    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "Failed to load font data : %s\n", fontPath.c_str());
+    fontFileBuffer.Clear();
+    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "Failed to load font file : %s\n", fontPath.c_str());
     return;
   }
 
-  // Cache font data
-  mCacheHandler->CacheFontData(fontPath, std::move(fontDataBuffer), dataSize);
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "PreLoad font file buffer : %zu, size : %ld, path : %s\n", fontFileBuffer.Size(), static_cast<long>(fileSize), fontPath.c_str());
+
+  // Cache font file
+  mFontFileManager.CacheFontFile(fontPath, std::move(fontFileBuffer), fileSize);
 }
 
-void FontClient::Plugin::CacheFontFaceFromFile(const std::string& fontPath) const
+void FontClient::Plugin::CacheFontFaceFromFile(const FontPath& fontPath) const
 {
   if(fontPath.empty())
   {
     return;
   }
 
-  if(mCacheHandler->FindFontFace(fontPath))
-  {
-    // Font face is already cached, no need to reload
-    return;
-  }
-
-  FT_Face ftFace;
-  int     error = FT_New_Face(mFreeTypeLibrary, fontPath.c_str(), 0, &ftFace);
+  FT_Face  ftFace;
+  FT_Error error = mCacheHandler->mFontFaceManager->LoadFace(mFreeTypeLibrary, fontPath, 0, ftFace);
   if(FT_Err_Ok != error)
   {
     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "Failed to load font face : %s\n", fontPath.c_str());
     return;
   }
 
-  // Cache font face
-  mCacheHandler->CacheFontFace(fontPath, ftFace);
   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "PreLoad font new face : %s\n", fontPath.c_str());
 }
 
 void FontClient::Plugin::FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList) const
 {
-  for(const auto& fontPath : fontPathList)
+  for(const auto& memoryFontPath : memoryFontPathList)
   {
-    CacheFontFaceFromFile(fontPath);
+    CacheFontDataFromFile(memoryFontPath);
   }
 
-  for(const auto& memoryFontPath : memoryFontPathList)
+  for(const auto& fontPath : fontPathList)
   {
-    CacheFontDataFromFile(memoryFontPath);
+    CacheFontFaceFromFile(fontPath);
   }
 }
 
@@ -1233,110 +1219,20 @@ FontId FontClient::Plugin::CreateFont(const FontPath& path,
   FT_Face  ftFace;
   FT_Error error;
 
-  uint8_t*       fontDataPtr   = nullptr;
-  std::streampos dataSize      = 0;
-  bool           fontDataFound = mCacheHandler->FindFontData(path, fontDataPtr, dataSize);
-
-  if(fontDataFound)
-  {
-    // Create & cache new font face from pre-loaded font
-    error = FT_New_Memory_Face(mFreeTypeLibrary, reinterpret_cast<FT_Byte*>(fontDataPtr), static_cast<FT_Long>(dataSize), 0, &ftFace);
 #if defined(TRACE_ENABLED)
-    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-    {
-      DALI_LOG_DEBUG_INFO("DALI_TEXT_CREATE_FONT : FT_New_Memory_Face : %s\n", path.c_str());
-    }
-#endif
-  }
-  else
+  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
   {
-    // Create & cache new font face
-    error = FT_New_Face(mFreeTypeLibrary, path.c_str(), 0, &ftFace);
-#if defined(TRACE_ENABLED)
-    if(gTraceFilter && gTraceFilter->IsTraceEnabled())
-    {
-      DALI_LOG_DEBUG_INFO("DALI_TEXT_CREATE_FONT : FT_New_Face : %s\n", path.c_str());
-    }
-#endif
+    DALI_LOG_DEBUG_INFO("DALI_TEXT_CREATE_FONT : %s\n", path.c_str());
   }
-
+#endif
+  error = mCacheHandler->mFontFaceManager->LoadFace(mFreeTypeLibrary, path, 0, ftFace);
   if(FT_Err_Ok == error)
   {
-    // Check if a font is scalable.
-    const bool isScalable           = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
-    const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
-    const bool hasColorTables       = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
-
-    DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "            isScalable : [%s]\n", (isScalable ? "true" : "false"));
-    DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
-    DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "        hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
-
-    // Set Variable axes if applicable.
-    if(variationsMapPtr)
+    if(mCacheHandler->mFontFaceManager->IsBitmapFont(ftFace))
     {
-      Property::Map& variationsMap = *variationsMapPtr;
-
-      FT_MM_Var* mm_var;
-      error = FT_Get_MM_Var(ftFace, &mm_var);
+      const int fixedSizeIndex = mCacheHandler->mFontFaceManager->FindFixedSizeIndex(ftFace, requestedPointSize);
+      error                    = mCacheHandler->mFontFaceManager->SelectFixedSize(ftFace, requestedPointSize, fixedSizeIndex);
       if(FT_Err_Ok == error)
-      {
-        FT_Fixed* coordinates = new FT_Fixed[mm_var->num_axis];
-        for(uint32_t axisIndex = 0; axisIndex < mm_var->num_axis; axisIndex++)
-        {
-          char stringTag[FONT_AXIS_NAME_LEN + 1];
-          ConvertTagToString(mm_var->axis[axisIndex].tag, stringTag);
-          auto  valuePtr = variationsMap.Find(stringTag);
-          float value    = 0.0f;
-
-          if(valuePtr != nullptr && valuePtr->Get(value))
-          {
-            coordinates[axisIndex] = static_cast<FT_Fixed>(value * FROM_16DOT16);
-          }
-          else
-          {
-            // Set to default.
-            coordinates[axisIndex] = static_cast<FT_Fixed>(mm_var->axis[axisIndex].def);
-          }
-        }
-
-        FT_Set_Var_Design_Coordinates(ftFace, mm_var->num_axis, coordinates);
-        delete[] coordinates;
-      }
-    }
-
-    // Check to see if the font contains fixed sizes?
-    if(!isScalable && hasFixedSizedBitmaps)
-    {
-      PointSize26Dot6 actualPointSize = 0u;
-      int             fixedSizeIndex  = 0;
-      for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
-      {
-        const PointSize26Dot6 fixedSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
-        DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
-
-        if(fixedSize >= requestedPointSize)
-        {
-          actualPointSize = fixedSize;
-          break;
-        }
-      }
-
-      if(0u == actualPointSize)
-      {
-        // The requested point size is bigger than the bigest fixed size.
-        fixedSizeIndex  = ftFace->num_fixed_sizes - 1;
-        actualPointSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
-      }
-
-      DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
-
-      // Tell Freetype to use this size
-      error = FT_Select_Size(ftFace, fixedSizeIndex);
-      if(FT_Err_Ok != error)
-      {
-        DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
-      }
-      else
       {
         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
 
@@ -1346,17 +1242,32 @@ FontId FontClient::Plugin::CreateFont(const FontPath& path,
                             static_cast<float>(ftFace->underline_position) * FROM_266,
                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
 
-        const float fixedWidth  = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
-        const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
+        const float fixedWidth    = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
+        const float fixedHeight   = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
+        const bool hasColorTables = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
 
         // Create the FreeType font face item to cache.
-        FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables, (variationsMapPtr ? variationsMapPtr->GetHash() : 0u));
+        FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetFontFaceManager(), mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
 
         fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
+        mCacheHandler->mFontFaceManager->ReferenceFace(path);
+      }
+      else
+      {
+        DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType Select_Size error: %d for pointSize %d\n", error, requestedPointSize);
       }
     }
     else
     {
+      std::size_t                 variationsHash = 0u;
+      std::vector<FT_Fixed>       freeTypeCoords;
+      std::vector<hb_variation_t> harfBuzzVariations;
+      if(variationsMapPtr)
+      {
+        variationsHash = variationsMapPtr->GetHash();
+        mCacheHandler->mFontFaceManager->BuildVariations(ftFace, variationsMapPtr, freeTypeCoords, harfBuzzVariations);
+      }
+
       if(mIsAtlasLimitationEnabled)
       {
         //There is limitation on block size to fit in predefined atlas size.
@@ -1365,7 +1276,7 @@ FontId FontClient::Plugin::CreateFont(const FontPath& path,
         //Decrementing point-size until arriving to maximum allowed block size.
         auto        requestedPointSizeBackup = requestedPointSize;
         const Size& maxSizeFitInAtlas        = GetCurrentMaximumBlockSizeFitInAtlas();
-        error                                = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
+        error                                = SearchOnProperPointSize(ftFace, mCacheHandler->GetFontFaceManager(), maxSizeFitInAtlas, requestedPointSize, variationsHash, freeTypeCoords);
 
         if(requestedPointSize != requestedPointSizeBackup)
         {
@@ -1374,11 +1285,7 @@ FontId FontClient::Plugin::CreateFont(const FontPath& path,
       }
       else
       {
-        error = FT_Set_Char_Size(ftFace,
-                                 0,
-                                 FT_F26Dot6(requestedPointSize),
-                                 mDpiHorizontal,
-                                 mDpiVertical);
+        error = mCacheHandler->mFontFaceManager->ActivateFace(ftFace, requestedPointSize, variationsHash, freeTypeCoords);
       }
 
       if(FT_Err_Ok == error)
@@ -1392,9 +1299,10 @@ 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, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics);
+        FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetFontFaceManager(), mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics, variationsHash, std::move(freeTypeCoords), std::move(harfBuzzVariations));
 
         fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
+        mCacheHandler->mFontFaceManager->ReferenceFace(path);
       }
       else
       {
index d6711b98aa47b26210f367a87f614a706b8b8dce..1acb766f8879989804a067c9467ac1fca11ae969 100644 (file)
@@ -81,7 +81,7 @@ public: // Dali::TextAbstraction::FontClient
    * @param[in] horizontalDpi The horizontal dpi.
    * @param[in] verticalDpi The vertical dpi.
    */
-  Plugin(unsigned int horizontalDpi, unsigned int verticalDpi);
+  Plugin(TextAbstraction::FontFileManager fontFlieManager, unsigned int horizontalDpi, unsigned int verticalDpi);
 
   /**
    * Default destructor.
@@ -392,7 +392,7 @@ private:
    *
    * @param[in] fontPath The font path to cache the data for.
    */
-  void CacheFontDataFromFile(const std::string& fontPath) const;
+  void CacheFontDataFromFile(const FontPath& fontPath) const;
 
   /**
    * @brief Caches FreeType face for the specified font path if it is not already cached.
@@ -402,7 +402,7 @@ private:
    *
    * @param[in] fontPath The font path to cache the face for.
    */
-  void CacheFontFaceFromFile(const std::string& fontPath) const;
+  void CacheFontFaceFromFile(const FontPath& fontPath) const;
 
 private:
   Plugin(const Plugin&) = delete;
@@ -411,6 +411,8 @@ private:
 private:
   FT_Library mFreeTypeLibrary; ///< A handle to a FreeType library instance.
 
+  TextAbstraction::FontFileManager mFontFileManager; ///< A handle to FontFileManager.
+
   unsigned int mDpiHorizontal; ///< Horizontal dpi.
   unsigned int mDpiVertical;   ///< Vertical dpi.
 
index b544a80c2a7f0bc59d16e7f2b2f867b08727fe1a..9e47c61613922fcaa84e196774d07920086a3061 100644 (file)
@@ -88,15 +88,20 @@ inline GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy()
 }
 } // namespace
 
-FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
-                                     FT_Face            ftFace,
-                                     GlyphCacheManager* glyphCacheManager,
-                                     const FontPath&    path,
-                                     PointSize26Dot6    requestedPointSize,
-                                     FaceIndex          face,
-                                     const FontMetrics& metrics)
+FontFaceCacheItem::FontFaceCacheItem(const FT_Library&                  freeTypeLibrary,
+                                     FT_Face                            ftFace,
+                                     FontFaceManager*                   fontFaceManager,
+                                     GlyphCacheManager*                 glyphCacheManager,
+                                     const FontPath&                    path,
+                                     PointSize26Dot6                    requestedPointSize,
+                                     FaceIndex                          face,
+                                     const FontMetrics&                 metrics,
+                                     const std::size_t                  variationsHash,
+                                     const std::vector<FT_Fixed>&       freeTypeCoords,
+                                     const std::vector<hb_variation_t>& harfBuzzVariations)
 : mFreeTypeLibrary(freeTypeLibrary),
   mFreeTypeFace(ftFace),
+  mFontFaceManager(fontFaceManager),
   mGlyphCacheManager(glyphCacheManager),
   mHarfBuzzProxyFont(),
   mPath(path),
@@ -111,12 +116,15 @@ FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
   mFontId(0u),
   mIsFixedSizeBitmap(false),
   mHasColorTables(false),
-  mVariationsHash(0u)
+  mVariationsHash(variationsHash),
+  mFreeTypeCoords(freeTypeCoords),
+  mHarfBuzzVariations(harfBuzzVariations)
 {
 }
 
 FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
                                      FT_Face            ftFace,
+                                     FontFaceManager*   fontFaceManager,
                                      GlyphCacheManager* glyphCacheManager,
                                      const FontPath&    path,
                                      PointSize26Dot6    requestedPointSize,
@@ -125,10 +133,10 @@ FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
                                      int                fixedSizeIndex,
                                      float              fixedWidth,
                                      float              fixedHeight,
-                                     bool               hasColorTables,
-                                     std::size_t        variationsHash)
+                                     bool               hasColorTables)
 : mFreeTypeLibrary(freeTypeLibrary),
   mFreeTypeFace(ftFace),
+  mFontFaceManager(fontFaceManager),
   mGlyphCacheManager(glyphCacheManager),
   mHarfBuzzProxyFont(),
   mPath(path),
@@ -143,7 +151,9 @@ FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
   mFontId(0u),
   mIsFixedSizeBitmap(true),
   mHasColorTables(hasColorTables),
-  mVariationsHash(variationsHash)
+  mVariationsHash(0u),
+  mFreeTypeCoords(),
+  mHarfBuzzVariations()
 {
 }
 
@@ -153,6 +163,7 @@ FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs) noexcept
 : mFreeTypeLibrary(rhs.mFreeTypeLibrary)
 {
   mFreeTypeFace       = rhs.mFreeTypeFace;
+  mFontFaceManager    = rhs.mFontFaceManager;
   mGlyphCacheManager  = rhs.mGlyphCacheManager;
   mHarfBuzzProxyFont  = std::move(rhs.mHarfBuzzProxyFont);
   mPath               = std::move(rhs.mPath);
@@ -162,14 +173,20 @@ FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs) noexcept
   mCharacterSet       = rhs.mCharacterSet;
   mFixedSizeIndex     = rhs.mFixedSizeIndex;
   mFixedWidthPixels   = rhs.mFixedWidthPixels;
-  mFixedHeightPixels  = rhs.mFixedWidthPixels;
+  // Fixed height has been used as fixed width for a long time due to this typo.
+  // This fix may cause compatibility issue.
+  // mFixedHeightPixels  = rhs.mFixedWidthPixels;
+  mFixedHeightPixels  = rhs.mFixedHeightPixels;
   mVectorFontId       = rhs.mVectorFontId;
   mFontId             = rhs.mFontId;
   mIsFixedSizeBitmap  = rhs.mIsFixedSizeBitmap;
   mHasColorTables     = rhs.mHasColorTables;
   mVariationsHash     = rhs.mVariationsHash;
+  mFreeTypeCoords     = std::move(rhs.mFreeTypeCoords);
+  mHarfBuzzVariations = std::move(rhs.mHarfBuzzVariations);
 
   rhs.mFreeTypeFace      = nullptr;
+  rhs.mFontFaceManager   = nullptr;
   rhs.mGlyphCacheManager = nullptr;
 }
 
@@ -187,11 +204,13 @@ FontFaceCacheItem::~FontFaceCacheItem()
     mHarfBuzzProxyFont.reset();
   }
 
-  // Free face.
-  if(mFreeTypeFace)
+  if(mFontFaceManager)
   {
-    FT_Done_Face(mFreeTypeFace);
+    mFontFaceManager->ReleaseFace(mPath);
   }
+
+  mFreeTypeCoords.clear();
+  mHarfBuzzVariations.clear();
 }
 
 void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const
@@ -227,8 +246,13 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe
   // Check to see if we should be loading a Fixed Size bitmap?
   if(mIsFixedSizeBitmap)
   {
-    FT_Select_Size(mFreeTypeFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
-    mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, glyphDataPtr, error);
+    error = mFontFaceManager->SelectFixedSize(mFreeTypeFace, mRequestedPointSize, mFixedSizeIndex);
+    if(DALI_UNLIKELY(error != FT_Err_Ok))
+    {
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetGlyphMetrics. SelectFixedSize fail\n");
+    }
+
+    mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, mRequestedPointSize, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, mVariationsHash, glyphDataPtr, error);
 
     if(FT_Err_Ok == error)
     {
@@ -272,7 +296,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(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast<uint32_t>(glyphInfo.width), static_cast<uint32_t>(glyphInfo.height));
+          mGlyphCacheManager->ResizeBitmapGlyph(mFreeTypeFace, mRequestedPointSize, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, mVariationsHash, static_cast<uint32_t>(glyphInfo.width), static_cast<uint32_t>(glyphInfo.height));
         }
       }
     }
@@ -285,10 +309,16 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe
   else
 #endif
   {
+    error = mFontFaceManager->ActivateFace(mFreeTypeFace, mRequestedPointSize, mVariationsHash, mFreeTypeCoords);
+    if(DALI_UNLIKELY(error != FT_Err_Ok))
+    {
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetGlyphMetrics. ActivateFace fail\n");
+    }
+
     // 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(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphDataPtr, error);
+    mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, mRequestedPointSize, glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, mVariationsHash, glyphDataPtr, 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
@@ -320,7 +350,7 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe
       {
         // Get dummy glyph data without embolden.
         GlyphCacheManager::GlyphCacheDataPtr dummyDataPtr;
-        if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyDataPtr, error))
+        if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, mRequestedPointSize, glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, mVariationsHash, dummyDataPtr, error))
         {
           // If the glyph is emboldened by software, the advance is multiplied by a
           // scale factor to make it slightly bigger.
@@ -375,17 +405,29 @@ void FontFaceCacheItem::CreateBitmap(
   // Check to see if this is fixed size bitmap
   if(mIsFixedSizeBitmap)
   {
+    error = mFontFaceManager->SelectFixedSize(mFreeTypeFace, mRequestedPointSize, mFixedSizeIndex);
+    if(DALI_UNLIKELY(error != FT_Err_Ok))
+    {
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. SelectFixedSize fail\n");
+    }
+
     loadFlag = FT_LOAD_COLOR;
   }
   else
 #endif
   {
+    error = mFontFaceManager->ActivateFace(mFreeTypeFace, mRequestedPointSize, mVariationsHash, mFreeTypeCoords);
+    if(DALI_UNLIKELY(error != FT_Err_Ok))
+    {
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. ActivateFace fail\n");
+    }
+
     // 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?
     loadFlag = FT_LOAD_NO_AUTOHINT;
   }
-  mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, glyphDataPtr, error);
+  mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, mRequestedPointSize, glyphIndex, loadFlag, isBoldRequired, mVariationsHash, glyphDataPtr, error);
 
   if(FT_Err_Ok == error)
   {
@@ -492,10 +534,10 @@ void FontFaceCacheItem::CreateBitmap(
           // Note : We will call this API once per each glyph.
           if(ableUseCachedRenderedGlyph)
           {
-            mGlyphCacheManager->CacheRenderedGlyphBuffer(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
+            mGlyphCacheManager->CacheRenderedGlyphBuffer(mFreeTypeFace, mRequestedPointSize, glyphIndex, loadFlag, isBoldRequired, mVariationsHash, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
 
             GlyphCacheManager::GlyphCacheDataPtr dummyDataPtr;
-            mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, dummyDataPtr, error);
+            mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, mRequestedPointSize, glyphIndex, loadFlag, isBoldRequired, mVariationsHash, dummyDataPtr, error);
 
             if(DALI_LIKELY(FT_Err_Ok == error && dummyDataPtr->mRenderedBuffer))
             {
@@ -550,7 +592,7 @@ bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
   if(mHasColorTables)
   {
     GlyphCacheManager::GlyphCacheDataPtr dummyDataPtr;
-    mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR, false, dummyDataPtr, error);
+    mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, mRequestedPointSize, glyphIndex, FT_LOAD_COLOR, false, mVariationsHash, dummyDataPtr, error);
   }
 #endif
   return FT_Err_Ok == error;
@@ -567,7 +609,6 @@ bool FontFaceCacheItem::IsCharacterSupported(FcConfig* fontConfig, Character cha
   {
     // Create again the character set.
     // It can be null if the ResetSystemDefaults() method has been called.
-
     FontDescription description;
     description.path   = mPath;
     description.family = std::move(FontFamily(mFreeTypeFace->family_name));
@@ -606,7 +647,24 @@ 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));
+    mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, mVariationsHash, mHarfBuzzVariations, horizontalDpi, verticalDpi, mGlyphCacheManager));
+  }
+
+  if(mIsFixedSizeBitmap)
+  {
+    FT_Error error = mFontFaceManager->SelectFixedSize(mFreeTypeFace, mRequestedPointSize, mFixedSizeIndex);
+    if(DALI_UNLIKELY(error != FT_Err_Ok))
+    {
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetHarfBuzzFont. SelectFixedSize fail\n");
+    }
+  }
+  else
+  {
+    FT_Error error = mFontFaceManager->ActivateFace(mFreeTypeFace, mRequestedPointSize, mVariationsHash, mFreeTypeCoords);
+    if(DALI_UNLIKELY(error != FT_Err_Ok))
+    {
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetHarfBuzzFont. ActivateFace fail\n");
+    }
   }
   return mHarfBuzzProxyFont->GetHarfBuzzFont();
 }
index 3ca2b13173f67068c149f112f57474088acc8666..5cd55c2e638ec789d0765d58fda719b557cf0665 100644 (file)
@@ -19,6 +19,7 @@
 
 // INTERNAL INCLUDES
 #include <dali/internal/text/text-abstraction/plugin/font-cache-item-interface.h>
+#include <dali/internal/text/text-abstraction/plugin/font-face-manager.h>
 #include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
 #include <dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h>
 
@@ -41,16 +42,21 @@ namespace Dali::TextAbstraction::Internal
  */
 struct FontFaceCacheItem : public FontCacheItemInterface
 {
-  FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
-                    FT_Face            ftFace,
-                    GlyphCacheManager* glyphCacheManager,
-                    const FontPath&    path,
-                    PointSize26Dot6    requestedPointSize,
-                    FaceIndex          face,
-                    const FontMetrics& metrics);
+  FontFaceCacheItem(const FT_Library&                  freeTypeLibrary,
+                    FT_Face                            ftFace,
+                    FontFaceManager*                   fontFaceManager,
+                    GlyphCacheManager*                 glyphCacheManager,
+                    const FontPath&                    path,
+                    PointSize26Dot6                    requestedPointSize,
+                    FaceIndex                          face,
+                    const FontMetrics&                 metrics,
+                    const std::size_t                  variationsHash,
+                    const std::vector<FT_Fixed>&       freeTypeCoords,
+                    const std::vector<hb_variation_t>& harfBuzzVariations);
 
   FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
                     FT_Face            ftFace,
+                    FontFaceManager*   fontFaceManager,
                     GlyphCacheManager* glyphCacheManager,
                     const FontPath&    path,
                     PointSize26Dot6    requestedPointSize,
@@ -59,8 +65,7 @@ struct FontFaceCacheItem : public FontCacheItemInterface
                     int                fixedSizeIndex,
                     float              fixedWidth,
                     float              fixedHeight,
-                    bool               hasColorTables,
-                    std::size_t        variationsHash = 0u);
+                    bool               hasColorTables);
 
   FontFaceCacheItem(const FontFaceCacheItem& rhs) = delete; // Do not use copy construct
   FontFaceCacheItem(FontFaceCacheItem&& rhs) noexcept;
@@ -135,22 +140,25 @@ public:
   const FT_Library& mFreeTypeLibrary; ///< A handle to a FreeType library instance.
   FT_Face           mFreeTypeFace;    ///< The FreeType face.
 
+  FontFaceManager*                   mFontFaceManager;   ///< The reference of font face manager. Owned from font-client-plugin-cache-handler.
   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.
-  PointSize26Dot6 mRequestedPointSize;    ///< The font point size.
-  FaceIndex       mFaceIndex;             ///< The face index.
-  FontMetrics     mMetrics;               ///< The font metrics.
-  _FcCharSet*     mCharacterSet;          ///< Pointer with the range of characters.
-  int             mFixedSizeIndex;        ///< Index to the fixed size table for the requested size.
-  float           mFixedWidthPixels;      ///< The height in pixels (fixed size bitmaps only)
-  float           mFixedHeightPixels;     ///< The height in pixels (fixed size bitmaps only)
-  unsigned int    mVectorFontId;          ///< The ID of the equivalent vector-based font
-  FontId          mFontId;                ///< Index to the vector with the cache of font's ids.
-  bool            mIsFixedSizeBitmap : 1; ///< Whether the font has fixed size bitmaps.
-  bool            mHasColorTables : 1;    ///< Whether the font has color tables.
-  std::size_t     mVariationsHash;
+  FontPath                    mPath;                  ///< The path to the font file name.
+  PointSize26Dot6             mRequestedPointSize;    ///< The font point size.
+  FaceIndex                   mFaceIndex;             ///< The face index.
+  FontMetrics                 mMetrics;               ///< The font metrics.
+  _FcCharSet*                 mCharacterSet;          ///< Pointer with the range of characters.
+  int                         mFixedSizeIndex;        ///< Index to the fixed size table for the requested size.
+  float                       mFixedWidthPixels;      ///< The height in pixels (fixed size bitmaps only)
+  float                       mFixedHeightPixels;     ///< The height in pixels (fixed size bitmaps only)
+  unsigned int                mVectorFontId;          ///< The ID of the equivalent vector-based font
+  FontId                      mFontId;                ///< Index to the vector with the cache of font's ids.
+  bool                        mIsFixedSizeBitmap : 1; ///< Whether the font has fixed size bitmaps.
+  bool                        mHasColorTables : 1;    ///< Whether the font has color tables.
+  std::size_t                 mVariationsHash;        ///< The hash of the variations to use key.
+  std::vector<FT_Fixed>       mFreeTypeCoords;        ///< The FreeType coordinates for the variations.
+  std::vector<hb_variation_t> mHarfBuzzVariations;    ///< The HarfBuzz variations data.
 };
 
 } // namespace Dali::TextAbstraction::Internal
index 7052b30c36335d2021cc7afdb5adb0fbd3c097fe..2042d5980cd1428126389e0601894842b7139811 100644 (file)
@@ -49,17 +49,19 @@ GlyphCacheManager::~GlyphCacheManager()
 }
 
 bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
-  const FT_Face      freeTypeFace,
-  const GlyphIndex   index,
-  const FT_Int32     flag,
-  const bool         isBoldRequired,
-  GlyphCacheDataPtr& glyphDataPtr,
-  FT_Error&          error)
+  const FT_Face         freeTypeFace,
+  const PointSize26Dot6 requestedPointSize,
+  const GlyphIndex      index,
+  const FT_Int32        flag,
+  const bool            isBoldRequired,
+  const std::size_t     variationsHash,
+  GlyphCacheDataPtr&    glyphDataPtr,
+  FT_Error&             error)
 {
   // Append some error value here instead of FT_Err_Ok.
   error = static_cast<FT_Error>(-1);
 
-  const GlyphCacheKey key  = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired);
+  const GlyphCacheKey key  = GlyphCacheKey(freeTypeFace, requestedPointSize, index, flag, isBoldRequired, variationsHash);
   auto                iter = mLRUGlyphCache.Find(key);
 
   if(iter == mLRUGlyphCache.End())
@@ -202,12 +204,14 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex(
 }
 
 void GlyphCacheManager::ResizeBitmapGlyph(
-  const FT_Face    freeTypeFace,
-  const GlyphIndex index,
-  const FT_Int32   flag,
-  const bool       isBoldRequired,
-  const uint32_t   desiredWidth,
-  const uint32_t   desiredHeight)
+  const FT_Face         freeTypeFace,
+  const PointSize26Dot6 requestedPointSize,
+  const GlyphIndex      index,
+  const FT_Int32        flag,
+  const bool            isBoldRequired,
+  const std::size_t     variationsHash,
+  const uint32_t        desiredWidth,
+  const uint32_t        desiredHeight)
 {
   if(desiredWidth * desiredHeight <= 0)
   {
@@ -216,7 +220,7 @@ void GlyphCacheManager::ResizeBitmapGlyph(
   }
   FT_Error          error;
   GlyphCacheDataPtr glyphDataPtr;
-  if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphDataPtr, error))
+  if(GetGlyphCacheDataFromIndex(freeTypeFace, requestedPointSize, index, flag, isBoldRequired, variationsHash, glyphDataPtr, error))
   {
     GlyphCacheData& glyphData = *glyphDataPtr.get();
     if(DALI_LIKELY(glyphData.mIsBitmap && glyphData.mBitmap))
@@ -319,9 +323,11 @@ void GlyphCacheManager::ResizeBitmapGlyph(
 
 void GlyphCacheManager::CacheRenderedGlyphBuffer(
   const FT_Face               freeTypeFace,
+  const PointSize26Dot6       requestedPointSize,
   const GlyphIndex            index,
   const FT_Int32              flag,
   const bool                  isBoldRequired,
+  const std::size_t           variationsHash,
   const FT_Bitmap&            srcBitmap,
   const CompressionPolicyType policy)
 {
@@ -332,7 +338,7 @@ void GlyphCacheManager::CacheRenderedGlyphBuffer(
   }
   FT_Error          error;
   GlyphCacheDataPtr glyphDataPtr;
-  if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphDataPtr, error))
+  if(GetGlyphCacheDataFromIndex(freeTypeFace, requestedPointSize, index, flag, isBoldRequired, variationsHash, glyphDataPtr, error))
   {
     GlyphCacheData& glyphData = *glyphDataPtr.get();
     if(DALI_LIKELY(!glyphData.mIsBitmap && glyphData.mRenderedBuffer == nullptr))
index 521c37340e1f983783dde0dd64eb756e98371493..8bc7266c606220f3c03d334261b84393a114227a 100644 (file)
@@ -24,6 +24,8 @@
 
 // EXTERNAL INCLUDES
 #include <memory> // for std::shared_ptr
+#include <map>
+#include <fontconfig/fontconfig.h>
 
 #include <ft2build.h>
 #include FT_FREETYPE_H
@@ -101,20 +103,24 @@ public:
    * @note Inputed glyph data pointer will be overwrited.
    *
    * @param[in] freeTypeFace The freetype face handle.
+   * @param[in] requestedPointSize The requested point size.
    * @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.
+   * @param[in] variationsHash The hash of the variations to use key.
    * @param[out] glyphDataPtr Result of pointer of glyph load.
    * @param[out] error Error code during load glyph.
    * @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,
-    GlyphCacheDataPtr& glyphDataPtr,
-    FT_Error&          error);
+    const FT_Face         freeTypeFace,
+    const PointSize26Dot6 requestedPointSize,
+    const GlyphIndex      index,
+    const FT_Int32        flag,
+    const bool            isBoldRequired,
+    const std::size_t     variationsHash,
+    GlyphCacheDataPtr&    glyphDataPtr,
+    FT_Error&             error);
 
   /**
    * @brief Load GlyphCacheData from face. The result will not be cached.
@@ -140,36 +146,44 @@ public:
    * If glyph is not bitmap glyph, nothing happened.
    *
    * @param[in] freeTypeFace The freetype face handle.
+   * @param[in] requestedPointSize The requested point size.
    * @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.
+   * @param[in] variationsHash The hash of the variations to use key.
    * @param[in] desiredWidth Desired width of bitmap.
    * @param[in] desiredHeight Desired height of bitmap.
    */
   void ResizeBitmapGlyph(
-    const FT_Face    freeTypeFace,
-    const GlyphIndex index,
-    const FT_Int32   flag,
-    const bool       isBoldRequired,
-    const uint32_t   desiredWidth,
-    const uint32_t   desiredHeight);
+    const FT_Face         freeTypeFace,
+    const PointSize26Dot6 requestedPointSize,
+    const GlyphIndex      index,
+    const FT_Int32        flag,
+    const bool            isBoldRequired,
+    const std::size_t     variationsHash,
+    const uint32_t        desiredWidth,
+    const uint32_t        desiredHeight);
 
   /**
    * @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] requestedPointSize The requested point size.
    * @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.
+   * @param[in] variationsHash The hash of the variations to use key.
    * @param[in] srcBitmap Rendered glyph bitmap.
    * @param[in] policy Compress behavior policy.
    */
   void CacheRenderedGlyphBuffer(
     const FT_Face               freeTypeFace,
+    const PointSize26Dot6       requestedPointSize,
     const GlyphIndex            index,
     const FT_Int32              flag,
     const bool                  isBoldRequired,
+    const std::size_t           variationsHash,
     const FT_Bitmap&            srcBitmap,
     const CompressionPolicyType policy);
 
@@ -197,28 +211,34 @@ private:
   {
     GlyphCacheKey()
     : mFreeTypeFace(nullptr),
+      mRequestedPointSize(0),
       mIndex(0u),
       mFlag(0),
-      mIsBoldRequired(false)
+      mIsBoldRequired(false),
+      mVariationsHash(0u)
     {
     }
 
-    GlyphCacheKey(const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool boldRequired)
+    GlyphCacheKey(const FT_Face freeTypeFace, const PointSize26Dot6 requestedPointSize, const GlyphIndex index, const FT_Int32 flag, const bool boldRequired, const std::size_t variationsHash)
     : mFreeTypeFace(freeTypeFace),
+      mRequestedPointSize(requestedPointSize),
       mIndex(index),
       mFlag(flag),
-      mIsBoldRequired(boldRequired)
+      mIsBoldRequired(boldRequired),
+      mVariationsHash(variationsHash)
     {
     }
 
-    FT_Face    mFreeTypeFace;
-    GlyphIndex mIndex;
-    FT_Int32   mFlag;
-    bool       mIsBoldRequired : 1;
+    FT_Face         mFreeTypeFace;
+    PointSize26Dot6 mRequestedPointSize;
+    GlyphIndex      mIndex;
+    FT_Int32        mFlag;
+    bool            mIsBoldRequired : 1;
+    std::size_t     mVariationsHash;
 
     bool operator==(GlyphCacheKey const& rhs) const noexcept
     {
-      return mFreeTypeFace == rhs.mFreeTypeFace && mIndex == rhs.mIndex && mFlag == rhs.mFlag && mIsBoldRequired == rhs.mIsBoldRequired;
+      return mFreeTypeFace == rhs.mFreeTypeFace && mRequestedPointSize == rhs.mRequestedPointSize && mIndex == rhs.mIndex && mFlag == rhs.mFlag && mIsBoldRequired == rhs.mIsBoldRequired && mVariationsHash == rhs.mVariationsHash;
     }
   };
 
@@ -230,9 +250,11 @@ private:
     std::size_t operator()(GlyphCacheKey const& key) const noexcept
     {
       return static_cast<std::size_t>(reinterpret_cast<std::uintptr_t>(key.mFreeTypeFace)) ^
+             static_cast<std::size_t>(key.mRequestedPointSize) ^
              static_cast<std::size_t>(key.mIndex) ^
              static_cast<std::size_t>(key.mFlag) ^
-             (static_cast<std::size_t>(key.mIsBoldRequired) << 29);
+             (static_cast<std::size_t>(key.mIsBoldRequired) << 29) ^
+             key.mVariationsHash;
     }
   };
 
diff --git a/dali/internal/text/text-abstraction/plugin/font-face-manager.cpp b/dali/internal/text/text-abstraction/plugin/font-face-manager.cpp
new file mode 100644 (file)
index 0000000..36e10b4
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+// CLASS HEADER
+#include <dali/internal/text/text-abstraction/plugin/font-face-manager.h>
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/any.h>
+
+#if defined(DEBUG_ENABLED)
+extern Dali::Integration::Log::Filter* gFontClientLogFilter;
+#endif
+
+namespace Dali::TextAbstraction::Internal
+{
+namespace
+{
+const uint32_t FONT_AXIS_NAME_LEN = 4;
+const uint32_t FROM_16DOT16       = (1 << 16);
+
+/**
+ * @brief Convert FreeType-type tag to string.
+ *
+ * @param[in] tag The FreeType variable tag.
+ * @param[out] buffer The converted string tag.
+ */
+void ConvertTagToString(FT_ULong tag, char buffer[5])
+{
+  // the tag is same format as used in Harfbuzz.
+  buffer[0] = (tag >> 24) & 0xFF;
+  buffer[1] = (tag >> 16) & 0xFF;
+  buffer[2] = (tag >> 8) & 0xFF;
+  buffer[3] = tag & 0xFF;
+  buffer[4] = 0;
+}
+} // namespace
+
+FontFaceManager::FontFaceManager(std::size_t maxNumberOfFaceSizeCache)
+: mMaxNumberOfFaceSizeCache(maxNumberOfFaceSizeCache),
+  mLRUFaceSizeCache(mMaxNumberOfFaceSizeCache),
+  mFontFileManager(),
+  mFreeTypeFaces(),
+  mActivatedSizes(),
+  mSelectedIndices(),
+  mDpiHorizontal(0u),
+  mDpiVertical(0u)
+{
+  DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::FontFaceManager Create with maximum size : %d\n", static_cast<int>(mMaxNumberOfFaceSizeCache));
+}
+
+FontFaceManager::~FontFaceManager()
+{
+  ClearCache();
+}
+
+void FontFaceManager::SetFontFileManager(TextAbstraction::FontFileManager fontFileManager)
+{
+  mFontFileManager = fontFileManager;
+}
+
+void FontFaceManager::SetDpi(const uint32_t dpiHorizontal, const uint32_t dpiVertical)
+{
+  mDpiHorizontal = dpiHorizontal;
+  mDpiVertical   = dpiVertical;
+}
+
+FT_Error FontFaceManager::LoadFace(const FT_Library& freeTypeLibrary, const FontPath& fontPath, const FaceIndex faceIndex, FT_Face& ftFace)
+{
+  FT_Error error;
+
+  auto iter = mFreeTypeFaces.find(fontPath);
+  if(iter != mFreeTypeFaces.end())
+  {
+    ftFace = iter->second.mFreeTypeFace;
+    error = FT_Err_Ok;
+  }
+  else
+  {
+    Dali::Any      fontFilePtr   = nullptr;
+    std::streampos fileSize      = 0;
+    bool           fontFileFound = mFontFileManager.FindFontFile(fontPath, fontFilePtr, fileSize);
+
+    if(fontFileFound)
+    {
+      error = FT_New_Memory_Face(freeTypeLibrary, reinterpret_cast<FT_Byte*>(AnyCast<uint8_t*>(fontFilePtr)), static_cast<FT_Long>(fileSize), 0, &ftFace);
+      DALI_LOG_DEBUG_INFO("FontFaceManager, FT_New_Memory_Face : %s\n", fontPath.c_str());
+    }
+    else
+    {
+      error = FT_New_Face(freeTypeLibrary, fontPath.c_str(), faceIndex, &ftFace);
+      DALI_LOG_DEBUG_INFO("FontFaceManager, FT_New_Face : %s\n", fontPath.c_str());
+    }
+
+    if(DALI_UNLIKELY(error != FT_Err_Ok))
+    {
+      DALI_LOG_ERROR("Load freetype face fail, error code:0x%02X, memory face:%d\n", error, fontFileFound);
+    }
+    else
+    {
+      mFreeTypeFaces[fontPath] = std::move(FaceCacheData(ftFace));
+    }
+  }
+  return error;
+}
+
+void FontFaceManager::ReferenceFace(const FontPath& fontPath)
+{
+  auto iter = mFreeTypeFaces.find(fontPath);
+  if(iter != mFreeTypeFaces.end())
+  {
+    iter->second.mReference++;
+  }
+}
+
+void FontFaceManager::ReleaseFace(const FontPath& fontPath)
+{
+  if(fontPath.empty())
+  {
+    return;
+  }
+
+  auto iter = mFreeTypeFaces.find(fontPath);
+  if(iter != mFreeTypeFaces.end())
+  {
+    iter->second.mReference--;
+    if(iter->second.mReference <= 0)
+    {
+      // TODO:
+      // Currently FontFaceCacheItem can be removed from LRU Cache regardless of whether the text control is referenced or not.
+      // This will cause a crash when the freetype face is destroyed at this time.
+      // We should remove this comment after fixing the FontFaceCacheItem issue.
+      // iter->second.ReleaseData();
+      // mFreeTypeFaces.erase(iter);
+    }
+  }
+}
+
+void FontFaceManager::BuildVariations(FT_Face ftFace, const Property::Map* variationsMapPtr, std::vector<FT_Fixed>& freeTypeCoords, std::vector<hb_variation_t>& harfBuzzVariations)
+{
+  if(variationsMapPtr)
+  {
+    FT_MM_Var* mm_var;
+    FT_Error   error = FT_Get_MM_Var(ftFace, &mm_var);
+    if(DALI_UNLIKELY(error != FT_Err_Ok))
+    {
+      DALI_LOG_ERROR("FT_Get_MM_Var fail, error code:0x%02X\n", error);
+      return;
+    }
+
+    const Property::Map& variationsMap = *variationsMapPtr;
+
+    freeTypeCoords.resize(mm_var->num_axis);
+    harfBuzzVariations.resize(mm_var->num_axis);
+
+    for(uint32_t axisIndex = 0; axisIndex < mm_var->num_axis; axisIndex++)
+    {
+      char stringTag[FONT_AXIS_NAME_LEN + 1];
+      ConvertTagToString(mm_var->axis[axisIndex].tag, stringTag);
+      auto  valuePtr = variationsMap.Find(stringTag);
+      float value    = 0.0f;
+
+      if(valuePtr != nullptr && valuePtr->Get(value))
+      {
+        freeTypeCoords[axisIndex] = static_cast<FT_Fixed>(value * FROM_16DOT16);
+      }
+      else
+      {
+        freeTypeCoords[axisIndex] = mm_var->axis[axisIndex].def;
+        value                     = freeTypeCoords[axisIndex] / FROM_16DOT16;
+      }
+
+      hb_variation_t harfBuzzVariation;
+      harfBuzzVariation.tag         = mm_var->axis[axisIndex].tag;
+      harfBuzzVariation.value       = value;
+      harfBuzzVariations[axisIndex] = harfBuzzVariation;
+    }
+  }
+}
+
+FT_Error FontFaceManager::ActivateFace(FT_Face ftFace, const PointSize26Dot6 requestedPointSize, const std::size_t variationsHash, const std::vector<FT_Fixed>& freeTypeCoords)
+{
+  auto sizeIter = mActivatedSizes.find(ftFace);
+  if(sizeIter != mActivatedSizes.end())
+  {
+    if(sizeIter->second.mRequestedPointSize == requestedPointSize && variationsHash == sizeIter->second.mVariationsHash)
+    {
+      // Already activated face.
+      return FT_Err_Ok;
+    }
+  }
+
+  const FaceSizeCacheKey key  = FaceSizeCacheKey(ftFace, requestedPointSize, variationsHash);
+  auto                   iter = mLRUFaceSizeCache.Find(key);
+  FaceSizeCacheDataPtr   faceSizePtr;
+  FT_Error               error;
+
+  if(iter != mLRUFaceSizeCache.End())
+  {
+    if(!freeTypeCoords.empty())
+    {
+      FT_Set_Var_Design_Coordinates(ftFace, freeTypeCoords.size(), const_cast<FT_Fixed*>(freeTypeCoords.data()));
+    }
+
+    mActivatedSizes[ftFace] = ActivatedSizeData(requestedPointSize, variationsHash);
+    faceSizePtr             = mLRUFaceSizeCache.GetElement(iter);
+    error                   = FT_Activate_Size(faceSizePtr->mFreeTypeSize);
+    if(DALI_UNLIKELY(error != FT_Err_Ok))
+    {
+      DALI_LOG_ERROR("FT_Activate_Size fail, error code:0x%02X\n", error);
+    }
+    return error;
+  }
+
+  if(mLRUFaceSizeCache.IsFull())
+  {
+    auto removedData = mLRUFaceSizeCache.PopWithKey();
+    if(removedData.first.mRequestedPointSize == mActivatedSizes[removedData.first.mFreeTypeFace].mRequestedPointSize &&
+       removedData.first.mVariationsHash == mActivatedSizes[removedData.first.mFreeTypeFace].mVariationsHash)
+    {
+      DALI_LOG_DEBUG_INFO("FontClient::Plugin::FontFaceManager::ActivateFace, cache size : %zu, erase : %p\n", mLRUFaceSizeCache.Count(), removedData.second->mFreeTypeSize);
+      mActivatedSizes.erase(removedData.first.mFreeTypeFace);
+    }
+  }
+
+  if(!freeTypeCoords.empty())
+  {
+    FT_Set_Var_Design_Coordinates(ftFace, freeTypeCoords.size(), const_cast<FT_Fixed*>(freeTypeCoords.data()));
+  }
+
+  FT_Size ftSize;
+  error = FT_New_Size(ftFace, &ftSize);
+  if(DALI_UNLIKELY(error != FT_Err_Ok))
+  {
+    DALI_LOG_ERROR("FT_New_Size fail, error code:0x%02X\n", error);
+    return error;
+  }
+
+  error = FT_Activate_Size(ftSize);
+  if(DALI_UNLIKELY(error != FT_Err_Ok))
+  {
+    FT_Done_Size(ftSize);
+    DALI_LOG_ERROR("FT_Activate_Size fail, error code:0x%02X\n", error);
+    return error;
+  }
+
+  error = FT_Set_Char_Size(ftFace, 0u, FT_F26Dot6(requestedPointSize), mDpiHorizontal, mDpiVertical);
+  if(DALI_UNLIKELY(error != FT_Err_Ok))
+  {
+    FT_Done_Size(ftSize);
+    DALI_LOG_ERROR("FT_Set_Char_Size fail, error code:0x%02X\n", error);
+    return error;
+  }
+
+  mActivatedSizes[ftFace]    = ActivatedSizeData(requestedPointSize, variationsHash);
+  faceSizePtr                = std::make_shared<FaceSizeCacheData>();
+  faceSizePtr->mFreeTypeSize = std::move(ftSize);
+  mLRUFaceSizeCache.Push(key, faceSizePtr);
+
+  return error;
+}
+
+bool FontFaceManager::IsBitmapFont(FT_Face ftFace) const
+{
+  const bool isScalable           = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
+  const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
+  return !isScalable && hasFixedSizedBitmaps;
+}
+
+int FontFaceManager::FindFixedSizeIndex(FT_Face ftFace, const PointSize26Dot6 requestedPointSize)
+{
+  PointSize26Dot6 actualPointSize = 0u;
+  int             fixedSizeIndex  = 0;
+  for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
+  {
+    const PointSize26Dot6 fixedSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
+    if(fixedSize >= requestedPointSize)
+    {
+      actualPointSize = fixedSize;
+      break;
+    }
+  }
+
+  if(0u == actualPointSize)
+  {
+    // The requested point size is bigger than the bigest fixed size.
+    fixedSizeIndex = ftFace->num_fixed_sizes - 1;
+  }
+  return fixedSizeIndex;
+}
+
+FT_Error FontFaceManager::SelectFixedSize(FT_Face ftFace, const PointSize26Dot6 requestedPointSize, const int fixedSizeIndex)
+{
+  auto sizeIter = mSelectedIndices.find(ftFace);
+  if(sizeIter != mSelectedIndices.end())
+  {
+    if(sizeIter->second == requestedPointSize)
+    {
+      // Already selected fixed size.
+      return FT_Err_Ok;
+    }
+  }
+
+  FT_Error error = FT_Select_Size(ftFace, fixedSizeIndex);
+  if(DALI_UNLIKELY(error != FT_Err_Ok))
+  {
+    DALI_LOG_ERROR("FT_Select_Size fail, error code:0x%02X\n", error);
+  }
+  else
+  {
+    mSelectedIndices[ftFace] = requestedPointSize;
+  }
+  return error;
+}
+
+void FontFaceManager::ClearCache(const std::size_t remainCount)
+{
+  if(remainCount == 0u)
+  {
+    // Clear all cache.
+    mLRUFaceSizeCache.Clear();
+  }
+  else
+  {
+    // While the cache count is bigger than remainCount, remove oldest glyph.
+    while(mLRUFaceSizeCache.Count() > remainCount)
+    {
+      auto removedData = mLRUFaceSizeCache.Pop();
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::FontFaceManager::ClearCache[%zu / %zu]. Remove oldest face size : %p\n", mLRUFaceSizeCache.Count(), remainCount, removedData->mFreeTypeSize);
+    }
+  }
+
+  for(auto& item : mFreeTypeFaces)
+  {
+    item.second.ReleaseData();
+  }
+
+  mFreeTypeFaces.clear();
+  mActivatedSizes.clear();
+  mSelectedIndices.clear();
+}
+
+// FontFaceManager::FaceCacheData
+void FontFaceManager::FaceCacheData::ReleaseData()
+{
+  FT_Done_Face(mFreeTypeFace);
+  mFreeTypeFace = nullptr;
+}
+
+// FontFaceManager::FaceSizeCacheData
+void FontFaceManager::FaceSizeCacheData::ReleaseData()
+{
+  FT_Done_Size(mFreeTypeSize);
+}
+
+FontFaceManager::FaceSizeCacheData::FaceSizeCacheData()
+: mFreeTypeSize{}
+{
+}
+
+FontFaceManager::FaceSizeCacheData::~FaceSizeCacheData()
+{
+  ReleaseData();
+}
+
+FontFaceManager::FaceSizeCacheData::FaceSizeCacheData(FaceSizeCacheData&& rhs) noexcept
+: mFreeTypeSize{}
+{
+  *this = std::move(rhs);
+}
+
+FontFaceManager::FaceSizeCacheData& FontFaceManager::FaceSizeCacheData::operator=(FaceSizeCacheData&& rhs) noexcept
+{
+  // Self-assignment detection
+  if(this == &rhs)
+  {
+    return *this;
+  }
+
+  ReleaseData();
+
+  mFreeTypeSize     = rhs.mFreeTypeSize;
+  rhs.mFreeTypeSize = nullptr;
+
+  return *this;
+}
+
+} // namespace Dali::TextAbstraction::Internal
diff --git a/dali/internal/text/text-abstraction/plugin/font-face-manager.h b/dali/internal/text/text-abstraction/plugin/font-face-manager.h
new file mode 100644 (file)
index 0000000..d2ae147
--- /dev/null
@@ -0,0 +1,302 @@
+#ifndef DALI_TEST_ABSTRACTION_INTERNAL_FONT_FACE_MANAGER_H
+#define DALI_TEST_ABSTRACTION_INTERNAL_FONT_FACE_MANAGER_H
+
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-file-manager.h>
+#include <dali/devel-api/text-abstraction/font-list.h>
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+#include <dali/internal/text/text-abstraction/plugin/lru-cache-container.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/property-map.h>
+
+#include <memory> // for std::shared_ptr
+#include <unordered_map>
+
+#include <fontconfig/fontconfig.h>
+#include <harfbuzz/hb-ft.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_SIZES_H
+#include FT_MULTIPLE_MASTERS_H
+
+namespace Dali::TextAbstraction::Internal
+{
+/**
+ * @brief A manager for handling font faces and their associated sizes and variations.
+ *
+ * This class is responsible for managing font face data, including loading font faces, caching them,
+ * handling size and variation settings, and managing the interaction with FreeType and HarfBuzz.
+ */
+class FontFaceManager
+{
+public:
+  /**
+   * @brief Constructor for FontFaceManager.
+   *
+   * @param[in] maxNumberOfFaceSizeCache The maximum number of face size entries to cache.
+   */  FontFaceManager(std::size_t maxNumberOfFaceSizeCache);
+
+  // Destructor
+  ~FontFaceManager();
+
+  FontFaceManager(const FontFaceManager& rhs) = delete; // Do not use copy construct
+  FontFaceManager(FontFaceManager&& rhs)      = delete; // Do not use move construct
+
+  /**
+   * @brief Data structure for caching face-related data.
+   */
+  struct FaceCacheData
+  {
+    FaceCacheData()
+    : mFreeTypeFace(nullptr),
+      mReference(0)
+    {
+    }
+
+    FaceCacheData(FT_Face freeTypeFace)
+    : mFreeTypeFace(freeTypeFace),
+      mReference(0)
+    {
+    }
+
+    FT_Face mFreeTypeFace; ///< The FreeType face handle.
+    int     mReference;    ///< The reference count for the face.
+
+    void ReleaseData();
+  };
+
+  /**
+   * @brief Data structure for caching face size-related data.
+   *
+   * This struct holds the FreeType size handle for a specific face size.
+   */
+  struct FaceSizeCacheData
+  {
+    FaceSizeCacheData();
+    ~FaceSizeCacheData();
+
+    // Move operations
+    FaceSizeCacheData(FaceSizeCacheData&& rhs) noexcept;
+    FaceSizeCacheData& operator=(FaceSizeCacheData&& rhs) noexcept;
+
+    FT_Size mFreeTypeSize{};
+
+  private:
+    // Delete copy operations
+    FaceSizeCacheData(const FaceSizeCacheData&) = delete;
+    FaceSizeCacheData& operator=(const FaceSizeCacheData&) = delete;
+
+    void ReleaseData();
+  };
+
+  using FaceSizeCacheDataPtr = std::shared_ptr<FaceSizeCacheData>;
+
+  /**
+   * @brief Key structure for identifying face size cache entries.
+   */
+  struct FaceSizeCacheKey
+  {
+    FaceSizeCacheKey()
+    : mFreeTypeFace(nullptr),
+      mRequestedPointSize(0u),
+      mVariationsHash(0u)
+    {
+    }
+
+    FaceSizeCacheKey(const FT_Face freeTypeFace, const PointSize26Dot6 requestedPointSize, const std::size_t variationsHash)
+    : mFreeTypeFace(freeTypeFace),
+      mRequestedPointSize(requestedPointSize),
+      mVariationsHash(variationsHash)
+    {
+    }
+
+    FT_Face         mFreeTypeFace;
+    PointSize26Dot6 mRequestedPointSize;
+    std::size_t     mVariationsHash;
+
+    bool operator==(FaceSizeCacheKey const& rhs) const noexcept
+    {
+      return mFreeTypeFace == rhs.mFreeTypeFace && mRequestedPointSize == rhs.mRequestedPointSize && mVariationsHash == rhs.mVariationsHash;
+    }
+  };
+
+  /**
+   * @brief Hash function of FaceSizeCacheKey.
+   */
+  struct FaceSizeCacheKeyHash
+  {
+    std::size_t operator()(FaceSizeCacheKey const& key) const noexcept
+    {
+      return static_cast<std::size_t>(reinterpret_cast<std::uintptr_t>(key.mFreeTypeFace)) ^
+             static_cast<std::size_t>(key.mRequestedPointSize) ^
+             key.mVariationsHash;
+    }
+  };
+
+  /**
+   * @brief Data structure for storing activated face size data.
+   */
+  struct ActivatedSizeData
+  {
+    ActivatedSizeData()
+    : mRequestedPointSize(0u),
+      mVariationsHash(0u)
+    {
+    }
+
+    ActivatedSizeData(const PointSize26Dot6 requestedPointSize, const std::size_t variationsHash)
+    : mRequestedPointSize(requestedPointSize),
+      mVariationsHash(variationsHash)
+    {
+    }
+
+    PointSize26Dot6 mRequestedPointSize;
+    std::size_t     mVariationsHash;
+  };
+
+private:
+  std::size_t mMaxNumberOfFaceSizeCache; ///< The maximum capacity of face size cache.
+
+  using CacheContainer = LRUCacheContainer<FaceSizeCacheKey, FaceSizeCacheDataPtr, FaceSizeCacheKeyHash>;
+
+  CacheContainer mLRUFaceSizeCache; ///< LRU Cache container of face size.
+
+public:
+  /**
+   * @brief Sets the font file manager.
+   * @note For freetype memory face, not thread safe, read only.
+   *
+   * @param[in] fontFileManager The font file manager.
+   */
+  void SetFontFileManager(TextAbstraction::FontFileManager fontFileManager);
+
+  /**
+   * @brief Sets the DPI for horizontal and vertical dimensions.
+   *
+   * @param[in] dpiHorizontal The horizontal DPI.
+   * @param[in] dpiVertical The vertical DPI.
+   */
+  void SetDpi(const uint32_t dpiHorizontal, const uint32_t dpiVertical);
+
+  /**
+   * @brief Loads a FreeType face from a font file.
+   * @note The basic strategy of LoadFace is to create only one freetype face per font file.
+   * If there is a face already created in the cache, it returns the cached face.
+   * When creating a face, if there is a font file in the cache of the font file manager, it uses FT_New_Memory_Face.
+   * Otherwise, it uses FT_New_Face.
+   *
+   * @param[in] freeTypeLibrary The FreeType library handle.
+   * @param[in] fontPath The path to the font file.
+   * @param[in] faceIndex The index of the face in the font.
+   * @param[out] ftFace The FreeType face handle that will be loaded.
+   * @return FT_Err_Ok on success, otherwise an error code.
+   */
+  FT_Error LoadFace(const FT_Library& freeTypeLibrary, const FontPath& fontPath, const FaceIndex faceIndex, FT_Face& ftFace);
+
+  /**
+   * @brief Increases the reference count for a given font face.
+   *
+   * @param[in] fontPath The path to the font file.
+   */
+  void ReferenceFace(const FontPath& fontPath);
+
+  /**
+   * @brief Decreases the reference count for a given font face and releases it if the count reaches zero.
+   *
+   * @param[in] fontPath The path to the font file.
+   */
+  void ReleaseFace(const FontPath& fontPath);
+
+  /**
+   * @brief Builds variations data for a font face based on a property map.
+   * @note Face only.
+   *
+   * @param[in] ftFace The FreeType face handle.
+   * @param[in] variationsMapPtr The variation map pointer.
+   * @param[out] freeTypeCoords The FreeType coordinates for the variations.
+   * @param[out] harfBuzzVariations The HarfBuzz variations data.
+   */
+  void BuildVariations(FT_Face ftFace, const Property::Map* variationsMapPtr, std::vector<FT_Fixed>& freeTypeCoords, std::vector<hb_variation_t>& harfBuzzVariations);
+
+  /**
+   * @brief Activates a face for rendering with a specific size and variations.
+   * @note Face only.
+   * If necessary, call FT_Set_Var_Design_Coordinates, FT_Activate_Size.
+   * The key to this method is to minimize calls to FT_Set_Char_Size, which has overhead.
+   *
+   * @param[in] ftFace The FreeType face handle.
+   * @param[in] requestedPointSize The requested point size.
+   * @param[in] variationsHash The hash of the variations to use cache key.
+   * @param[in] freeTypeCoords The FreeType coordinates for the variations.
+   * @return FT_Err_Ok on success, otherwise an error code.
+   */
+  FT_Error ActivateFace(FT_Face ftFace, const PointSize26Dot6 requestedPointSize, const std::size_t variationsHash, const std::vector<FT_Fixed>& freeTypeCoords);
+
+  /**
+   * @brief Whether the freetype face is a bitmap font or not.
+   *
+   * @param[in] ftFace The FreeType face handle.
+   * @return true if face is bitmap font, false otherwise.
+   */
+  bool IsBitmapFont(FT_Face ftFace) const;
+
+  /**
+   * @brief Find the proper fixed size the given freetype face and requested point size.
+   * @note Bitmap only.
+   *
+   * @param[in] ftFace The FreeType face handle.
+   * @param[in] requestedPointSize The requested point size.
+   * @return The fixed size index for the given freetype face and requested point size.
+   */
+  int FindFixedSizeIndex(FT_Face ftFace, const PointSize26Dot6 requestedPointSize);
+
+  /**
+   * @brief Select the given fixed size.
+   * @note Bitmap only.
+   * This method minimizes calls to FT_Select_Size, but FT_Select_Size has almost no overhead.
+   *
+   * @param[in] ftFace The FreeType face handle.
+   * @param[in] requestedPointSize The requested point size.
+   * @param[in] fixedSizeIndex The fixed size index for freetype face.
+   * @return FT_Err_Ok on success, otherwise an error code.
+   */
+  FT_Error SelectFixedSize(FT_Face ftFace, const PointSize26Dot6 requestedPointSize, const int fixedSizeIndex);
+
+  /**
+   * @brief Clear all cached face size 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:
+  TextAbstraction::FontFileManager               mFontFileManager; ///< Handle to the font file manager.
+  std::unordered_map<std::string, FaceCacheData> mFreeTypeFaces;   //< Cache of loaded FreeType faces.
+  std::unordered_map<FT_Face, ActivatedSizeData> mActivatedSizes;  ///< Cache of activated face sizes.
+  std::unordered_map<FT_Face, PointSize26Dot6>   mSelectedIndices; ///< Cache of selected fixed size indices.
+
+  uint32_t mDpiHorizontal; ///< Horizontal dpi.
+  uint32_t mDpiVertical;   ///< Vertical dpi.
+};
+
+} // namespace Dali::TextAbstraction::Internal
+
+#endif //DALI_TEST_ABSTRACTION_INTERNAL_FONT_FACE_MANAGER_H
index fa09dbe2e6ce3cbe73757b6fe9ab5fedc69ca9e8..6e97126ae05f6c0d8d773dfd950b5db2c17f2831 100644 (file)
@@ -41,9 +41,12 @@ struct HarfBuzzProxyFont::Impl
    * @param[in] freeTypeFace The FreeType face.
    * @param[in] glyphCacheManager Glyph caching system for this harfbuzz font. It will be used as harfbuzz callback data.
    */
-  Impl(FT_Face freeTypeFace, GlyphCacheManager* glyphCacheManager)
+  Impl(FT_Face freeTypeFace, GlyphCacheManager* glyphCacheManager, PointSize26Dot6 requestedPointSize, const std::size_t variationsHash, const std::vector<hb_variation_t>& harfBuzzVariations)
   : mFreeTypeFace(freeTypeFace),
     mGlyphCacheManager(glyphCacheManager),
+    mRequestedPointSize(requestedPointSize),
+    mVariationsHash(variationsHash),
+    mHarfBuzzVariations(harfBuzzVariations),
     mHarfBuzzFont(nullptr)
   {
   }
@@ -75,16 +78,19 @@ private:
   void SetHarfBuzzFunctions();
 
 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-client-plugin-cache-handler.
+  FT_Face                     mFreeTypeFace;       ///< The FreeType face. Owned from font-face-cache-item.
+  GlyphCacheManager*          mGlyphCacheManager;  ///< Glyph caching system for this harfbuzz font. Owned from font-client-plugin-cache-handler.
+  PointSize26Dot6             mRequestedPointSize; ///< The requested point size.
+  std::size_t                 mVariationsHash;     ///< The hash of the variations to use cache key.
+  std::vector<hb_variation_t> mHarfBuzzVariations; ///< The HarfBuzz variations data.
 
   hb_font_t* mHarfBuzzFont; ///< Harfbuzz font handle integrated with FT_Face.
 };
 
-HarfBuzzProxyFont::HarfBuzzProxyFont(FT_Face freeTypeFace, const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi, GlyphCacheManager* glyphCacheManager)
+HarfBuzzProxyFont::HarfBuzzProxyFont(FT_Face freeTypeFace, const PointSize26Dot6& requestedPointSize, const std::size_t variationsHash, const std::vector<hb_variation_t>& harfBuzzVariations, const uint32_t& horizontalDpi, const uint32_t& verticalDpi, GlyphCacheManager* glyphCacheManager)
 : mHorizontalDpi(horizontalDpi),
   mVerticalDpi(verticalDpi),
-  mImpl(new Impl(freeTypeFace, glyphCacheManager))
+  mImpl(new Impl(freeTypeFace, glyphCacheManager, requestedPointSize, variationsHash, harfBuzzVariations))
 {
   mImpl->CreateHarfBuzzFont(requestedPointSize, mHorizontalDpi, mVerticalDpi);
 }
@@ -126,7 +132,7 @@ static bool GetGlyphCacheData(void* font_data, const GlyphIndex& glyphIndex, Gly
   if(DALI_LIKELY(impl && impl->mGlyphCacheManager))
   {
     FT_Error error;
-    return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(impl->mFreeTypeFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphDataPtr, error);
+    return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(impl->mFreeTypeFace, impl->mRequestedPointSize, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, impl->mVariationsHash, glyphDataPtr, error);
   }
   return false;
 }
@@ -377,16 +383,14 @@ void HarfBuzzProxyFont::Impl::CreateHarfBuzzFont(const PointSize26Dot6& requeste
 
   if(mFreeTypeFace)
   {
-    // Before create hb_font_t, we must set FT_Char_Size
-    FT_Set_Char_Size(mFreeTypeFace,
-                     0u,
-                     FT_F26Dot6(requestedPointSize),
-                     horizontalDpi,
-                     verticalDpi);
-
     // Create font face with increase font face's reference.
     mHarfBuzzFont = hb_ft_font_create_referenced(mFreeTypeFace);
 
+    if(!mHarfBuzzVariations.empty())
+    {
+      hb_font_set_variations(mHarfBuzzFont, mHarfBuzzVariations.data(), mHarfBuzzVariations.size());
+    }
+
     SetHarfBuzzFunctions();
 
     if(mHarfBuzzFont)
index a14f2f72348f2780c5551e45610c5571c52e184b..0b3cc03ee7508f3b415e2971c30889df7d04214e 100644 (file)
@@ -23,6 +23,7 @@
 #include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
 
 // EXTERNAL INCLUDES
+#include <harfbuzz/hb-ft.h>
 #include <ft2build.h>
 #include FT_FREETYPE_H
 
@@ -44,7 +45,7 @@ public:
    * @param[in] verticalDpi Vertical DPI.
    * @param[in] glyphCacheManager Glyph caching system for this harfbuzz font. It will be used as harfbuzz callback data.
    */
-  HarfBuzzProxyFont(FT_Face freeTypeFace, const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi, GlyphCacheManager* glyphCacheManager);
+  HarfBuzzProxyFont(FT_Face freeTypeFace, const PointSize26Dot6& requestedPointSize, const std::size_t variationsHash, const std::vector<hb_variation_t>& harfBuzzVariations, const uint32_t& horizontalDpi, const uint32_t& verticalDpi, GlyphCacheManager* glyphCacheManager);
 
   // Destructor
   ~HarfBuzzProxyFont();
index fdc18e1b1d9a308e34dc716d819a8f6660750d6c..f2f461b10e13e31789ce07b3a5104a1e07fda847 100644 (file)
@@ -244,6 +244,32 @@ public:
     return mData[id].element;
   }
 
+  /**
+   * @brief Pops key and element off the oldest used element.
+   * After pop, CacheId relative with this element cannot be used.
+   * Access by poped element's CacheId is Undefined Behavior.
+   *
+   * @return A copy of the key and element.
+   * @warning This method asserts if the container is empty.
+   */
+  std::pair<KeyType, ElementType> PopWithKey()
+  {
+    DALI_ASSERT_ALWAYS(!IsEmpty() && "Reading from empty container");
+
+    const CacheId id = mOldestId;
+    InternalPop(id);
+    InternalInsertAfterFooter(id);
+
+    --mNumberOfElements;
+
+    KeyType key = mData[id].cacheIdIterator->first;
+
+    // Erase cache id.
+    mCacheId.erase(mData[id].cacheIdIterator);
+
+    return std::make_pair(key, mData[id].element);
+  }
+
   /**
    * @brief Get an element by the key. It will be marked as recent
    *
index 2102dddb091dd47efb2406497862902d706623c2..77d0bb8d101dc2d9a36bc8a931e9ccf5af8bd24c 100644 (file)
@@ -134,8 +134,7 @@ struct Shaping::Plugin
                const Character*       const text,
                Length                       numberOfCharacters,
                FontId                       fontId,
-               Script                       script,
-               Property::Map*               variationsMapPtr)
+               Script                       script)
   {
     // Clear previoursly shaped texts.
     mIndices.Clear();
@@ -183,55 +182,6 @@ struct Shaping::Plugin
         /* Layout the text */
         hb_buffer_add_utf32(harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters);
 
-        /*
-         * This code has been stopped because the original issue was resolved by other modifications.
-         * Maybe the optimization code has resolved the original issue.
-
-         * The below code produce a noise (un-wanted glyph) when compine "Negative Squared Latin Capital Letter" with U+FE0E at the end of line.
-         * i.e: Like this text "&#x1f170;&#xfe0e;"
-         */
-        /*
-        //The invisible unicodes like U+FE0F and U+FE0E should be replaced by zero width glyph
-        //i.e: This text "&#x262a;&#xfe0f;&#xfe0f;&#xfe0f;&#x262a;&#xfe0f;" should be rendered as two adjacent glyphs.
-        if(TextAbstraction::IsOneOfEmojiScripts(script))
-        {
-          //TODO: check if this should be applied to all scripts
-          //Applied this only on EMOJI scripts to avoid compatibility issues with other scripts
-          hb_buffer_set_invisible_glyph(harfBuzzBuffer, TextAbstraction::GetUnicodeForInvisibleGlyph());
-        }
-        */
-
-        if(variationsMapPtr != nullptr)
-        {
-          Property::Map& variationsMap = *variationsMapPtr;
-
-          std::size_t axis_cnt = variationsMap.Count();
-          hb_variation_t *variations = new hb_variation_t[axis_cnt];
-          int valid_cnt = 0;
-
-          for(std::size_t index = 0; index < axis_cnt; index++)
-          {
-            const KeyValuePair& keyvalue = variationsMap.GetKeyValue(index);
-
-            if(keyvalue.first.type == Property::Key::STRING)
-            {
-              const std::string& key   = keyvalue.first.stringKey;
-              float              value = 0.0f;
-
-              if(key.length() == 4u && keyvalue.second.Get(value))
-              {
-                variations[valid_cnt].tag = HB_TAG(key[0], key[1], key[2], key[3]);
-                variations[valid_cnt].value = value;
-
-                ++valid_cnt;
-              }
-            }
-          }
-
-          hb_font_set_variations(harfBuzzFont, variations, valid_cnt);
-          delete[] variations;
-        }
-
         hb_shape(harfBuzzFont, harfBuzzBuffer, NULL, 0u);
 
         /* Get glyph data */
@@ -400,8 +350,7 @@ Length Shaping::Shape(TextAbstraction::FontClient& fontClient,
                       const Character*       const text,
                       Length                       numberOfCharacters,
                       FontId                       fontId,
-                      Script                       script,
-                      Property::Map*               variationsMapPtr)
+                      Script                       script)
 {
   CreatePlugin();
 
@@ -409,8 +358,7 @@ Length Shaping::Shape(TextAbstraction::FontClient& fontClient,
                         text,
                         numberOfCharacters,
                         fontId,
-                        script,
-                        variationsMapPtr);
+                        script);
 }
 
 void Shaping::GetGlyphs(GlyphInfo*      glyphInfo,
index 6bae347ab813f1b4b8054213fc8a0a5a2fe88b27..7a53b3c58aee37fd81329e682329c1c3943bec67 100644 (file)
@@ -59,8 +59,7 @@ public:
                const Character*       const text,
                Length                       numberOfCharacters,
                FontId                       fontId,
-               Script                       script,
-               Property::Map*               variationsMapPtr = nullptr);
+               Script                       script);
 
   /**
    * @copydoc Dali::Shaping::GetGlyphs()