[Tizen] Fix small font size text layout issue
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-face-cache-item.cpp
index 76e7d99..6ad5c96 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.
  * limitations under the License.
  */
 
+// EXTERNAL HEADERS
 #include <dali/integration-api/debug.h>
+
+// INTERNAL HEADERS
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
 
@@ -24,17 +28,80 @@ extern Dali::Integration::Log::Filter* gFontClientLogFilter;
 
 namespace Dali::TextAbstraction::Internal
 {
+namespace
+{
 const float FROM_266        = 1.0f / 64.0f;
 const float POINTS_PER_INCH = 72.f;
 
-FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
+/**
+ * @brief Maximum rate of bitmap glyph resize.
+ * If scale factor is bigger than this value, we will not cache resized glyph.
+ * Else, resize bitmap glyph itself and cache it.
+ */
+constexpr float MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE = 1.5f;
+
+/**
+ * @brief Behavior about cache the rendered glyph cache.
+ */
+constexpr bool DEFAULT_ENABLE_CACHE_RENDERED_GLYPH = true;
+constexpr auto ENABLE_CACHE_RENDERED_GLYPH_ENV     = "DALI_ENABLE_CACHE_RENDERED_GLYPH";
+
+/**
+ * @brief Get whether we allow to cache rendered glyph from environment.
+ * If not settuped, default as true.
+ * @note This value fixed when we call it first time.
+ * @return True if we allow to cache rendered glyph.
+ */
+inline bool EnableCacheRenderedGlyph()
+{
+  using Dali::EnvironmentVariable::GetEnvironmentVariable;
+  static auto numberString = GetEnvironmentVariable(ENABLE_CACHE_RENDERED_GLYPH_ENV);
+  static auto number       = numberString ? (std::strtoul(numberString, nullptr, 10) ? true : false) : DEFAULT_ENABLE_CACHE_RENDERED_GLYPH;
+  return number;
+}
+
+/**
+ * @brief Policy about compress the cached rendered glyph.
+ * It will be used only if CacheRenderedGlyph is enabled
+ */
+constexpr auto DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY =
+#if !(defined(DALI_PROFILE_UBUNTU) || defined(ANDROID) || defined(WIN32) || defined(__APPLE__))
+  GlyphCacheManager::CompressionPolicyType::MEMORY; // If tizen target
+#else
+  GlyphCacheManager::CompressionPolicyType::SPEED; // If not tizen target
+#endif
+constexpr auto RENDERED_GLYPH_COMPRESS_POLICY_ENV = "DALI_RENDERED_GLYPH_COMPRESS_POLICY";
+
+/**
+ * @brief Get whether we allow to cache rendered glyph from environment.
+ * If not settuped, default value used, as defined above.
+ * @note This value fixed when we call it first time.
+ * @return SPEED if value start with 's' or 'S'. MEMORY if value start with 'm' or 'M'. otherwise, use default
+ */
+inline GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy()
+{
+  using Dali::EnvironmentVariable::GetEnvironmentVariable;
+  static auto policyString = GetEnvironmentVariable(RENDERED_GLYPH_COMPRESS_POLICY_ENV);
+
+  static auto policy = policyString ? policyString[0] == 's' || policyString[0] == 'S' ? GlyphCacheManager::CompressionPolicyType::SPEED
+                                                                                       : policyString[0] == 'm' || policyString[0] == 'M' ? GlyphCacheManager::CompressionPolicyType::MEMORY
+                                                                                                                                          : DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY
+                                    : DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY;
+  return policy;
+}
+} // namespace
+
+FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
                                      FT_Face            ftFace,
+                                     GlyphCacheManager* glyphCacheManager,
                                      const FontPath&    path,
                                      PointSize26Dot6    requestedPointSize,
                                      FaceIndex          face,
                                      const FontMetrics& metrics)
 : mFreeTypeLibrary(freeTypeLibrary),
   mFreeTypeFace(ftFace),
+  mGlyphCacheManager(glyphCacheManager),
+  mHarfBuzzProxyFont(),
   mPath(path),
   mRequestedPointSize(requestedPointSize),
   mFaceIndex(face),
@@ -50,8 +117,9 @@ FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
 {
 }
 
-FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
+FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
                                      FT_Face            ftFace,
