[Tizen] Add support for FontClientFontPreLoad API 49/292749/1
authorBowon Ryu <bowon.ryu@samsung.com>
Tue, 2 May 2023 04:43:55 +0000 (13:43 +0900)
committerBowon Ryu <bowon.ryu@samsung.com>
Fri, 12 May 2023 07:29:11 +0000 (16:29 +0900)
This patch adds a new FontClientFontPreLoad API that preloads font faces.
this can prevents delays on main thread during the inital call of FT_New_Face.

using memoryFontPathList involves loading font buffers into memory.
And using FT_New_Memory_Face at runtime, which uses more memory
but can lead to greater performance improvements.

Change-Id: Id4ea04c23e2e63354e818b5ba74783508c43c8b7
Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
dali/devel-api/adaptor-framework/file-loader.cpp
dali/devel-api/adaptor-framework/file-loader.h
dali/devel-api/text-abstraction/font-client.cpp
dali/devel-api/text-abstraction/font-client.h
dali/devel-api/text-abstraction/font-list.h
dali/internal/text/text-abstraction/font-client-impl.cpp
dali/internal/text/text-abstraction/font-client-impl.h
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

index 340b875..4a593c9 100644 (file)
@@ -49,7 +49,11 @@ int ReadFile(const std::string& filename, Dali::Vector<uint8_t>& memblock, FileL
 int ReadFile(const std::string& filename, std::streampos& fileSize, Dali::Vector<char>& memblock, FileLoader::FileType fileType)
 {
   return Dali::Internal::Adaptor::ReadFile(filename, fileSize, memblock, fileType);
-  ;
+}
+
+int ReadFile(const std::string& filename, std::streampos& fileSize, Dali::Vector<uint8_t>& memblock, FileLoader::FileType fileType)
+{
+  return Dali::Internal::Adaptor::ReadFile(filename, fileSize, memblock, fileType);
 }
 
 std::streampos GetFileSize(const std::string& filename)
index 8b1dd7c..d4f31ef 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_FILE_LOADER_H
 
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -75,6 +75,18 @@ DALI_ADAPTOR_API int ReadFile(const std::string& filename, Dali::Vector<uint8_t>
 DALI_ADAPTOR_API int ReadFile(const std::string& filename, std::streampos& fileSize, Dali::Vector<char>& memblock, FileLoader::FileType fileType = BINARY);
 
 /**
+ * @brief Load the file. It will load it either as a binary or as a text
+ *
+ * @param[in] filename  Filename of the file to load.
+ * @param[in] fileSize  Size of the loaded file
+ * @param[in] memblock  Dali::Vector containing the buffer loaded
+ * @param[in] fileType  How we want to load the file. Binary or Text. Binary default
+ * @return error code. 0 - Error, 1 - Ok
+ *
+ */
+DALI_ADAPTOR_API int ReadFile(const std::string& filename, std::streampos& fileSize, Dali::Vector<uint8_t>& memblock, FileLoader::FileType fileType = BINARY);
+
+/**
  * @brief Get the file size of a file
  *
  * @param[in] filename  Filename of the file to load.
index 63a9e80..8899d5b 100644 (file)
@@ -667,6 +667,11 @@ void FontClient::GetDefaultFonts(FontList& defaultFonts)
   GetImplementation(*this).GetDefaultFonts(defaultFonts);
 }
 
+void FontClient::InitDefaultFontDescription()
+{
+  GetImplementation(*this).InitDefaultFontDescription();
+}
+
 void FontClient::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
 {
   GetImplementation(*this).GetDefaultPlatformFontDescription(fontDescription);
@@ -859,6 +864,11 @@ void FontClientPreCache(const FontFamilyList& fallbackFamilyList, const FontFami
   Internal::FontClient::PreCache(fallbackFamilyList, extraFamilyList, localeFamily, useThread);
 }
 
+void FontClientFontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool useThread)
+{
+  Internal::FontClient::PreLoad(fontPathList, memoryFontPathList, useThread);
+}
+
 } // namespace TextAbstraction
 
 } // namespace Dali
index 0ea768e..fd28e04 100644 (file)
@@ -284,6 +284,11 @@ public:
   void GetDefaultFonts(FontList& defaultFonts);
 
   /**
+   * @brief Initializes and caches default font from the system.
+   */
+  void InitDefaultFontDescription();
+
+  /**
    * @brief Retrieve the active default font from the system.
    *
    * @param[out] fontDescription font structure describing the default font.
@@ -649,7 +654,7 @@ public: // Not intended for application developers
 DALI_ADAPTOR_API FontClient FontClientPreInitialize();
 
 /**
- * @brief This is used to pre-cache fonts in order to improve the runtime performance of the application.
+ * @brief This is used to pre-cache FontConfig in order to improve the runtime performance of the application.
  *
  * @param[in] fallbackFamilyList A list of fallback font families to be pre-cached.
  * @param[in] extraFamilyList A list of additional font families to be pre-cached.
@@ -658,6 +663,23 @@ DALI_ADAPTOR_API FontClient FontClientPreInitialize();
  */
 DALI_ADAPTOR_API void FontClientPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread);
 
+/**
+ * @brief This is used to pre-load FreeType font face in order to improve the runtime performance of the application.
+ *
+ * @param[in] fontPathList A list of font paths to be pre-loaded.
+ * @param[in] memoryFontPathList A list of memory font paths to be pre-loaded.
+ * @param[in] useThread True if the font client should create thread and perform font pre-loading, false otherwise.
+ *
+ * @note
+ * The fonts in the fontPathList perform FT_New_Face during pre-loading,
+ * which can provide some performace benefits.
+ *
+ * The fonts in the memoryFontPathList read the font file and cache the buffer in memory during pre-load.
+ * This enables the use of FT_New_Memory_Face during runtime and provides a performance boost.
+ * It requires memory equivalent to the size of each font file.
+ */
+DALI_ADAPTOR_API void FontClientFontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool useThread);
+
 } // namespace TextAbstraction
 
 } // namespace Dali
