Merge "[AT-SPI] Rework intercepting key events" into devel/master
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-face-glyph-cache-manager.cpp
index 1ced0b5..0bed3f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -36,9 +36,8 @@ namespace
 constexpr uint32_t THRESHOLD_WIDTH_FOR_RLE4_COMPRESSION = 8; // The smallest width of glyph that we use RLE4 method.
 } // namespace
 
-GlyphCacheManager::GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyphCache)
-: mFreeTypeFace(ftFace),
-  mGlyphCacheMaxSize(maxNumberOfGlyphCache),
+GlyphCacheManager::GlyphCacheManager(std::size_t maxNumberOfGlyphCache)
+: mGlyphCacheMaxSize(maxNumberOfGlyphCache),
   mLRUGlyphCache(mGlyphCacheMaxSize)
 {
   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager Create with maximum size : %d\n", static_cast<int>(mGlyphCacheMaxSize));
@@ -46,27 +45,21 @@ GlyphCacheManager::GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyp
 
 GlyphCacheManager::~GlyphCacheManager()
 {
-  while(!mLRUGlyphCache.IsEmpty())
-  {
-    auto removedData = mLRUGlyphCache.Pop();
-
-    // Release Glyph data resource
-    removedData.ReleaseGlyphData();
-  }
-  mLRUGlyphCache.Clear();
+  ClearCache();
 }
 
 bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
-  const GlyphIndex index,
-  const FT_Int32   flag,
-  const bool       isBoldRequired,
-  GlyphCacheData&  glyphData,
-  FT_Error&        error)
+  const FT_Face      freeTypeFace,
+  const GlyphIndex   index,
+  const FT_Int32     flag,
+  const bool         isBoldRequired,
+  GlyphCacheDataPtr& glyphDataPtr,
+  FT_Error&          error)
 {
   // Append some error value here instead of FT_Err_Ok.
   error = static_cast<FT_Error>(-1);
 
-  const GlyphCacheKey key  = GlyphCacheKey(index, flag, isBoldRequired);
+  const GlyphCacheKey key  = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired);
   auto                iter = mLRUGlyphCache.Find(key);
 
   if(iter == mLRUGlyphCache.End())
@@ -76,19 +69,21 @@ bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
     {
       auto removedData = mLRUGlyphCache.Pop();
 
-      DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Remove oldest cache for glyph : %p\n", removedData.mGlyph);
-
-      // Release Glyph data resource
-      removedData.ReleaseGlyphData();
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Remove oldest cache for glyph : %p\n", removedData->mGlyph);
     }
 
-    const bool loadSuccess = LoadGlyphDataFromIndex(index, flag, isBoldRequired, glyphData, error);
+    // Create new GlyphCacheData.
+    glyphDataPtr = std::make_shared<GlyphCacheData>();
+
+    GlyphCacheData& glyphData = *glyphDataPtr.get();
+
+    const bool loadSuccess = LoadGlyphDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphData, error);
     if(loadSuccess)
     {
       // Copy and cached data.
-      mLRUGlyphCache.Push(key, glyphData);
+      mLRUGlyphCache.Push(key, glyphDataPtr);
 
-      DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for face : %p, index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", freeTypeFace, index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
     }
 
     return loadSuccess;
@@ -98,36 +93,37 @@ bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
     error = FT_Err_Ok;
 
     // We already notify that we use this glyph. And now, copy cached data.
-    glyphData = iter->element;
+    glyphDataPtr = mLRUGlyphCache.GetElement(iter);
 