+                                     GlyphCacheManager* glyphCacheManager,
                                      const FontPath&    path,
                                      PointSize26Dot6    requestedPointSize,
                                      FaceIndex          face,
@@ -62,6 +130,8 @@ FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
                                      bool               hasColorTables)
 : mFreeTypeLibrary(freeTypeLibrary),
   mFreeTypeFace(ftFace),
+  mGlyphCacheManager(glyphCacheManager),
+  mHarfBuzzProxyFont(),
   mPath(path),
   mRequestedPointSize(requestedPointSize),
   mFaceIndex(face),
@@ -77,6 +147,52 @@ FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
 {
 }
 
+// Move constructor. font client plugin container may call this.
+// Note that we make nullptr of some reference sensitive values here.
+FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs)
+: mFreeTypeLibrary(rhs.mFreeTypeLibrary)
+{
+  mFreeTypeFace       = rhs.mFreeTypeFace;
+  mGlyphCacheManager  = rhs.mGlyphCacheManager;
+  mHarfBuzzProxyFont  = std::move(rhs.mHarfBuzzProxyFont);
+  mPath               = std::move(rhs.mPath);
+  mRequestedPointSize = rhs.mRequestedPointSize;
+  mFaceIndex          = rhs.mFaceIndex;
+  mMetrics            = rhs.mMetrics;
+  mCharacterSet       = rhs.mCharacterSet;
+  mFixedSizeIndex     = rhs.mFixedSizeIndex;
+  mFixedWidthPixels   = rhs.mFixedWidthPixels;
+  mFixedHeightPixels  = rhs.mFixedWidthPixels;
+  mVectorFontId       = rhs.mVectorFontId;
+  mFontId             = rhs.mFontId;
+  mIsFixedSizeBitmap  = rhs.mIsFixedSizeBitmap;
+  mHasColorTables     = rhs.mHasColorTables;
+
+  rhs.mFreeTypeFace      = nullptr;
+  rhs.mGlyphCacheManager = nullptr;
+}
+
+FontFaceCacheItem::~FontFaceCacheItem()
+{
+  // delete cached glyph informations before free face.
+  if(mGlyphCacheManager)
+  {
+    mGlyphCacheManager->RemoveGlyphFromFace(mFreeTypeFace);
+  }
+
+  // delete harfbuzz proxy font before free face.
+  if(mHarfBuzzProxyFont)
+  {
+    mHarfBuzzProxyFont.reset();
+  }
+
+  // Free face.
+  if(mFreeTypeFace)
+  {
+    FT_Done_Face(mFreeTypeFace);
+  }
+}
+
 void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const
 {
   metrics = mMetrics;
@@ -90,40 +206,45 @@ void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVer
     {
       const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
 
-      metrics.ascender           = metrics.ascender * scaleFactor;
-      metrics.descender          = metrics.descender * scaleFactor;
-      metrics.height             = metrics.height * scaleFactor;
+      metrics.ascender           = round(metrics.ascender * scaleFactor);
+      metrics.descender          = round(metrics.descender * scaleFactor);
+      metrics.height             = round(metrics.height * scaleFactor);
       metrics.underlinePosition  = metrics.underlinePosition * scaleFactor;
       metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
     }
   }
 }
 
-bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertical, bool horizontal) const
+bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVertical, bool horizontal) const
 {
   bool success(true);
 
-  FT_Face ftFace = mFreeTypeFace;
+  GlyphCacheManager::GlyphCacheData glyphData;
+  FT_Error                          error;
 
 #ifdef FREETYPE_BITMAP_SUPPORT
   // Check to see if we should be loading a Fixed Size bitmap?
   if(mIsFixedSizeBitmap)
   {
-    FT_Select_Size(ftFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
-    int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
+    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, glyphData, error);
+
     if(FT_Err_Ok == error)
     {
-      glyph.width    = mFixedWidthPixels;
-      glyph.height   = mFixedHeightPixels;
-      glyph.advance  = mFixedWidthPixels;
-      glyph.xBearing = 0.0f;
+      glyphInfo.width    = mFixedWidthPixels;
+      glyphInfo.height   = mFixedHeightPixels;
+      glyphInfo.advance  = mFixedWidthPixels;
+      glyphInfo.xBearing = 0.0f;
+
+      const auto& metrics = glyphData.mGlyphMetrics;
+
       if(horizontal)
       {
-        glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
+        glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
       }
       else
       {
-        glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
+        glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
       }
 
       // Adjust the metrics if the fixed-size font should be down-scaled
@@ -132,14 +253,24 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertic
       if(desiredFixedSize > 0.f)
       {
         const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
+        glyphInfo.width         = round(glyphInfo.width * scaleFactor);
+        glyphInfo.height        = round(glyphInfo.height * scaleFactor);
+        glyphInfo.advance       = round(glyphInfo.advance * scaleFactor);
+        glyphInfo.xBearing      = round(glyphInfo.xBearing * scaleFactor);
+        glyphInfo.yBearing      = round(glyphInfo.yBearing * scaleFactor);
+
+        glyphInfo.scaleFactor = scaleFactor;
 
-        glyph.width    = glyph.width * scaleFactor;
-        glyph.height   = glyph.height * scaleFactor;
-        glyph.advance  = glyph.advance * scaleFactor;
-        glyph.xBearing = glyph.xBearing * scaleFactor;
-        glyph.yBearing = glyph.yBearing * scaleFactor;
+        if(scaleFactor < MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE)
+        {
+          // Resize bitmap glyph and cache it due to the performance issue.
+          // If scaleFactor is too big, cached bitmap may hold too big memory.
+          // So, we only hold small enough case.
 
-        glyph.scaleFactor = scaleFactor;
+          // 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));
+        }
       }
     }
     else
@@ -154,60 +285,60 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertic
     // 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?
-    int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);
+    mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, 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
     // advance as Harfbuzz doesn't apply any SW emboldening to calculate
     // the advance of the glyph.
-    const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
 
     if(FT_Err_Ok == error)
     {
-      const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
-      if(isEmboldeningRequired)
-      {
-        // Does the software bold.
-        FT_GlyphSlot_Embolden(ftFace->glyph);
-      }
+      const auto& metrics = glyphData.mGlyphMetrics;
 
-      glyph.width  = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
-      glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
+      glyphInfo.width  = static_cast<float>(metrics.width) * FROM_266;
+      glyphInfo.height = static_cast<float>(metrics.height) * FROM_266;
       if(horizontal)
       {
-        glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
-        glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
+        glyphInfo.xBearing += static_cast<float>(metrics.horiBearingX) * FROM_266;
+        glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
       }
       else
       {
-        glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
-        glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
+        glyphInfo.xBearing += static_cast<float>(metrics.vertBearingX) * FROM_266;
+        glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
       }
+      glyphInfo.advance = round(glyphInfo.advance);
 
-      if(isEmboldeningRequired && !Dali::EqualsZero(width))
+      const bool isEmboldeningRequired = glyphInfo.isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
+      if(isEmboldeningRequired)
       {
-        // If the glyph is emboldened by software, the advance is multiplied by a
-        // scale factor to make it slightly bigger.
-        glyph.advance *= (glyph.width / width);
+        // Get dummy glyph data without embolden.
+        GlyphCacheManager::GlyphCacheData dummyData;
+        if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error))
+        {
+          // If the glyph is emboldened by software, the advance is multiplied by a
+          // scale factor to make it slightly bigger.
+          const float width = static_cast<float>(dummyData.mGlyphMetrics.width) * FROM_266;
+          if(!EqualsZero(width))
+          {
+            glyphInfo.advance *= (glyphInfo.width / width);
+          }
+        }
       }
 
       // Use the bounding box of the bitmap to correct the metrics.
       // For some fonts i.e the SNum-3R the metrics need to be corrected,
       // otherwise the glyphs 'dance' up and down depending on the
       // font's point size.
-
-      FT_Glyph ftGlyph;
-      error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);
+      FT_Glyph glyph = glyphData.mGlyph;
 
       FT_BBox bbox;
-      FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
-
-      const float descender = glyph.height - glyph.yBearing;
-      glyph.height          = (bbox.yMax - bbox.yMin) * FROM_266;
-      glyph.yBearing        = glyph.height - round(descender);
+      FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
 