index 9cb44cc..a36fe83 100644 (file)
@@ -34,6 +34,7 @@ typedef std::string FontPath;
 typedef std::string FontFamily;
 typedef std::string FontStyle;
 typedef std::vector<FontFamily> FontFamilyList;
+typedef std::vector<FontPath> FontPathList;
 
 namespace FontWidth
 {
index 5d17658..1a0bd45 100644 (file)
@@ -19,6 +19,7 @@
 #include <dali/internal/text/text-abstraction/font-client-impl.h>
 
 // EXTERNAL INCLUDES
+#include <mutex>
 #include <thread>
 #if defined(VCONF_ENABLED)
 #include <vconf.h>
 
 #include <dali/devel-api/text-abstraction/glyph-info.h>
 
-#define FONT_LOG_MESSAGE(level, format, ...)                                   \
-  do                                                                           \
-  {                                                                            \
-    char buffer[256];                                                          \
-    int result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
-    if (result >= static_cast<int>(sizeof(buffer)))                            \
-    {                                                                          \
-      std::string log("Font log message is too long to fit in the buffer.\n"); \
-      Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log);     \
-      break;                                                                   \
-    }                                                                          \
-    std::string log(buffer);                                                   \
-    Dali::TizenPlatform::LogMessage(level, log);                               \
+// Use this macro only if need to log messages before the log function is set.
+#define FONT_LOG_MESSAGE(level, format, ...)                                    \
+  do                                                                            \
+  {                                                                             \
+    char buffer[256];                                                           \
+    int  result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
+    if(result >= static_cast<int>(sizeof(buffer)))                              \
+    {                                                                           \
+      std::string log("Font log message is too long to fit in the buffer.\n");  \
+      Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log);      \
+      break;                                                                    \
+    }                                                                           \
+    std::string log(buffer);                                                    \
+    Dali::TizenPlatform::LogMessage(level, log);                                \
   } while(0)
 
 namespace Dali
@@ -55,10 +57,14 @@ namespace Internal
 {
 Dali::TextAbstraction::FontClient FontClient::gPreCreatedFontClient(NULL);
 std::thread                       gPreCacheThread;
+std::thread                       gPreLoadThread;
+std::mutex                        gMutex;
+
 /* TODO: This is to prevent duplicate calls of font pre-cache.
  * We may support this later, but currently we can't guarantee the behaviour
  * if there is a pre-cache call from the user after the font client has been created. */
 bool gFontPreCacheAvailable = true;
+bool gFontPreLoadAvailable  = true;
 
 FontClient::FontClient()
 : mPlugin(nullptr),
@@ -95,6 +101,12 @@ Dali::TextAbstraction::FontClient FontClient::Get()
         FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache thread join\n");
       }
 
