From 00644e7b78593e1aa061e1b6c4c85f9befc894f7 Mon Sep 17 00:00:00 2001 From: "Eunki, Hong" Date: Thu, 26 May 2022 17:05:10 +0900 Subject: [PATCH] Harfbuzz Shape optimize Make harfbuzz library use our optimized FontClient plugin cache feature. It will reduce shape time near 90%. NOTE : When we use bitmap glyph, advance value return not-valid value. But harfbuzz library already return not-valid value here. So don't care about it. Change-Id: I70bf16e10a2f8274ed01ed207a6c08db26dc3f16 Signed-off-by: Eunki, Hong --- dali/internal/text/file.list | 1 + .../text/text-abstraction/font-client-impl.cpp | 9 +- .../text/text-abstraction/font-client-impl.h | 18 +- .../plugin/bitmap-font-cache-item.h | 8 + .../plugin/font-cache-item-interface.h | 12 + .../plugin/font-client-plugin-impl.cpp | 10 + .../plugin/font-client-plugin-impl.h | 5 + .../plugin/font-face-cache-item.cpp | 25 +- .../text-abstraction/plugin/font-face-cache-item.h | 41 +- .../plugin/harfbuzz-proxy-font.cpp | 441 +++++++++++++++++++++ .../text-abstraction/plugin/harfbuzz-proxy-font.h | 79 ++++ .../text/text-abstraction/shaping-impl.cpp | 33 +- 12 files changed, 635 insertions(+), 47 deletions(-) create mode 100644 dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp create mode 100644 dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h diff --git a/dali/internal/text/file.list b/dali/internal/text/file.list index 1444e79..867fb26 100644 --- a/dali/internal/text/file.list +++ b/dali/internal/text/file.list @@ -14,4 +14,5 @@ SET( adaptor_text_common_src_files ${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-glyph-cache-manager.cpp + ${adaptor_text_dir}/text-abstraction/plugin/harfbuzz-proxy-font.cpp ) diff --git a/dali/internal/text/text-abstraction/font-client-impl.cpp b/dali/internal/text/text-abstraction/font-client-impl.cpp index 2b5c40a..0656765 100644 --- a/dali/internal/text/text-abstraction/font-client-impl.cpp +++ b/dali/internal/text/text-abstraction/font-client-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -419,6 +419,13 @@ bool FontClient::AddCustomFontDirectory(const FontPath& path) return mPlugin->AddCustomFontDirectory(path); } +HarfBuzzFontHandle FontClient::GetHarfBuzzFont(FontId fontId) +{ + CreatePlugin(); + + return mPlugin->GetHarfBuzzFont(fontId); +} + void FontClient::CreatePlugin() { if(!mPlugin) diff --git a/dali/internal/text/text-abstraction/font-client-impl.h b/dali/internal/text/text-abstraction/font-client-impl.h index ffa070a..7b158ef 100644 --- a/dali/internal/text/text-abstraction/font-client-impl.h +++ b/dali/internal/text/text-abstraction/font-client-impl.h @@ -2,7 +2,7 @@ #define DALI_INTERNAL_TEXT_ABSTRACTION_FONT_CLIENT_IMPL_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -32,6 +32,8 @@ namespace TextAbstraction { namespace Internal { +using HarfBuzzFontHandle = void*; ///< @note We don't want to make other class include harfbuzz header. So we will keep harfbuzz font data as HarfBuzzFontHandle. + /** * Implementation of the FontClient */ @@ -48,6 +50,7 @@ public: */ ~FontClient(); +public: // API for Dali::TextAbstraction::FontClient used. /** * @copydoc Dali::TextAbstraction::FontClient::Get() */ @@ -259,6 +262,12 @@ public: uint32_t GetNumberOfPointsPerOneUnitOfPointSize() const; /** + * @copydoc Dali::TextAbstraction::FontClient::AddCustomFontDirectory() + */ + bool AddCustomFontDirectory(const FontPath& path); + +public: // API for Dali::TextAbstraction::Internal::FontClient used. + /** * @brief Retrieves the pointer to the FreeType Font Face for the given @p fontId. * * @param[in] fontId The font id. @@ -277,9 +286,12 @@ public: FontDescription::Type GetFontType(FontId fontId); /** - * @copydoc Dali::TextAbstraction::FontClient::AddCustomFontDirectory() + * @brief Get the harfbuzz font data of font. + * + * @param fontId The font id. + * @return The harfbuzz font data, or nullptr if failed. */ - bool AddCustomFontDirectory(const FontPath& path); + HarfBuzzFontHandle GetHarfBuzzFont(FontId fontId); private: /** diff --git a/dali/internal/text/text-abstraction/plugin/bitmap-font-cache-item.h b/dali/internal/text/text-abstraction/plugin/bitmap-font-cache-item.h index 39e0cfb..1799eb2 100644 --- a/dali/internal/text/text-abstraction/plugin/bitmap-font-cache-item.h +++ b/dali/internal/text/text-abstraction/plugin/bitmap-font-cache-item.h @@ -107,6 +107,14 @@ struct BitmapFontCacheItem : public FontCacheItemInterface } /** + * @copydoc FontCacheItemInterface::GetHarfBuzzFont() + */ + HarfBuzzFontHandle GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi) override + { + return nullptr; + } + + /** * @copydoc FontCacheItemInterface::HasItalicStyle() */ bool HasItalicStyle() const override diff --git a/dali/internal/text/text-abstraction/plugin/font-cache-item-interface.h b/dali/internal/text/text-abstraction/plugin/font-cache-item-interface.h index 1d042d5..c5cce86 100644 --- a/dali/internal/text/text-abstraction/plugin/font-cache-item-interface.h +++ b/dali/internal/text/text-abstraction/plugin/font-cache-item-interface.h @@ -17,10 +17,13 @@ * limitations under the License. */ +// INTERNAL INCLUDES #include #include #include +#include // for HarfBuzzFontHandle +// EXTERNAL INCLUDES #include #include FT_FREETYPE_H @@ -96,6 +99,15 @@ struct FontCacheItemInterface virtual FT_Face GetTypeface() const = 0; /** + * Get the harfbuzz font struct for this font. + * + * @param[in] horizontalDpi Horizontal DPI for this harfbuzz font. + * @param[in] verticalDpi Vertical DPI for this harfbuzz font. + * @return the harfbuzz font data, or nullptr if failed. + */ + virtual HarfBuzzFontHandle GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi) = 0; + + /** * @return true if this font has an italic style */ virtual bool HasItalicStyle() const = 0; diff --git a/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp b/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp index 6c0c264..1c63e32 100644 --- a/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp +++ b/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp @@ -1262,6 +1262,16 @@ bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path) return FcConfigAppFontAddDir(nullptr, reinterpret_cast(path.c_str())); } +HarfBuzzFontHandle FontClient::Plugin::GetHarfBuzzFont(FontId fontId) +{ + FontCacheItemInterface* fontCacheItem = const_cast(GetCachedFontItem(fontId)); + if(fontCacheItem != nullptr) + { + return fontCacheItem->GetHarfBuzzFont(mDpiHorizontal, mDpiVertical); // May cache + } + return nullptr; +} + GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat) { EmbeddedItem embeddedItem; diff --git a/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h b/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h index 97bd754..b73ae6d 100644 --- a/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h +++ b/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h @@ -414,6 +414,11 @@ public: */ bool AddCustomFontDirectory(const FontPath& path); + /** + * @copydoc Dali::TextAbstraction::FontClient::GetHarfBuzzFont() + */ + HarfBuzzFontHandle GetHarfBuzzFont(FontId fontId); + private: /** * @brief Caches the fonts present in the platform. diff --git a/dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp b/dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp index dcde3e7..dd45035 100644 --- a/dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp +++ b/dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp @@ -72,6 +72,7 @@ FontFaceCacheItem::FontFaceCacheItem(FT_Library& freeTypeLibrary, : mFreeTypeLibrary(freeTypeLibrary), mFreeTypeFace(ftFace), mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())), + mHarfBuzzProxyFont(), mPath(path), mRequestedPointSize(requestedPointSize), mFaceIndex(face), @@ -100,6 +101,7 @@ FontFaceCacheItem::FontFaceCacheItem(FT_Library& freeTypeLibrary, : mFreeTypeLibrary(freeTypeLibrary), mFreeTypeFace(ftFace), mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())), + mHarfBuzzProxyFont(), mPath(path), mRequestedPointSize(requestedPointSize), mFaceIndex(face), @@ -121,7 +123,8 @@ FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs) : mFreeTypeLibrary(rhs.mFreeTypeLibrary) { mFreeTypeFace = rhs.mFreeTypeFace; - mGlyphCacheManager = rhs.mGlyphCacheManager; + mGlyphCacheManager = std::move(rhs.mGlyphCacheManager); + mHarfBuzzProxyFont = std::move(rhs.mHarfBuzzProxyFont); mPath = std::move(rhs.mPath); mRequestedPointSize = rhs.mRequestedPointSize; mFaceIndex = rhs.mFaceIndex; @@ -135,8 +138,7 @@ FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs) mIsFixedSizeBitmap = rhs.mIsFixedSizeBitmap; mHasColorTables = rhs.mHasColorTables; - rhs.mGlyphCacheManager = nullptr; - rhs.mFreeTypeFace = nullptr; + rhs.mFreeTypeFace = nullptr; } FontFaceCacheItem::~FontFaceCacheItem() @@ -144,7 +146,12 @@ FontFaceCacheItem::~FontFaceCacheItem() // delete glyph cache manager before free face. if(mGlyphCacheManager) { - delete mGlyphCacheManager; + mGlyphCacheManager.reset(); + } + + if(mHarfBuzzProxyFont) + { + mHarfBuzzProxyFont.reset(); } // Free face. @@ -507,4 +514,14 @@ GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character, Character varia return FT_Face_GetCharVariantIndex(mFreeTypeFace, character, variantSelector); } +HarfBuzzFontHandle FontFaceCacheItem::GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi) +{ + // Create new harfbuzz font only first time or DPI changed. + if(DALI_UNLIKELY(!mHarfBuzzProxyFont || mHarfBuzzProxyFont->mHorizontalDpi != horizontalDpi || mHarfBuzzProxyFont->mVerticalDpi != verticalDpi)) + { + mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager.get())); + } + return mHarfBuzzProxyFont->GetHarfBuzzFont(); +} + } // namespace Dali::TextAbstraction::Internal diff --git a/dali/internal/text/text-abstraction/plugin/font-face-cache-item.h b/dali/internal/text/text-abstraction/plugin/font-face-cache-item.h index e1e0c6e..08baff7 100644 --- a/dali/internal/text/text-abstraction/plugin/font-face-cache-item.h +++ b/dali/internal/text/text-abstraction/plugin/font-face-cache-item.h @@ -20,9 +20,11 @@ // INTERNAL INCLUDES #include #include +#include // EXTERNAL INCLUDES #include +#include // for std::unique_ptr // EXTERNAL INCLUDES #include @@ -114,6 +116,11 @@ struct FontFaceCacheItem : public FontCacheItemInterface } /** + * @copydoc FontCacheItemInterface::GetHarfBuzzFont() + */ + HarfBuzzFontHandle GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi) override; + + /** * @copydoc FontCacheItemInterface::HasItalicStyle() */ bool HasItalicStyle() const override @@ -121,21 +128,25 @@ struct FontFaceCacheItem : public FontCacheItemInterface return (0u != (mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)); } - FT_Library& mFreeTypeLibrary; ///< A handle to a FreeType library instance. - FT_Face mFreeTypeFace; ///< The FreeType face. - GlyphCacheManager* mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs. - 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. +public: + FT_Library& mFreeTypeLibrary; ///< A handle to a FreeType library instance. + FT_Face mFreeTypeFace; ///< The FreeType face. + + std::unique_ptr mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs. + std::unique_ptr 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. }; } // namespace Dali::TextAbstraction::Internal diff --git a/dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp b/dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp new file mode 100644 index 0000000..5d62a95 --- /dev/null +++ b/dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2022 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 +#include + +// EXTERNAL INCLUDES +#include FT_GLYPH_H +#include +#include + +#if defined(DEBUG_ENABLED) +extern Dali::Integration::Log::Filter* gFontClientLogFilter; +#endif + +namespace Dali::TextAbstraction::Internal +{ +/** + * @brief Helper class to create and destroy harfbuzz font, and hold data in harfbuzz callback + * It also cache informations what harfbuzz font need to be created. + */ +struct HarfBuzzProxyFont::Impl +{ + /** + * @brief Constructor. + * + * @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) + : mFreeTypeFace(freeTypeFace), + mGlyphCacheManager(glyphCacheManager), + mHarfBuzzFont(nullptr) + { + } + + // Destructor + ~Impl() + { + if(mHarfBuzzFont) + { + // It will reduce reference of freetype face automatically. + hb_font_destroy(mHarfBuzzFont); + } + } + +public: + /** + * @brief Create new harfbuzz font. + * + * @param[in] requestedPointSize The requiested point size of font. + * @param[in] horizontalDpi Horizontal DPI. + * @param[in] verticalDpi Vertical DPI. + */ + void CreateHarfBuzzFont(const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi); + +private: + /** + * @brief Register harfbuzz callback functions into current harfbuzz font. + */ + 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-face-cache-item. + + 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) +: mHorizontalDpi(horizontalDpi), + mVerticalDpi(verticalDpi), + mImpl(new Impl(freeTypeFace, glyphCacheManager)) +{ + mImpl->CreateHarfBuzzFont(requestedPointSize, mHorizontalDpi, mVerticalDpi); +} + +HarfBuzzProxyFont::~HarfBuzzProxyFont() +{ + if(mImpl) + { + delete mImpl; + } +} + +HarfBuzzFontHandle HarfBuzzProxyFont::GetHarfBuzzFont() const +{ + if(mImpl) + { + return static_cast(mImpl->mHarfBuzzFont); + } + return nullptr; +} + +// Collection of harfbuzz custom callback functions. +// Reference : https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-ft.cc +namespace +{ +/** + * @brief Get glyph informations by dali glyph cache system. + * + * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type. + * @param[in] glyphIndex Index of glyph. + * @param[out] glyphData The result of cached glyph data. + * @return True if we success to get some glyph data. False otherwise. + */ +static bool GetGlyphCacheData(void* font_data, const GlyphIndex& glyphIndex, GlyphCacheManager::GlyphCacheData& glyphData) +{ + HarfBuzzProxyFont::Impl* impl = reinterpret_cast(font_data); + + // Note : HarfBuzz used only FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING internally. + if(DALI_LIKELY(impl && impl->mGlyphCacheManager)) + { + FT_Error error; + return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphData, error); + } + return false; +} + +/** + * @brief Calculate font extents value both in vertical and horizontal. + * + * @param[in] font Current harfbuzz font data. + * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type. + * @param[out] extents Extents value of font. (scale as 26.6) + * @param[in] user_data Registered user data. + * @return True if we success to get font extents. False otherwise. + */ +static hb_bool_t FontExtentsFunc(hb_font_t* font, void* font_data, hb_font_extents_t* extents, void* user_data) +{ + HarfBuzzProxyFont::Impl* impl = reinterpret_cast(font_data); + + if(DALI_LIKELY(impl && impl->mFreeTypeFace)) + { + FT_Size_Metrics& ftMetrics = impl->mFreeTypeFace->size->metrics; + + extents->ascender = ftMetrics.ascender; + extents->descender = ftMetrics.descender; + extents->line_gap = ftMetrics.height - (extents->ascender - extents->descender); + + return true; + } + return false; +} + +/** + * @brief Convert from character into index of glyph. + * + * @param[in] font Current harfbuzz font data. + * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type. + * @param[in] character The value of character what we want to get index. + * @param[out] glyphIndex Index of glyph that current font face used. + * @param[in] user_data Registered user data. + * @return True if we success to convert. + */ +static hb_bool_t GlyphNormalIndexConvertFunc(hb_font_t* font, void* font_data, hb_codepoint_t character, hb_codepoint_t* glyphIndex, void* user_data) +{ + HarfBuzzProxyFont::Impl* impl = reinterpret_cast(font_data); + + if(DALI_LIKELY(impl && impl->mFreeTypeFace)) + { + *glyphIndex = FT_Get_Char_Index(impl->mFreeTypeFace, character); + return *glyphIndex != 0; + } + return false; +} + +/** + * @brief Convert from character and variant selector into index of glyph. + * + * @param[in] font Current harfbuzz font data. + * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type. + * @param[in] character The value of character what we want to get index. + * @param[in] variantSelector Variant selector. + * @param[out] glyphIndex Index of glyph that current font face used. + * @param[in] user_data Registered user data. + * @return True if we success to convert. + */ +static hb_bool_t GlyphVariantIndexConvertFunc(hb_font_t* font, void* font_data, hb_codepoint_t character, hb_codepoint_t variantSelector, hb_codepoint_t* glyphIndex, void* user_data) +{ + HarfBuzzProxyFont::Impl* impl = reinterpret_cast(font_data); + + if(DALI_LIKELY(impl && impl->mFreeTypeFace)) + { + *glyphIndex = FT_Face_GetCharVariantIndex(impl->mFreeTypeFace, character, variantSelector); + return *glyphIndex != 0; + } + return false; +} + +/** + * @brief Calculate glyph advance value in horizontal. + * + * @param[in] font Current harfbuzz font data. + * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type. + * @param[in] glyphIndex Index of glyph. + * @param[in] user_data Registered user data. + * @return Horizontal advance value of glyphIndex. (scale as 26.6) + */ +static hb_position_t GlyphHorizontalAdvanceFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, void* user_data) +{ + // Output data stored here. + GlyphCacheManager::GlyphCacheData glyphData; + if(GetGlyphCacheData(font_data, static_cast(glyphIndex), glyphData)) + { + // Note : It may return invalid value for fixed size bitmap glyph. + // But, Harfbuzz library also return Undefined advanced value if it is fixed size font. + // So we'll also ignore that case. + return static_cast(glyphData.mGlyphMetrics.horiAdvance); + } + return 0; +} +/** + * @brief Calculate glyph advance value in vertical. + * + * @param[in] font Current harfbuzz font data. + * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type. + * @param[in] glyphIndex Index of glyph. + * @param[in] user_data Registered user data. + * @return Vertical advance value of glyphIndex. (scale as 26.6) + */ +static hb_position_t GlyphVerticalAdvanceFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, void* user_data) +{ + // Output data stored here. + GlyphCacheManager::GlyphCacheData glyphData; + if(GetGlyphCacheData(font_data, static_cast(glyphIndex), glyphData)) + { + // Note : It may return invalid value for fixed size bitmap glyph. + // But, Harfbuzz library also return Undefined advanced value if it is fixed size font. + // So we'll also ignore that case. + return static_cast(glyphData.mGlyphMetrics.vertAdvance); + } + return 0; +} + +/** + * @brief Calculate glyph origin position value in horizontal. + * + * @param[in] font Current harfbuzz font data. + * @param[in] font_data HarfBuzzProxyFont::Impl that register this callback as void* type. + * @param[in] glyphIndex Index of glyph. + * @param[out] x Origin position x (scale as 26.6) + * @param[out] y Origin position y (scale as 26.6) + * @param[in] user_data Registered user data. + * @return True if we get data successfully. False if some error occured. + */ +static hb_bool_t GlyphHorizontalOriginFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_position_t* x, hb_position_t* y, void* user_data) +{ + // Nothing to do + return true; +} +/** + * @brief Calculate glyph origin position value in vertical. + * + * @param[in] font Current harfbuzz font data. + * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type. + * @param[in] glyphIndex Index of glyph. + * @param[out] x Origin position x (scale as 26.6) + * @param[out] y Origin position y (scale as 26.6) + * @param[in] user_data Registered user data. + * @return True if we get data successfully. False if some error occured. + */ +static hb_bool_t GlyphVerticalOriginFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_position_t* x, hb_position_t* y, void* user_data) +{ + // Output data stored here. + GlyphCacheManager::GlyphCacheData glyphData; + if(GetGlyphCacheData(font_data, static_cast(glyphIndex), glyphData)) + { + *x = glyphData.mGlyphMetrics.horiBearingX - glyphData.mGlyphMetrics.vertBearingX; + *y = glyphData.mGlyphMetrics.horiBearingY + glyphData.mGlyphMetrics.vertBearingY; + return true; + } + return false; +} + +/** + * @brief Calculate glyph kerning value in horizontal. + * + * @param[in] font Current harfbuzz font data. + * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type. + * @param[in] glyphIndex1 First index of glyph to get kerning. + * @param[in] glyphIndex2 Second index of glyph to get kerning. + * @param[in] user_data Registered user data. + * @return Horizontal kerning position. (scale as 26.6) + */ +static hb_position_t GlyphHorizontalKerningFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex1, hb_codepoint_t glyphIndex2, void* user_data) +{ + HarfBuzzProxyFont::Impl* impl = reinterpret_cast(font_data); + + if(DALI_LIKELY(impl && impl->mFreeTypeFace)) + { + FT_Error error; + FT_Vector kerning; + + error = FT_Get_Kerning(impl->mFreeTypeFace, glyphIndex1, glyphIndex2, FT_KERNING_UNSCALED, &kerning); + if(error == FT_Err_Ok) + { + return kerning.x; + } + } + return 0; +} +/** + * @brief Calculate glyph kerning value in vertical. + * + * @param[in] font Current harfbuzz font data. + * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type. + * @param[in] glyphIndex1 First index of glyph to get kerning. + * @param[in] glyphIndex2 Second index of glyph to get kerning. + * @param[in] user_data Registered user data. + * @return Vertical kerning position. (scale as 26.6) + */ +static hb_position_t GlyphVerticalKerningFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex1, hb_codepoint_t glyphIndex2, void* user_data) +{ + // FreeType doesn't support vertical kerning + return 0; +} + +/** + * @brief Calculate glyph extents. + * + * @param[in] font Current harfbuzz font data. + * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type. + * @param[in] glyphIndex Index of glyph. + * @param[out] extents Extents value of glyph. (scale as 26.6) + * @param[in] user_data Registered user data. + * @return True if we get data successfully. False if some error occured. + */ +static hb_bool_t GlyphExtentsFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_glyph_extents_t* extents, void* user_data) +{ + // Output data stored here. + GlyphCacheManager::GlyphCacheData glyphData; + if(!GetGlyphCacheData(font_data, static_cast(glyphIndex), glyphData)) + { + extents->x_bearing = glyphData.mGlyphMetrics.horiBearingX; + extents->y_bearing = glyphData.mGlyphMetrics.horiBearingY; + extents->width = glyphData.mGlyphMetrics.width; + extents->height = glyphData.mGlyphMetrics.height; + return true; + } + return false; +} + +} // namespace + +void HarfBuzzProxyFont::Impl::CreateHarfBuzzFont(const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi) +{ + // Destroy previous hb_font_t if exist. + if(mHarfBuzzFont) + { + // It will reduce reference of freetype face automatically. + hb_font_destroy(mHarfBuzzFont); + mHarfBuzzFont = nullptr; + } + + if(mFreeTypeFace) + { + // Before create hb_font_t, we must set FT_Char_Size + FT_Set_Char_Size(mFreeTypeFace, + 0u, + requestedPointSize, + horizontalDpi, + verticalDpi); + + // Create font face with increase font face's reference. + mHarfBuzzFont = hb_ft_font_create_referenced(mFreeTypeFace); + + SetHarfBuzzFunctions(); + + if(mHarfBuzzFont) + { + DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::HarfBuzzManager::GetHarfBuzzFont. Create new harfbuzz font : %p freetype face : %p. Requested point size : %u, dpi : horizon %u vertial %u\n", mHarfBuzzFont, mFreeTypeFace, requestedPointSize, horizontalDpi, verticalDpi); + } + else + { + DALI_LOG_ERROR("ERROR! failed to create harfbuzz font."); + } + } + else + { + DALI_LOG_ERROR("ERROR! freetype face is null! something unknown problem occured."); + } +} + +void HarfBuzzProxyFont::Impl::SetHarfBuzzFunctions() +{ + if(mHarfBuzzFont) + { + hb_font_funcs_t* customFunctions = hb_font_funcs_create(); + + if(customFunctions) + { + // Bind custom functions here + hb_font_funcs_set_font_h_extents_func(customFunctions, FontExtentsFunc, 0, 0); + hb_font_funcs_set_font_v_extents_func(customFunctions, FontExtentsFunc, 0, 0); + + hb_font_funcs_set_nominal_glyph_func(customFunctions, GlyphNormalIndexConvertFunc, 0, 0); + hb_font_funcs_set_variation_glyph_func(customFunctions, GlyphVariantIndexConvertFunc, 0, 0); + + hb_font_funcs_set_glyph_h_advance_func(customFunctions, GlyphHorizontalAdvanceFunc, 0, 0); + hb_font_funcs_set_glyph_v_advance_func(customFunctions, GlyphVerticalAdvanceFunc, 0, 0); + hb_font_funcs_set_glyph_extents_func(customFunctions, GlyphExtentsFunc, 0, 0); + + hb_font_funcs_set_glyph_h_origin_func(customFunctions, GlyphHorizontalOriginFunc, 0, 0); + hb_font_funcs_set_glyph_v_origin_func(customFunctions, GlyphVerticalOriginFunc, 0, 0); + hb_font_funcs_set_glyph_h_kerning_func(customFunctions, GlyphHorizontalKerningFunc, 0, 0); + hb_font_funcs_set_glyph_v_kerning_func(customFunctions, GlyphVerticalKerningFunc, 0, 0); + + // Set custom functions into our own harfbuzz font + hb_font_set_funcs(mHarfBuzzFont, customFunctions, this, 0); + + // We must release functions type what we create. + hb_font_funcs_destroy(customFunctions); + } + else + { + DALI_LOG_ERROR("ERROR! Fail to create custom harfbuzz functions."); + + // Something wrong while create harfbuzz font. Destory it. + // It will reduce reference of freetype face automatically. + hb_font_destroy(mHarfBuzzFont); + mHarfBuzzFont = nullptr; + } + } +} + +} // namespace Dali::TextAbstraction::Internal diff --git a/dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h b/dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h new file mode 100644 index 0000000..a14f2f7 --- /dev/null +++ b/dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h @@ -0,0 +1,79 @@ +#ifndef DALI_TEXT_ABSTRACTION_INTERNAL_HARFBUZZ_PROXY_FONT_H +#define DALI_TEXT_ABSTRACTION_INTERNAL_HARFBUZZ_PROXY_FONT_H + +/* + * Copyright (c) 2022 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 +#include // for HarfBuzzFontHandle +#include + +// EXTERNAL INCLUDES +#include +#include FT_FREETYPE_H + +namespace Dali::TextAbstraction::Internal +{ +/** + * @brief Helper class to shape of FT_Face by harfbuzz library. + * @note Current class only be used for font face cache item. + */ +class HarfBuzzProxyFont +{ +public: + /** + * @brief Constructor harfbuzz font data integrated with FreeType face and our font face cache item. + * + * @param[in] freeTypeFace The FreeType face. + * @param[in] requestedPointSize The requiested point size of font. + * @param[in] horizontalDpi Horizontal DPI. + * @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); + + // Destructor + ~HarfBuzzProxyFont(); + +public: + // Public API area. + + /** + * @brief Get the created harfbuzz font data integrated with FreeType face and our font face cache item. + * + * @return Created harfbuzz font data. or nullptr if there is something error. + */ + HarfBuzzFontHandle GetHarfBuzzFont() const; + +private: + // Private API area. + HarfBuzzProxyFont() = delete; // Do not use default construct + HarfBuzzProxyFont(const HarfBuzzProxyFont& rhs) = delete; // Do not use copy construct + HarfBuzzProxyFont(HarfBuzzProxyFont&& rhs) = delete; // Do not use move construct +public: + struct Impl; // Harfbuzz callback can access this struct. + uint32_t mHorizontalDpi; ///< Horizontal DPI. + uint32_t mVerticalDpi; ///< VerticalDPI. + +private: + // Private member value area. + Impl* mImpl; +}; + +} // namespace Dali::TextAbstraction::Internal + +#endif //DALI_TEXT_ABSTRACTION_INTERNAL_HARFBUZZ_PROXY_FONT_H diff --git a/dali/internal/text/text-abstraction/shaping-impl.cpp b/dali/internal/text/text-abstraction/shaping-impl.cpp index 89d6498..f966fd6 100644 --- a/dali/internal/text/text-abstraction/shaping-impl.cpp +++ b/dali/internal/text/text-abstraction/shaping-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -151,6 +151,14 @@ struct Shaping::Plugin { case FontDescription::FACE_FONT: { + // Get our harfbuzz font struct + hb_font_t* harfBuzzFont = reinterpret_cast(fontClientImpl.GetHarfBuzzFont(fontId)); + if(nullptr == harfBuzzFont) + { + // Nothing to do if the harfBuzzFont is null. + return 0u; + } + // Reserve some space to avoid reallocations. const Length numberOfGlyphs = static_cast(1.3f * static_cast(numberOfCharacters)); mIndices.Reserve(numberOfGlyphs); @@ -158,28 +166,6 @@ struct Shaping::Plugin mCharacterMap.Reserve(numberOfGlyphs); mOffset.Reserve(2u * numberOfGlyphs); - // Retrieve a FreeType font's face. - FT_Face face = fontClientImpl.GetFreetypeFace(fontId); - if(nullptr == face) - { - // Nothing to do if the face is null. - return 0u; - } - - unsigned int horizontalDpi = 0u; - unsigned int verticalDpi = 0u; - fontClient.GetDpi(horizontalDpi, verticalDpi); - - FT_Set_Char_Size(face, - 0u, - fontClient.GetPointSize(fontId), - horizontalDpi, - verticalDpi); - - /* Get our harfbuzz font struct */ - hb_font_t* harfBuzzFont; - harfBuzzFont = hb_ft_font_create(face, NULL); - /* Create a buffer for harfbuzz to use */ hb_buffer_t* harfBuzzBuffer = hb_buffer_create(); @@ -276,7 +262,6 @@ struct Shaping::Plugin /* Cleanup */ hb_buffer_destroy(harfBuzzBuffer); - hb_font_destroy(harfBuzzFont); break; } case FontDescription::BITMAP_FONT: -- 2.7.4