Harfbuzz Shape optimize 74/275574/21
authorEunki, Hong <eunkiki.hong@samsung.com>
Thu, 26 May 2022 08:05:10 +0000 (17:05 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Thu, 9 Jun 2022 12:55:25 +0000 (21:55 +0900)
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 <eunkiki.hong@samsung.com>
12 files changed:
dali/internal/text/file.list
dali/internal/text/text-abstraction/font-client-impl.cpp
dali/internal/text/text-abstraction/font-client-impl.h
dali/internal/text/text-abstraction/plugin/bitmap-font-cache-item.h
dali/internal/text/text-abstraction/plugin/font-cache-item-interface.h
dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp
dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h
dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp
dali/internal/text/text-abstraction/plugin/font-face-cache-item.h
dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.cpp [new file with mode: 0644]
dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h [new file with mode: 0644]
dali/internal/text/text-abstraction/shaping-impl.cpp

index 1444e79..867fb26 100644 (file)
@@ -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
 )
index 2b5c40a..0656765 100644 (file)
@@ -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)
index ffa070a..7b158ef 100644 (file)
@@ -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:
   /**
index 39e0cfb..1799eb2 100644 (file)
@@ -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
index 1d042d5..c5cce86 100644 (file)
  * limitations under the License.
  */
 
+// INTERNAL INCLUDES
 #include <dali/devel-api/text-abstraction/font-client.h>
 #include <dali/devel-api/text-abstraction/font-metrics.h>
 #include <dali/devel-api/text-abstraction/glyph-info.h>
+#include <dali/internal/text/text-abstraction/font-client-impl.h> // for HarfBuzzFontHandle
 
+// EXTERNAL INCLUDES
 #include <ft2build.h>
 #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;
index 6c0c264..1c63e32 100644 (file)
@@ -1262,6 +1262,16 @@ bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
   return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
 }
 
+HarfBuzzFontHandle FontClient::Plugin::GetHarfBuzzFont(FontId fontId)
+{
+  FontCacheItemInterface* fontCacheItem = const_cast<FontCacheItemInterface*>(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;
index 97bd754..b73ae6d 100644 (file)
@@ -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.
index dcde3e7..dd45035 100644 (file)
@@ -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
index e1e0c6e..08baff7 100644 (file)
 // INTERNAL INCLUDES
 #include <dali/internal/text/text-abstraction/plugin/font-cache-item-interface.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>
 
 // EXTERNAL INCLUDES
 #include <fontconfig/fontconfig.h>
+#include <memory> // for std::unique_ptr
 
 // EXTERNAL INCLUDES
 #include <ft2build.h>
@@ -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<GlyphCacheManager> mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs.
+  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.
 };
 
 } // 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 (file)
index 0000000..5d62a95
--- /dev/null
@@ -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 <dali/integration-api/debug.h>
+#include <dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h>
+
+// EXTERNAL INCLUDES
+#include FT_GLYPH_H
+#include <harfbuzz/hb-ft.h>
+#include <harfbuzz/hb.h>
+
+#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<HarfBuzzFontHandle>(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<HarfBuzzProxyFont::Impl*>(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<HarfBuzzProxyFont::Impl*>(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<HarfBuzzProxyFont::Impl*>(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<HarfBuzzProxyFont::Impl*>(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>(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<hb_position_t>(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>(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<hb_position_t>(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>(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<HarfBuzzProxyFont::Impl*>(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>(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 (file)
index 0000000..a14f2f7
--- /dev/null
@@ -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 <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
+#include <dali/internal/text/text-abstraction/font-client-impl.h> // for HarfBuzzFontHandle
+#include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
+
+// EXTERNAL INCLUDES
+#include <ft2build.h>
+#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
index 89d6498..f966fd6 100644 (file)
@@ -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<hb_font_t*>(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<Length>(1.3f * static_cast<float>(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: