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>
${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
${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
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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
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,
* @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.
*/
const Character* const text,
Length numberOfCharacters,
FontId fontId,
- Script script,
- Property::Map* variationsMapPtr = nullptr);
+ Script script);
/**
* Gets the shaped text data.
#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>
#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"
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
${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
)
FontClient::FontClient()
: mPlugin(nullptr),
+ mFontFileManager(),
mDpiHorizontal(0),
mDpiVertical(0)
{
+ // FontFileManager::Get() must be called from the main thread.
+ mFontFileManager = TextAbstraction::FontFileManager::Get();
}
FontClient::~FontClient()
std::scoped_lock lock(gMutex);
if(!mPlugin)
{
- mPlugin = new Plugin(mDpiHorizontal, mDpiVertical);
+ mPlugin = new Plugin(mFontFileManager, mDpiHorizontal, mDpiVertical);
}
}
// INTERNAL INCLUDES
#include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/devel-api/text-abstraction/font-file-manager.h>
struct FT_FaceRec_;
struct Plugin;
Plugin* mPlugin;
+ TextAbstraction::FontFileManager mFontFileManager;
+
// Allows DPI to be set without loading plugin
unsigned int mDpiHorizontal;
unsigned int mDpiVertical;
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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
// 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>
{
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.
mFontDescriptionCache(),
mCharacterSetCache(),
mFontDescriptionSizeCache(GetMaxNumberOfDescriptionCache()),
- mFontDataCache(),
- mFontFTFaceCache(),
mEllipsisCache(),
mEmbeddedItemCache(),
mCustomFontDirectories(),
+ mFontFaceManager(new FontFaceManager(GetMaxNumberOfFaceSizeCache())),
mGlyphCacheManager(new GlyphCacheManager(GetMaxNumberOfGlyphCache())),
mLatestFoundFontDescription(),
mLatestFoundFontDescriptionId(0u),
// delete cached glyph informations before clear mFontFaceCache.
mGlyphCacheManager->ClearCache();
+ // delete cached face informations before clear mFontFaceCache.
+ mFontFaceManager->ClearCache();
+
mDefaultFontDescription = FontDescription();
mSystemFonts.clear();
mFontDescriptionSizeCache.Clear();
- mFontDataCache.clear();
- mFontDataCache.rehash(0);
-
- ClearFTFaceFromFontFTFaceCache();
- mFontFTFaceCache.clear();
- mFontFTFaceCache.rehash(0);
-
mEllipsisCache.clear();
mPixelBufferCache.clear();
mEmbeddedItemCache.clear();
// delete cached glyph informations before clear mFontFaceCache.
mGlyphCacheManager->ClearCache();
+ // delete cached face informations before clear mFontFaceCache.
+ mFontFaceManager->ClearCache();
+
mDefaultFontDescription = FontDescription();
mSystemFonts.clear();
}
}
-void FontClient::Plugin::CacheHandler::ClearFTFaceFromFontFTFaceCache()
-{
- for(auto& item : mFontFTFaceCache)
- {
- FT_Done_Face(item.second);
- }
-}
-
// System / Default
void FontClient::Plugin::CacheHandler::InitSystemFonts()
}
}
-// 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,
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.
// 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
*/
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.
*/
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
/**
public: // Other public API
+ FontFaceManager* GetFontFaceManager() const
+ {
+ return mFontFaceManager.get();
+ }
+
GlyphCacheManager* GetGlyphCacheManager() const
{
return mGlyphCacheManager.get();
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;
#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>
* @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
}
/**
- * @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;
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;
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;
} // 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),
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
#endif
FT_Done_FreeType(mFreeTypeLibrary);
+
+ mFontFileManager.ClearCache();
}
void FontClient::Plugin::ClearCache() const
{
mDpiHorizontal = horizontalDpi;
mDpiVertical = verticalDpi;
+
+ mCacheHandler->mFontFaceManager->SetDpi(horizontalDpi, verticalDpi);
}
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);
}
}
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;
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.
//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)
{
}
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)
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
{
* @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.
*
* @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.
*
* @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;
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.
}
} // 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),
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,
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),
mFontId(0u),
mIsFixedSizeBitmap(true),
mHasColorTables(hasColorTables),
- mVariationsHash(variationsHash)
+ mVariationsHash(0u),
+ mFreeTypeCoords(),
+ mHarfBuzzVariations()
{
}
: mFreeTypeLibrary(rhs.mFreeTypeLibrary)
{
mFreeTypeFace = rhs.mFreeTypeFace;
+ mFontFaceManager = rhs.mFontFaceManager;
mGlyphCacheManager = rhs.mGlyphCacheManager;
mHarfBuzzProxyFont = std::move(rhs.mHarfBuzzProxyFont);
mPath = std::move(rhs.mPath);
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;
}
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
// 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)
{
// 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));
}
}
}
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
{
// 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.
// 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)
{
// 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))
{
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;
{
// 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));
// 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();
}
// 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>
*/
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,
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;
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
}
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())
}
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)
{
}
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))
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)
{
}
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))
// EXTERNAL INCLUDES
#include <memory> // for std::shared_ptr
+#include <map>
+#include <fontconfig/fontconfig.h>
#include <ft2build.h>
#include FT_FREETYPE_H
* @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.
* 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);
{
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;
}
};
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;
}
};
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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
* @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)
{
}
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);
}
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;
}
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)
#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
* @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();
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
*
const Character* const text,
Length numberOfCharacters,
FontId fontId,
- Script script,
- Property::Map* variationsMapPtr)
+ Script script)
{
// Clear previoursly shaped texts.
mIndices.Clear();
/* 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 "🅰︎"
- */
- /*
- //The invisible unicodes like U+FE0F and U+FE0E should be replaced by zero width glyph
- //i.e: This text "☪️️️☪️" 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 */
const Character* const text,
Length numberOfCharacters,
FontId fontId,
- Script script,
- Property::Map* variationsMapPtr)
+ Script script)
{
CreatePlugin();
text,
numberOfCharacters,
fontId,
- script,
- variationsMapPtr);
+ script);
}
void Shaping::GetGlyphs(GlyphInfo* glyphInfo,
const Character* const text,
Length numberOfCharacters,
FontId fontId,
- Script script,
- Property::Map* variationsMapPtr = nullptr);
+ Script script);
/**
* @copydoc Dali::Shaping::GetGlyphs()