+      if(gPreLoadThread.joinable())
+      {
+        gPreLoadThread.join();
+        FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreLoad thread join\n");
+      }
+
       if(gPreCreatedFontClient)
       {
         fontClientHandle = gPreCreatedFontClient;
@@ -103,13 +115,12 @@ Dali::TextAbstraction::FontClient FontClient::Get()
       else
       {
         fontClientHandle = Dali::TextAbstraction::FontClient(new FontClient);
-
-        // Make DefaultFontDescription cached
-        Dali::TextAbstraction::FontDescription defaultFontDescription;
-        fontClientHandle.GetDefaultPlatformFontDescription(defaultFontDescription);
       }
 
+      fontClientHandle.InitDefaultFontDescription();
+
       gFontPreCacheAvailable = false;
+      gFontPreLoadAvailable  = false;
 
       uint32_t horizontalDpi, verticalDpi;
       fontClientHandle.GetDpi(horizontalDpi, verticalDpi);
@@ -131,67 +142,102 @@ Dali::TextAbstraction::FontClient FontClient::PreInitialize()
 {
   // Pre-cached font client already exists or pre-cache thread already running.
   // Font client pre-cache includes caching of the default font description.
-  if((gPreCreatedFontClient && !gFontPreCacheAvailable) ||
-     (gPreCacheThread.joinable()))
+  if(gPreCreatedFontClient && !gFontPreCacheAvailable)
   {
     return gPreCreatedFontClient;
   }
 
-  gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
+  if(!gPreCreatedFontClient)
+  {
+    gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
+  }
 
-  // Make DefaultFontDescription cached
-  Dali::TextAbstraction::FontDescription defaultFontDescription;
-  gPreCreatedFontClient.GetDefaultPlatformFontDescription(defaultFontDescription);
+  gPreCreatedFontClient.InitDefaultFontDescription();
 
   return gPreCreatedFontClient;
 }
 
 void FontClient::PreCacheRun(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily)
 {
-  if(gFontPreCacheAvailable)
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_RUN\n");
+  GetImplementation(gPreCreatedFontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_RUN\n");
+}
+
+void FontClient::PreLoadRun(const FontPathList& fontPathList, const FontPathList& memoryFontPathList)
+{
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_FONT_PRELOAD_RUN\n");
+  GetImplementation(gPreCreatedFontClient).FontPreLoad(fontPathList, memoryFontPathList);
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_FONT_PRELOAD_RUN\n");
+}
+
+void FontClient::PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread)
+{
+  if(!gFontPreCacheAvailable)
   {
-    gFontPreCacheAvailable = false;
-    FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_RUN\n");
-    if(!gPreCreatedFontClient)
-    {
-      gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
-    }
-    GetImplementation(gPreCreatedFontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
-    FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_RUN\n");
+    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache run failed, as a pre-cached font client already exists.\n");
+    return;
+  }
+
+  if(gPreCacheThread.joinable())
+  {
+    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache thread already running.\n");
+  }
+
+  if(!gPreCreatedFontClient)
+  {
+    gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
+  }
+
+  gFontPreCacheAvailable = false;
+
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache fallbackFamilyList : %zu\n", fallbackFamilyList.size());
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache extraFamilyList    : %zu\n", extraFamilyList.size());
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache localeFamily       : %s\n", localeFamily.c_str());
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache useThread          : %d\n", useThread);
+
+  if(useThread)
+  {
+    gPreCacheThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily);
   }
   else
   {
-    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache run failed, as a pre-cached font client already exists.\n");
+    PreCacheRun(fallbackFamilyList, extraFamilyList, localeFamily);
   }
 }
 
-void FontClient::PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread)
+void FontClient::PreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool useThread)
 {
-  if(!gFontPreCacheAvailable)
+  if(!gFontPreLoadAvailable)
   {
-    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache has been completed or the font client has already been created.\n");
+    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient font pre-load run failed, as a pre-loaded font client already exists.\n");
     return;
   }
 
-  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache fallbackFamilyList : %zu\n", fallbackFamilyList.size());
-  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache extraFamilyList    : %zu\n", extraFamilyList.size());
-  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache localeFamily       : %s\n", localeFamily.c_str());
-  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache useThread          : %d\n", useThread);
+  if(gPreLoadThread.joinable())
+  {
+    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient font pre-load thread already running.\n");
+    return;
+  }
 
-  if(gPreCacheThread.joinable())
+  if(!gPreCreatedFontClient)
   {
-    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache thread already running.\n");
+    gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
+  }
+
+  gFontPreLoadAvailable = false;
+
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad fontPathList       : %zu\n", fontPathList.size());
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad memoryFontPathList : %zu\n", memoryFontPathList.size());
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad useThread          : %d\n", useThread);
+
+  if(useThread)
+  {
+    gPreLoadThread = std::thread(PreLoadRun, fontPathList, memoryFontPathList);
   }
   else
   {
-    if(useThread)
-    {
-      gPreCacheThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily);
-    }
-    else
-    {
-      PreCacheRun(fallbackFamilyList, extraFamilyList, localeFamily);
-    }
+    PreLoadRun(fontPathList, memoryFontPathList);
   }
 }
 