-      // Created FT_Glyph object must be released with FT_Done_Glyph
-      FT_Done_Glyph(ftGlyph);
+      const float descender = glyphInfo.height - glyphInfo.yBearing;
+      glyphInfo.height      = (bbox.yMax - bbox.yMin) * FROM_266;
+      glyphInfo.yBearing    = glyphInfo.height - round(descender);
     }
     else
     {
@@ -221,16 +352,17 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertic
  * @brief Create a bitmap representation of a glyph from a face font
  *
  * @param[in]  glyphIndex        The index of a glyph within the specified font.
- * @param[in]  isItalicRequired  Whether the glyph requires italic style.
- * @param[in]  isBoldRequired    Whether the glyph requires bold style.
  * @param[out] data              The bitmap data.
  * @param[in]  outlineWidth      The width of the glyph outline in pixels.
+ * @param[in]  isItalicRequired  Whether the glyph requires italic style.
+ * @param[in]  isBoldRequired    Whether the glyph requires bold style.
  */
 void FontFaceCacheItem::CreateBitmap(
   GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
 {
-  FT_Face  ftFace = mFreeTypeFace;
-  FT_Error error;
+  GlyphCacheManager::GlyphCacheData glyphData;
+  FT_Error                          error;
+  FT_Int32                          loadFlag;
   // For the software italics.
   bool isShearRequired = false;
 
@@ -238,7 +370,7 @@ void FontFaceCacheItem::CreateBitmap(
   // Check to see if this is fixed size bitmap
   if(mIsFixedSizeBitmap)
   {
-    error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
+    loadFlag = FT_LOAD_COLOR;
   }
   else
 #endif
@@ -246,83 +378,98 @@ void FontFaceCacheItem::CreateBitmap(
     // 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?
-    error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
+    loadFlag = FT_LOAD_NO_AUTOHINT;
   }
+  mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, glyphData, error);
+
   if(FT_Err_Ok == error)
   {
-    if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
-    {
-      // Does the software bold.
-      FT_GlyphSlot_Embolden(ftFace->glyph);
-    }
-
-    if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
+    if(isItalicRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_ITALIC))
     {
       // Will do the software italic.
       isShearRequired = true;
     }
 
-    FT_Glyph glyph;
-    error = FT_Get_Glyph(ftFace->glyph, &glyph);
-
-    // Convert to bitmap if necessary
-    if(FT_Err_Ok == error)
+    if(!glyphData.mIsBitmap)
     {
-      if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
-      {
-        int  offsetX = 0, offsetY = 0;
-        bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
+      // Convert to bitmap if necessary
+      FT_Glyph glyph = glyphData.mGlyph;
 
-        // Create a bitmap for the outline
-        if(isOutlineGlyph)
+      DALI_ASSERT_ALWAYS(glyph->format != FT_GLYPH_FORMAT_BITMAP && "Something wrong with cashing. Some bitmap glyph cached failed.");
+
+      int  offsetX = 0, offsetY = 0;
+      bool isOutlineGlyph       = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
+      bool isStrokeGlyphSuccess = false;
+
+      // Create a bitmap for the outline
+      if(isOutlineGlyph)
+      {
+        // Retrieve the horizontal and vertical distance from the current pen position to the
+        // left and top border of the glyph bitmap for a normal glyph before applying the outline.
+        if(FT_Err_Ok == error)
         {
-          // Retrieve the horizontal and vertical distance from the current pen position to the
-          // left and top border of the glyph bitmap for a normal glyph before applying the outline.
+          // Copy new glyph, and keep original cached glyph.
+          error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0);
           if(FT_Err_Ok == error)
           {
-            FT_Glyph normalGlyph;
-            error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);
+            FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
 
-            error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
-            if(FT_Err_Ok == error)
-            {
-              FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);
-
-              offsetX = bitmapGlyph->left;
-              offsetY = bitmapGlyph->top;
-            }
+            offsetX = bitmapGlyph->left;
+            offsetY = bitmapGlyph->top;
 
-            // Created FT_Glyph object must be released with FT_Done_Glyph
-            FT_Done_Glyph(normalGlyph);
+            // Copied FT_Glyph object must be released with FT_Done_Glyph
+            FT_Done_Glyph(glyph);
           }
 
-          // Now apply the outline
+          // Replace as original glyph
+          glyph = glyphData.mGlyph;
+        }
 
-          // Set up a stroker
-          FT_Stroker stroker;
-          error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
+        // Now apply the outline
+
+        // Set up a stroker
+        FT_Stroker stroker;
+        error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
+
+        if(FT_Err_Ok == error)
+        {
+          // Copy glyph pointer for release memory.
+          FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
+          error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 0);
 
           if(FT_Err_Ok == error)
           {
-            FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
-            error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
-
-            if(FT_Err_Ok == error)
-            {
-              FT_Stroker_Done(stroker);
-            }
-            else
-            {
-              DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
-            }
+            FT_Stroker_Done(stroker);
+            isStrokeGlyphSuccess = true;
           }
           else
           {
-            DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
+            DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
           }
         }
+        else
+        {
+          DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
+        }
+      }
 
-        error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
+      const bool ableUseCachedRenderedGlyph = EnableCacheRenderedGlyph() && !isOutlineGlyph && !isShearRequired;
+
+      // If we cache rendered glyph, and if we can use it, use cached thing first.
+      if(ableUseCachedRenderedGlyph && glyphData.mRenderedBuffer)
+      {
+        data.buffer          = glyphData.mRenderedBuffer->buffer;
+        data.width           = glyphData.mRenderedBuffer->width;
+        data.height          = glyphData.mRenderedBuffer->height;
+        data.format          = glyphData.mRenderedBuffer->format;
+        data.compressionType = glyphData.mRenderedBuffer->compressionType;
+        data.isBufferOwned   = false;
+      }
+      else
+      {
+        // Copy new glyph, and keep original cached glyph.
+        // If we already copy new glyph by stroke, just re-use that.
+        error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, isStrokeGlyphSuccess);
         if(FT_Err_Ok == error)
         {
           FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
@@ -334,23 +481,52 @@ void FontFaceCacheItem::CreateBitmap(
             data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
           }
 
-          ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
+          // If we can cache this bitmapGlyph, store it.
+          // Note : We will call this API once per each glyph.
+          if(ableUseCachedRenderedGlyph)
+          {
+            mGlyphCacheManager->CacheRenderedGlyphBuffer(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
+
+            GlyphCacheManager::GlyphCacheData dummyData;
+            mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, dummyData, error);
+
+            if(DALI_LIKELY(FT_Err_Ok == error && dummyData.mRenderedBuffer))
+            {
+              data.buffer          = dummyData.mRenderedBuffer->buffer;
+              data.width           = dummyData.mRenderedBuffer->width;
+              data.height          = dummyData.mRenderedBuffer->height;
+              data.format          = dummyData.mRenderedBuffer->format;
+              data.compressionType = dummyData.mRenderedBuffer->compressionType;
+              data.isBufferOwned   = false;
+            }
+            else
+            {
+              // Something problem during cache or get rendered glyph buffer.
+              // Move bitmap buffer into data.buffer
+              ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
+            }
+          }
+          else
+          {
+            // Move bitmap buffer into data.buffer
+            ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
+          }
+
+          // Copied FT_Glyph object must be released with FT_Done_Glyph
+          FT_Done_Glyph(glyph);
         }
         else
         {
           DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
         }
       }
-      else
-      {
-        ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
-      }
-
-      data.isColorEmoji = mIsFixedSizeBitmap;
-
-      // Created FT_Glyph object must be released with FT_Done_Glyph
-      FT_Done_Glyph(glyph);
     }
+    else
+    {
+      ConvertBitmap(data, *glyphData.mBitmap, isShearRequired);
+    }
+
+    data.isColorEmoji = mIsFixedSizeBitmap;
   }
   else
   {
@@ -366,7 +542,8 @@ bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
   // Check to see if this is fixed size bitmap
   if(mHasColorTables)
   {
-    error = FT_Load_Glyph(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR);
+    GlyphCacheManager::GlyphCacheData dummyData;
+    mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR, false, dummyData, error);
   }
 #endif
   return FT_Err_Ok == error;
@@ -416,4 +593,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));
+  }
+  return mHarfBuzzProxyFont->GetHarfBuzzFont();
+}
+
 } // namespace Dali::TextAbstraction::Internal