-    DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
+    DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for face : %p, index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", freeTypeFace, index, static_cast<int>(flag), isBoldRequired, glyphDataPtr->mIsBitmap, glyphDataPtr->mGlyph);
     return true;
   }
 }
 
 bool GlyphCacheManager::LoadGlyphDataFromIndex(
+  const FT_Face    freeTypeFace,
   const GlyphIndex index,
   const FT_Int32   flag,
   const bool       isBoldRequired,
   GlyphCacheData&  glyphData,
   FT_Error&        error)
 {
-  error = FT_Load_Glyph(mFreeTypeFace, index, flag);
+  error = FT_Load_Glyph(freeTypeFace, index, flag);
   if(FT_Err_Ok == error)
   {
-    glyphData.mStyleFlags = mFreeTypeFace->style_flags;
+    glyphData.mStyleFlags = freeTypeFace->style_flags;
 
     const bool isEmboldeningRequired = isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
     if(isEmboldeningRequired)
     {
       // Does the software bold.
-      FT_GlyphSlot_Embolden(mFreeTypeFace->glyph);
+      FT_GlyphSlot_Embolden(freeTypeFace->glyph);
     }
 
-    glyphData.mGlyphMetrics = mFreeTypeFace->glyph->metrics;
+    glyphData.mGlyphMetrics = freeTypeFace->glyph->metrics;
     glyphData.mIsBitmap     = false;
     // Load glyph
-    error = FT_Get_Glyph(mFreeTypeFace->glyph, &glyphData.mGlyph);
+    error = FT_Get_Glyph(freeTypeFace->glyph, &glyphData.mGlyph);
 
     if(glyphData.mGlyph->format == FT_GLYPH_FORMAT_BITMAP)
     {
@@ -137,7 +133,7 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex(
       // Copy rendered bitmap
       // TODO : Is there any way to keep bitmap buffer without copy?
       glyphData.mBitmap  = new FT_Bitmap();
-      *glyphData.mBitmap = mFreeTypeFace->glyph->bitmap;
+      *glyphData.mBitmap = freeTypeFace->glyph->bitmap;
 
       // New allocate buffer
       size_t bufferSize = 0;
@@ -172,7 +168,18 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex(
       {
         glyphData.mIsBitmap       = true;
         glyphData.mBitmap->buffer = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
-        memcpy(glyphData.mBitmap->buffer, mFreeTypeFace->glyph->bitmap.buffer, bufferSize);
+        if(DALI_UNLIKELY(!glyphData.mBitmap->buffer))
+        {
+          DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", bufferSize * sizeof(uint8_t));
+          delete glyphData.mBitmap;
+          glyphData.mIsBitmap = false;
+          glyphData.mBitmap   = nullptr;
+          error               = static_cast<FT_Error>(-1);
+        }
+        else
+        {
+          memcpy(glyphData.mBitmap->buffer, freeTypeFace->glyph->bitmap.buffer, bufferSize);
+        }
       }
       else
       {
@@ -195,6 +202,7 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex(
 }
 
 void GlyphCacheManager::ResizeBitmapGlyph(
+  const FT_Face    freeTypeFace,
   const GlyphIndex index,
   const FT_Int32   flag,
   const bool       isBoldRequired,
@@ -206,54 +214,65 @@ void GlyphCacheManager::ResizeBitmapGlyph(
     // Skip this API if desired size is zero
     return;
   }
-  FT_Error       error;
-  GlyphCacheData originGlyphData;
-  if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
+  FT_Error          error;
+  GlyphCacheDataPtr glyphDataPtr;
+  if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphDataPtr, error))
   {
-    if(DALI_LIKELY(originGlyphData.mIsBitmap && originGlyphData.mBitmap))
+    GlyphCacheData& glyphData = *glyphDataPtr.get();
+    if(DALI_LIKELY(glyphData.mIsBitmap && glyphData.mBitmap))
     {
-      const bool requiredResize = (originGlyphData.mBitmap->rows != desiredHeight) || (originGlyphData.mBitmap->width != desiredWidth);
+      const bool requiredResize = (glyphData.mBitmap->rows != desiredHeight) || (glyphData.mBitmap->width != desiredWidth);
       if(requiredResize)
       {
-        // originalGlyphData is copy data. For change cached information, we should access as iterator.
-        const GlyphCacheKey key  = GlyphCacheKey(index, flag, isBoldRequired);
-        auto                iter = mLRUGlyphCache.Find(key);
-
-        GlyphCacheData& destinationGlpyhData = iter->element;
-
-        const ImageDimensions inputDimensions(destinationGlpyhData.mBitmap->width, destinationGlpyhData.mBitmap->rows);
+        const ImageDimensions inputDimensions(glyphData.mBitmap->width, glyphData.mBitmap->rows);
         const ImageDimensions desiredDimensions(desiredWidth, desiredHeight);
 
         uint8_t* desiredBuffer = nullptr;
 
-        switch(destinationGlpyhData.mBitmap->pixel_mode)
+        switch(glyphData.mBitmap->pixel_mode)
         {
           case FT_PIXEL_MODE_GRAY:
           {
-            if(destinationGlpyhData.mBitmap->pitch == static_cast<int>(destinationGlpyhData.mBitmap->width))
+            if(glyphData.mBitmap->pitch == static_cast<int>(glyphData.mBitmap->width))
             {
               desiredBuffer = (uint8_t*)malloc(desiredWidth * desiredHeight * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
-              // Resize bitmap here.
-              Dali::Internal::Platform::LanczosSample1BPP(destinationGlpyhData.mBitmap->buffer,
-                                                          inputDimensions,
-                                                          destinationGlpyhData.mBitmap->width,
-                                                          desiredBuffer,
-                                                          desiredDimensions);
+
+              if(DALI_UNLIKELY(!desiredBuffer))
+              {
+                DALI_LOG_ERROR("malloc is failed. request malloc size : %u x %u x 1\n", desiredWidth, desiredHeight);
+              }
+              else
+              {
+                // Resize bitmap here.
+                Dali::Internal::Platform::LanczosSample1BPP(glyphData.mBitmap->buffer,
+                                                            inputDimensions,
+                                                            glyphData.mBitmap->width,
+                                                            desiredBuffer,
+                                                            desiredDimensions);
+              }
             }
             break;
           }
 #ifdef FREETYPE_BITMAP_SUPPORT
           case FT_PIXEL_MODE_BGRA:
           {
-            if(destinationGlpyhData.mBitmap->pitch == static_cast<int>(destinationGlpyhData.mBitmap->width << 2u))
+            if(glyphData.mBitmap->pitch == static_cast<int>(glyphData.mBitmap->width << 2u))
             {
               desiredBuffer = (uint8_t*)malloc((desiredWidth * desiredHeight * sizeof(uint8_t)) << 2u); // @note The caller is responsible for deallocating the bitmap data using free.
-              // Resize bitmap here.
-              Dali::Internal::Platform::LanczosSample4BPP(destinationGlpyhData.mBitmap->buffer,
-                                                          inputDimensions,
-                                                          destinationGlpyhData.mBitmap->width,
-                                                          desiredBuffer,
-                                                          desiredDimensions);
+
+              if(DALI_UNLIKELY(!desiredBuffer))
+              {
+                DALI_LOG_ERROR("malloc is failed. request malloc size : %u x %u x 4\n", desiredWidth, desiredHeight);
+              }
+              else
+              {
+                // Resize bitmap here.
+                Dali::Internal::Platform::LanczosSample4BPP(glyphData.mBitmap->buffer,
+                                                            inputDimensions,
+                                                            glyphData.mBitmap->width,
+                                                            desiredBuffer,
+                                                            desiredDimensions);
+              }
             }
             break;
           }
@@ -269,23 +288,23 @@ void GlyphCacheManager::ResizeBitmapGlyph(
         {
           // Success to resize bitmap glyph.
           // Release origin bitmap buffer.
-          free(destinationGlpyhData.mBitmap->buffer);
+          free(glyphData.mBitmap->buffer);
 
           // Replace as desired buffer and size.
-          destinationGlpyhData.mBitmap->buffer = desiredBuffer;
-          destinationGlpyhData.mBitmap->width  = desiredWidth;
-          destinationGlpyhData.mBitmap->rows   = desiredHeight;
-          switch(destinationGlpyhData.mBitmap->pixel_mode)
+          glyphData.mBitmap->buffer = desiredBuffer;
+          glyphData.mBitmap->width  = desiredWidth;
+          glyphData.mBitmap->rows   = desiredHeight;
+          switch(glyphData.mBitmap->pixel_mode)
           {
             case FT_PIXEL_MODE_GRAY:
             {
-              destinationGlpyhData.mBitmap->pitch = desiredWidth;
+              glyphData.mBitmap->pitch = desiredWidth;
               break;
             }
 #ifdef FREETYPE_BITMAP_SUPPORT
             case FT_PIXEL_MODE_BGRA:
             {
-              destinationGlpyhData.mBitmap->pitch = desiredWidth << 2u;
+              glyphData.mBitmap->pitch = desiredWidth << 2u;
               break;
             }
 #endif
@@ -297,6 +316,7 @@ void GlyphCacheManager::ResizeBitmapGlyph(
 }
 
 void GlyphCacheManager::CacheRenderedGlyphBuffer(
+  const FT_Face               freeTypeFace,
   const GlyphIndex            index,
   const FT_Int32              flag,
   const bool                  isBoldRequired,
@@ -308,26 +328,21 @@ void GlyphCacheManager::CacheRenderedGlyphBuffer(
     // Skip this API if rendered bitmap size is zero
     return;
   }
-  FT_Error       error;
-  GlyphCacheData originGlyphData;
-  if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
+  FT_Error          error;
+  GlyphCacheDataPtr glyphDataPtr;
+  if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphDataPtr, error))
   {
-    if(DALI_LIKELY(!originGlyphData.mIsBitmap && originGlyphData.mRenderedBuffer == nullptr))
+    GlyphCacheData& glyphData = *glyphDataPtr.get();
+    if(DALI_LIKELY(!glyphData.mIsBitmap && glyphData.mRenderedBuffer == nullptr))
     {
-      // originalGlyphData is copy data. For change cached information, we should access as iterator.
-      const GlyphCacheKey key  = GlyphCacheKey(index, flag, isBoldRequired);
-      auto                iter = mLRUGlyphCache.Find(key);
-
-      GlyphCacheData& destinationGlpyhData = iter->element;
-
-      destinationGlpyhData.mRenderedBuffer = new TextAbstraction::FontClient::GlyphBufferData();
-      if(DALI_UNLIKELY(!destinationGlpyhData.mRenderedBuffer))
+      glyphData.mRenderedBuffer = new TextAbstraction::GlyphBufferData();
+      if(DALI_UNLIKELY(!glyphData.mRenderedBuffer))
       {
         DALI_LOG_ERROR("Allocate GlyphBufferData failed\n");
         return;
       }
 
-      TextAbstraction::FontClient::GlyphBufferData& renderBuffer = *destinationGlpyhData.mRenderedBuffer;
+      TextAbstraction::GlyphBufferData& renderBuffer = *glyphData.mRenderedBuffer;
 
       // Set basic informations.
       renderBuffer.width  = srcBitmap.width;
@@ -342,28 +357,28 @@ void GlyphCacheManager::CacheRenderedGlyphBuffer(
           if(policy == CompressionPolicyType::SPEED)
           {
             // If policy is SPEED, we will not compress bitmap.
-            renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+            renderBuffer.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
           }
           else
           {
             // If small enough glyph, compress as BPP4 method.
             if(srcBitmap.width < THRESHOLD_WIDTH_FOR_RLE4_COMPRESSION)
             {
-              renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4;
+              renderBuffer.compressionType = TextAbstraction::GlyphBufferData::CompressionType::BPP_4;
             }
             else
             {
-              renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4;
+              renderBuffer.compressionType = TextAbstraction::GlyphBufferData::CompressionType::RLE_4;
             }
           }
 
-          const auto compressedBufferSize = TextAbstraction::FontClient::GlyphBufferData::Compress(srcBitmap.buffer, renderBuffer);
+          const auto compressedBufferSize = TextAbstraction::GlyphBufferData::Compress(srcBitmap.buffer, renderBuffer);
           if(DALI_UNLIKELY(compressedBufferSize == 0u))
           {
             DALI_ASSERT_DEBUG(0 == "Compress failed at FT_PIXEL_MODE_GRAY");
             DALI_LOG_ERROR("Compress failed. Ignore cache\n");
-            delete destinationGlpyhData.mRenderedBuffer;
-            destinationGlpyhData.mRenderedBuffer = nullptr;
+            delete glyphData.mRenderedBuffer;
+            glyphData.mRenderedBuffer = nullptr;
             return;
           }
           break;
@@ -372,16 +387,16 @@ void GlyphCacheManager::CacheRenderedGlyphBuffer(
         case FT_PIXEL_MODE_BGRA:
         {
           // Copy buffer without compress
-          renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+          renderBuffer.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
           renderBuffer.format          = Pixel::BGRA8888;
 
-          const auto compressedBufferSize = TextAbstraction::FontClient::GlyphBufferData::Compress(srcBitmap.buffer, renderBuffer);
+          const auto compressedBufferSize = TextAbstraction::GlyphBufferData::Compress(srcBitmap.buffer, renderBuffer);
           if(DALI_UNLIKELY(compressedBufferSize == 0u))
           {
             DALI_ASSERT_DEBUG(0 == "Compress failed at FT_PIXEL_MODE_BGRA");
             DALI_LOG_ERROR("Compress failed. Ignore cache\n");
-            delete destinationGlpyhData.mRenderedBuffer;
-            destinationGlpyhData.mRenderedBuffer = nullptr;
+            delete glyphData.mRenderedBuffer;
+            glyphData.mRenderedBuffer = nullptr;
             return;
           }
           break;
@@ -390,8 +405,8 @@ void GlyphCacheManager::CacheRenderedGlyphBuffer(
         default:
         {
           DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::CacheRenderedGlyphBuffer. FontClient Unable to create Bitmap of this PixelType\n");
-          delete destinationGlpyhData.mRenderedBuffer;
-          destinationGlpyhData.mRenderedBuffer = nullptr;
+          delete glyphData.mRenderedBuffer;
+          glyphData.mRenderedBuffer = nullptr;
           break;
         }
       }
@@ -399,11 +414,56 @@ void GlyphCacheManager::CacheRenderedGlyphBuffer(
   }
 }
 
+void GlyphCacheManager::RemoveGlyphFromFace(const FT_Face freeTypeFace)
+{
+  uint32_t removedItemCount = 0;
+
+  auto endIter = mLRUGlyphCache.End();
+  for(auto iter = mLRUGlyphCache.Begin(); iter != endIter;)
+  {
+    // Check whether this cached item has inputed freeTypeFace as key.
+    auto keyFace = mLRUGlyphCache.GetKey(iter).mFreeTypeFace;
+    if(keyFace == freeTypeFace)
+    {
+      ++removedItemCount;
+      iter = mLRUGlyphCache.Erase(iter);
+    }
+    else
+    {
+      ++iter;
+    }
+  }
+
+  DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::RemoveGlyphFromFace. Remove all cached glyph with face : %p, removed glyph count : %u\n", freeTypeFace, removedItemCount);
+}
+
+void GlyphCacheManager::ClearCache(const std::size_t remainCount)
+{
+  if(remainCount == 0u)
+  {
+    // Clear all cache.
+    mLRUGlyphCache.Clear();
+  }
+  else
+  {
+    // While the cache count is bigger than remainCount, remove oldest glyph.
+    while(mLRUGlyphCache.Count() > remainCount)
+    {
+      auto removedData = mLRUGlyphCache.Pop();
+
+      DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::ClearCache[%zu / %zu]. Remove oldest cache for glyph : %p\n", mLRUGlyphCache.Count(), remainCount, removedData->mGlyph);
+    }
+  }
+}
+
+// GlyphCacheManager::GlyphCacheData
+
 void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
 {
   if(mIsBitmap && mBitmap)
   {
     // Created FT_Bitmap object must be released with FT_Bitmap_Done
+    // But, this class's mBitmap it not an actual FT_Bitmap object. So free buffer is enough.
     free(mBitmap->buffer); // This buffer created by malloc
 
     delete mBitmap;
@@ -419,7 +479,79 @@ void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
   if(mRenderedBuffer)
   {
     delete mRenderedBuffer;
+    mRenderedBuffer = nullptr;
+  }
+
+  mStyleFlags = 0;
+}
+
+GlyphCacheManager::GlyphCacheData::GlyphCacheData()
+: mGlyph{nullptr},
+  mGlyphMetrics{},
+  mStyleFlags{0},
+  mIsBitmap{false},
+  mRenderedBuffer{nullptr}
+{
+}
+
+GlyphCacheManager::GlyphCacheData::~GlyphCacheData()
+{
+  ReleaseGlyphData();
+}
+
+GlyphCacheManager::GlyphCacheData::GlyphCacheData(GlyphCacheData&& rhs) noexcept
+: mGlyph{nullptr},
+  mGlyphMetrics{},
+  mStyleFlags{0},
+  mIsBitmap{false},
+  mRenderedBuffer{nullptr}
+{
+  *this = std::move(rhs);
+}
+
+GlyphCacheManager::GlyphCacheData& GlyphCacheManager::GlyphCacheData::operator=(GlyphCacheData&& rhs) noexcept
+{
+  // Self-assignment detection
+  if(this == &rhs)
+  {
+    return *this;
   }
+
+  // Delete self data first.
+  ReleaseGlyphData();
+
+  mIsBitmap = false;
+
+  if(rhs.mIsBitmap && rhs.mBitmap)
+  {
+    mIsBitmap = true;
+    mBitmap   = rhs.mBitmap;
+
+    rhs.mBitmap = nullptr;
+  }
+  else if(rhs.mGlyph)
+  {
+    mGlyph = rhs.mGlyph;
+
+    rhs.mGlyph = nullptr;
+  }
+  else
+  {
+    mGlyph = nullptr;
+  }
+
+  if(rhs.mRenderedBuffer)
+  {
+    mRenderedBuffer     = rhs.mRenderedBuffer;
+    rhs.mRenderedBuffer = nullptr;
+  }
+  else
+  {
+    mRenderedBuffer = nullptr;
+  }
+
+  mStyleFlags = rhs.mStyleFlags;
+  return *this;
 }
 
 } // namespace Dali::TextAbstraction::Internal