@@ -253,6 +299,20 @@ void FontClient::FontPreCache(const FontFamilyList& fallbackFamilyList, const Fo
   mPlugin->FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
 }
 
+void FontClient::FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList)
+{
+  CreatePlugin();
+
+  mPlugin->FontPreLoad(fontPathList, memoryFontPathList);
+}
+
+void FontClient::InitDefaultFontDescription()
+{
+  CreatePlugin();
+
+  mPlugin->InitDefaultFontDescription();
+}
+
 void FontClient::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
 {
   CreatePlugin();
@@ -536,6 +596,7 @@ HarfBuzzFontHandle FontClient::GetHarfBuzzFont(FontId fontId)
 
 void FontClient::CreatePlugin()
 {
+  std::scoped_lock lock(gMutex);
   if(!mPlugin)
   {
     mPlugin = new Plugin(mDpiHorizontal, mDpiVertical);
index f7731db..d0f7494 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_TEXT_ABSTRACTION_FONT_CLIENT_IMPL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -64,18 +64,30 @@ public: // API for Dali::TextAbstraction::FontClient used.
   static Dali::TextAbstraction::FontClient PreInitialize();
 
   /**
-   * @brief This is used to pre-cache fonts in order to improve the runtime performance of the application.
+   * @brief This is used to pre-cache FontConfig in order to improve the runtime performance of the application.
    *
    * @see Dali::TextAbstraction::FontClientPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread);
    */
-  static void PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useUiThread);
+  static void PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread);
 
   /**
-   * @brief This is used to creates a global font client and pre-caches the fonts.
+   * @brief This is used to creates a global font client and pre-caches the FontConfig.
    */
   static void PreCacheRun(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily);
 
   /**
+   * @brief This is used to pre-load FreeType font face in order to improve the runtime performance of the application.
+   *
+   * @see Dali::TextAbstraction:FontClientFontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool useThread);
+   */
+  static void PreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool useThread);
+
+  /**
+   * @brief This is used to creates a global font client and pre-loads the FreeType font face.
+   */
+  static void PreLoadRun(const FontPathList& fontPathList, const FontPathList& memoryFontPathList);
+
+  /**
    * @copydoc Dali::TextAbstraction::FontClient::ClearCache()
    */
   void ClearCache();
@@ -106,6 +118,11 @@ public: // API for Dali::TextAbstraction::FontClient used.
   void GetDefaultFonts(FontList& defaultFonts);
 
   /**
+   * @copydoc Dali::TextAbstraction::FontClient::InitDefaultFontDescription()
+   */
+  void InitDefaultFontDescription();
+
+  /**
    * @copydoc Dali::TextAbstraction::FontClient::GetDefaultPlatformFontDescription()
    */
   void GetDefaultPlatformFontDescription(FontDescription& fontDescription);
@@ -314,6 +331,16 @@ public: // API for Dali::TextAbstraction::Internal::FontClient used.
    */
   void FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily);
 
+  /**
+   * @brief This is used to pre-load FreeType font face in order to improve the runtime performance of the application.
+   *
+   * @param[in] fontPathList A list of font paths to be pre-loaded.
+   * @param[in] memoryFontPathList A list of memory font paths to be pre-loaded.
+   * @param[in] useThread True if the font client should create thread and perform font pre-loading, false otherwise.
+   */
+  void FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList);
+
+
 private:
   /**
    * Helper for lazy initialization.
index d7fb50d..c248787 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
 
 // 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/logging.h>
 #include <dali/internal/text/text-abstraction/font-client-impl.h>
 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
 
+// Use this macro only if need to log messages before the log function is set.
+#define FONT_LOG_MESSAGE(level, format, ...)                                    \
+  do                                                                            \
+  {                                                                             \
+    char buffer[256];                                                           \
+    int  result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
+    if(result >= static_cast<int>(sizeof(buffer)))                              \
+    {                                                                           \
+      std::string log("Font log message is too long to fit in the buffer.\n");  \
+      Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log);      \
+      break;                                                                    \
+    }                                                                           \
+    std::string log(buffer);                                                    \
+    Dali::TizenPlatform::LogMessage(level, log);                                \
+  } while(0)
+
 #if defined(DEBUG_ENABLED)
 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
 
@@ -274,6 +292,8 @@ FontClient::Plugin::CacheHandler::CacheHandler()
   mFontDescriptionCache(),
   mCharacterSetCache(),
   mFontDescriptionSizeCache(),
+  mFontDataCache(),
+  mFontFTFaceCache(),
   mEllipsisCache(),
   mEmbeddedItemCache(),
   mGlyphCacheManager(new GlyphCacheManager(GetMaxNumberOfGlyphCache())),
@@ -320,6 +340,13 @@ void FontClient::Plugin::CacheHandler::ClearCache()
   mFontDescriptionSizeCache.clear();
   mFontDescriptionSizeCache.rehash(0); // Note : unordered_map.clear() didn't deallocate memory
 
+  mFontDataCache.clear();
+  mFontDataCache.rehash(0);
+
+  ClearFTFaceFromFontFTFaceCache();
+  mFontFTFaceCache.clear();
+  mFontFTFaceCache.rehash(0);
+
   mEllipsisCache.clear();
   mPixelBufferCache.clear();
   mEmbeddedItemCache.clear();
@@ -415,6 +442,14 @@ 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()
@@ -482,6 +517,8 @@ void FontClient::Plugin::CacheHandler::InitDefaultFontDescription()
 {
   if(!mDefaultFontDescriptionCached)
   {
+    mDefaultFontDescriptionCached = true;
+
     // Clear any font config stored info in the caches.
     ClearCharacterSet();
 
@@ -534,9 +571,65 @@ void FontClient::Plugin::CacheHandler::InitDefaultFontDescription()
 
     // Create again the character sets as they are not valid after FcInitReinitialize()
     CreateCharacterSet();
+  }
+}
 
-    mDefaultFontDescriptionCached = true;
+// 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 : %lu, 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
index 20c5995..d61fb72 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_TEXT_ABSTRACTION_FONT_CLIENT_PLUGIN_CACHE_HANDLER_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -152,6 +152,11 @@ 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.
@@ -179,6 +184,65 @@ 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
 
   /**
@@ -406,6 +470,9 @@ public:                                    // Cache container list
 
   FontDescriptionSizeCacheContainer mFontDescriptionSizeCache; ///< Caches font identifiers for the pairs of font point size and the index to the vector with font descriptions of the validated fonts.
 
+  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<BitmapFontCacheItem>  mBitmapFontCache;   ///< Stores bitmap fonts.
   std::vector<PixelBufferCacheItem> mPixelBufferCache;  ///< Caches the pixel buffer of a url.
index 2c31868..78448d9 100644 (file)
@@ -25,6 +25,7 @@
 #include <dali/integration-api/platform-abstraction.h>
 #include <dali/internal/adaptor/common/adaptor-impl.h>
 #include <dali/internal/imaging/common/image-operations.h>
+#include <dali/internal/system/common/logging.h>
 #include <dali/internal/text/text-abstraction/plugin/bitmap-font-cache-item.h>
 #include <dali/internal/text/text-abstraction/plugin/embedded-item.h>
 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.h>
 #include <algorithm>
 #include <iterator>
 
+// Use this macro only if need to log messages before the log function is set.
+#define FONT_LOG_MESSAGE(level, format, ...)                                    \
+  do                                                                            \
+  {                                                                             \
+    char buffer[256];                                                           \
+    int  result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
+    if(result >= static_cast<int>(sizeof(buffer)))                              \
+    {                                                                           \
+      std::string log("Font log message is too long to fit in the buffer.\n");  \
+      Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log);      \
+      break;                                                                    \
+    }                                                                           \
+    std::string log(buffer);                                                    \
+    Dali::TizenPlatform::LogMessage(level, log);                                \
+  } while(0)
+
 #if defined(DEBUG_ENABLED)
 
 // Note, to turn on trace and verbose logging, use "export LOG_FONT_CLIENT=3,true"
@@ -265,6 +282,71 @@ void FontClient::Plugin::ResetSystemDefaults() const
   mCacheHandler->ResetSystemDefaults();
 }
 
+void FontClient::Plugin::CacheFontDataFromFile(const std::string& fontPath) const
+{
+  if(fontPath.empty())
+  {
+    return;
+  }
+
+  if(mCacheHandler->FindFontData(fontPath))
+  {
+    // Font data is already cached, no need to reload
+    return;
+  }
+
+  Dali::Vector<uint8_t> fontDataBuffer;
+  std::streampos        dataSize = 0;
+  if(!mCacheHandler->LoadFontDataFromFile(fontPath, fontDataBuffer, dataSize))
+  {
+    fontDataBuffer.Clear();
+    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "Failed to load font data : %s\n", fontPath.c_str());
+    return;
+  }
+
+  // Cache font data
+  mCacheHandler->CacheFontData(fontPath, fontDataBuffer, dataSize);
+}
+
+void FontClient::Plugin::CacheFontFaceFromFile(const std::string& 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);
+  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)
+  {
+    CacheFontFaceFromFile(fontPath);
+  }
+
+  for(const auto& memoryFontPath : memoryFontPathList)
+  {
+    CacheFontDataFromFile(memoryFontPath);
+  }
+}
+
 void FontClient::Plugin::FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily) const
 {
   mCacheHandler->InitDefaultFontDescription();
@@ -320,6 +402,11 @@ void FontClient::Plugin::FontPreCache(const FontFamilyList& fallbackFamilyList,
   }
 }
 
+void FontClient::Plugin::InitDefaultFontDescription() const
+{
+  mCacheHandler->InitDefaultFontDescription();
+}
+
 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription) const
 {
   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
@@ -1072,7 +1159,6 @@ bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& curren
 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
 {
   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
-  ;
 }
 
 FontId FontClient::Plugin::CreateFont(const FontPath& path,
@@ -1085,21 +1171,37 @@ FontId FontClient::Plugin::CreateFont(const FontPath& path,
   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
 
   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_CREATE_FONT");
-  FontId fontId = 0u;
 
-  #if defined(TRACE_ENABLED)
-  if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+  FontId   fontId = 0u;
+  FT_Face  ftFace;
+  FT_Error error;
+
+  uint8_t*       fontDataPtr   = nullptr;
+  std::streampos dataSize      = 0;
+  bool           fontDataFound = mCacheHandler->FindFontData(path, fontDataPtr, dataSize);
+
+  if(fontDataFound)
   {
-    DALI_LOG_DEBUG_INFO("DALI_TEXT_CREATE_FONT : FT_New_Face : %s\n", path.c_str());
+    // 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
+  {
+    // 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
   }
-  #endif
-
-  // Create & cache new font face
-  FT_Face ftFace;
-  int     error = FT_New_Face(mFreeTypeLibrary,
-                          path.c_str(),
-                          0,
-                          &ftFace);
 
   if(FT_Err_Ok == error)
   {
index 2a0c5ee..507ac1d 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_TEXT_ABSTRACTION_FONT_CLIENT_PLUGIN_IMPL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -105,6 +105,11 @@ public: // Dali::TextAbstraction::FontClient
   void ResetSystemDefaults() const;
 
   /**
+   * @copydoc Dali::TextAbstraction::FontClient::InitDefaultFontDescription()
+   */
+  void InitDefaultFontDescription() const;
+
+  /**
    * @copydoc Dali::TextAbstraction::FontClient::GetDefaultPlatformFontDescription()
    */
   void GetDefaultPlatformFontDescription(FontDescription& fontDescription) const;
@@ -318,6 +323,11 @@ public: // Dali::TextAbstraction::Internal::FontClient
    */
   void FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily) const;
 
+  /**
+   * @copydoc Dali::TextAbstraction::Internal::FontClient::FontPreLoad()
+   */
+  void FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList) const;
+
 private:
   /**
    * Get the cached font item for the given font
@@ -358,6 +368,27 @@ private:
                     FaceIndex       faceIndex,
                     bool            cacheDescription) const;
 
+
+  /**
+   * @brief Caches font data for the specified font path if it is not already cached.
+   *
+   * If the font data is not already cached, this function will load the font file
+   * from disk and cache the data for future use.
+   *
+   * @param[in] fontPath The font path to cache the data for.
+   */
+  void CacheFontDataFromFile(const std::string& fontPath) const;
+
+  /**
+   * @brief Caches FreeType face for the specified font path if it is not already cached.
+   *
+   * If the font face is not already cached, this function will perform the new face
+   * from font file and cache the face for future use.
+   *
+   * @param[in] fontPath The font path to cache the face for.
+   */
+  void CacheFontFaceFromFile(const std::string& fontPath) const;
+
 private:
   Plugin(const Plugin&) = delete;
   Plugin& operator=(const Plugin&) = delete;