Merge branch 'devel/master' into tizen
authorjoogab.yun <joogab.yun@samsung.com>
Mon, 4 Jul 2022 05:10:11 +0000 (14:10 +0900)
committerjoogab.yun <joogab.yun@samsung.com>
Mon, 4 Jul 2022 05:10:11 +0000 (14:10 +0900)
Change-Id: Ia0eeef1cb84097b34da46631af6ff71fc6cded58

26 files changed:
automated-tests/src/dali-adaptor-internal/utc-Dali-FontClient.cpp
dali/devel-api/adaptor-framework/native-image-source-queue.cpp
dali/devel-api/adaptor-framework/native-image-source-queue.h
dali/devel-api/adaptor-framework/window-devel.cpp
dali/devel-api/adaptor-framework/window-devel.h
dali/devel-api/text-abstraction/font-client.cpp
dali/devel-api/text-abstraction/font-client.h
dali/integration-api/adaptor-framework/scene-holder-impl.h
dali/integration-api/adaptor-framework/scene-holder.cpp
dali/integration-api/adaptor-framework/scene-holder.h
dali/internal/imaging/common/image-operations.cpp
dali/internal/imaging/common/image-operations.h
dali/internal/imaging/tizen/native-image-source-queue-impl-tizen.cpp
dali/internal/text/text-abstraction/cairo-renderer.cpp
dali/internal/text/text-abstraction/plugin/bitmap-font-cache-item.cpp
dali/internal/text/text-abstraction/plugin/embedded-item.cpp
dali/internal/text/text-abstraction/plugin/font-client-utils.cpp
dali/internal/text/text-abstraction/plugin/font-client-utils.h
dali/internal/text/text-abstraction/plugin/font-face-cache-item.cpp
dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.cpp
dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h
dali/internal/text/text-abstraction/plugin/lru-cache-container.h
dali/internal/window-system/tizen-wayland/display-connection-impl-ecore-wl.cpp
dali/internal/window-system/tizen-wayland/display-connection-impl-ecore-wl.h
dali/public-api/dali-adaptor-version.cpp
packaging/dali-adaptor.spec

index 006f04d..208deb1 100644 (file)
@@ -173,11 +173,6 @@ int UtcDaliFontClientAtlasLimitationEnabled(void)
   DALI_TEST_GREATER(MAX_WIDTH_FIT_IN_ATLAS, glyphBufferData2000.width, TEST_LOCATION);
   DALI_TEST_GREATER(MAX_HEIGHT_FIT_IN_ATLAS, glyphBufferData2000.height, TEST_LOCATION);
 
-  // Release copied memories
-  free(glyphBufferData200.buffer);
-  free(glyphBufferData1000.buffer);
-  free(glyphBufferData2000.buffer);
-
   END_TEST;
 }
 
index d55e178..9655a5a 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.
@@ -38,7 +38,7 @@ NativeImageSourceQueuePtr NativeImageSourceQueue::New(uint32_t width, uint32_t h
 NativeImageSourceQueuePtr NativeImageSourceQueue::New(Any nativeImageSourceQueue)
 {
   //ColorFormat will be ignored.
-  NativeImageSourceQueuePtr image = new NativeImageSourceQueue(0, 0, ColorFormat::RGBA8888, nativeImageSourceQueue);
+  NativeImageSourceQueuePtr image = new NativeImageSourceQueue(0, 0, ColorFormat::BGRA8888, nativeImageSourceQueue);
   if(image->mImpl)
   {
     return image;
index cb62fc1..01ca473 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_NATIVE_IMAGE_SOURCE_QUEUE_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.
@@ -61,12 +61,16 @@ class DALI_ADAPTOR_API NativeImageSourceQueue : public NativeImageInterface
 public:
   /**
     * @brief Enumeration for the instance when creating a native image, the color depth has to be specified.
+    * @note This ColorFormat follows pixel byte order.
     */
   enum class ColorFormat
   {
     RGB888,   /// 8 red bits, 8 green bits, 8 blue bits
     RGBA8888, /// 8 red bits, 8 green bits, 8 blue bits, alpha 8 bits
-    RGBX8888  /// 8 red bits, 8 green bits, 8 blue bits, and 8 ignored bits
+    RGBX8888, /// 8 red bits, 8 green bits, 8 blue bits, and 8 ignored bits
+    BGR888,   /// 8 blue bits, 8 green bits, 8 red bits
+    BGRA8888, /// 8 blue bits, 8 green bits, 8 red bits, alpha 8 bits
+    BGRX8888, /// 8 blue bits, 8 green bits, 8 red bits, and 8 ignored bits
   };
 
   /**
index c6ffb56..43565ed 100644 (file)
@@ -269,6 +269,11 @@ const TouchEvent& GetLastTouchEvent(Window window)
   return GetImplementation(window).GetLastTouchEvent();
 }
 
+InterceptKeyEventSignalType& InterceptKeyEventSignal(Window window)
+{
+  return GetImplementation(window).InterceptKeyEventSignal();
+}
+
 } // namespace DevelWindow
 
 } // namespace Dali
index 20bcc75..883cdaa 100644 (file)
@@ -36,23 +36,16 @@ struct TouchPoint;
 
 namespace DevelWindow
 {
-typedef Signal<void()> EventProcessingFinishedSignalType; ///< Event Processing finished signal type
-
-typedef Signal<void(const KeyEvent&)> KeyEventSignalType; ///< Key event signal type
-
-typedef Signal<void(const TouchEvent&)> TouchEventSignalType; ///< Touch signal type
-
-typedef Signal<void(const WheelEvent&)> WheelEventSignalType; ///< Touched signal type
-
-typedef Signal<void(Window, bool)> VisibilityChangedSignalType; ///< Visibility changed signal type
-
-typedef Signal<void(Window, WindowEffectState, WindowEffectType)> TransitionEffectEventSignalType; ///< Effect signal type and state
-
-typedef Signal<void()> KeyboardRepeatSettingsChangedSignalType; ///< Keyboard repeat settings changed signal type
-
-typedef Signal<void(const std::string&, const std::string&, const Property::Array&)> AuxiliaryMessageSignalType; ///< Auxiliary message signal type
-
-typedef Signal<void(Window, bool)> AccessibilityHighlightSignalType; ///< Accessibility Highlight signal type
+typedef Signal<void()>                                                               EventProcessingFinishedSignalType;       ///< Event Processing finished signal type
+typedef Signal<void(const KeyEvent&)>                                                KeyEventSignalType;                      ///< Key event signal type
+typedef Signal<void(const TouchEvent&)>                                              TouchEventSignalType;                    ///< Touch signal type
+typedef Signal<void(const WheelEvent&)>                                              WheelEventSignalType;                    ///< Wheel signal type
+typedef Signal<void(Window, bool)>                                                   VisibilityChangedSignalType;             ///< Visibility changed signal type
+typedef Signal<void(Window, WindowEffectState, WindowEffectType)>                    TransitionEffectEventSignalType;         ///< Effect signal type and state
+typedef Signal<void()>                                                               KeyboardRepeatSettingsChangedSignalType; ///< Keyboard repeat settings changed signal type
+typedef Signal<void(const std::string&, const std::string&, const Property::Array&)> AuxiliaryMessageSignalType;              ///< Auxiliary message signal type
+typedef Signal<void(Window, bool)>                                                   AccessibilityHighlightSignalType;        ///< Accessibility Highlight signal type
+typedef Signal<bool(const KeyEvent&)>                                                InterceptKeyEventSignalType;             ///< Intercept Key event signal type
 
 /**
  * @brief Creates an initialized handle to a new Window.
@@ -528,6 +521,17 @@ DALI_ADAPTOR_API const KeyEvent& GetLastKeyEvent(Window window);
  */
 DALI_ADAPTOR_API const TouchEvent& GetLastTouchEvent(Window window);
 
+/**
+ * @brief The user would connect to this signal to intercept a KeyEvent at window.
+ *
+ * Intercepts KeyEvents in the window before dispatching KeyEvents to the control.
+ * If a KeyEvent is consumed, no KeyEvent is delivered to the control.
+ *
+ * @param[in] window The window instance.
+ * @return The signal to connect to
+ */
+DALI_ADAPTOR_API InterceptKeyEventSignalType& InterceptKeyEventSignal(Window window);
+
 } // namespace DevelWindow
 
 } // namespace Dali
index 7cea697..704469c 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.
@@ -19,6 +19,7 @@
 #include <dali/devel-api/text-abstraction/font-client.h>
 
 // INTERNAL INCLUDES
+#include <dali/internal/imaging/common/image-operations.h>
 #include <dali/internal/text/text-abstraction/font-client-impl.h>
 
 namespace Dali
@@ -48,6 +49,8 @@ const Size FontClient::MAX_SIZE_FIT_IN_ATLAS(MAX_TEXT_ATLAS_WIDTH - PADDING_TEXT
 
 const uint32_t FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE = 64u; //Found this value from toolkit
 
+// FontClient::GlyphBufferData
+
 FontClient::GlyphBufferData::GlyphBufferData()
 : buffer{nullptr},
   width{0u},
@@ -55,14 +58,526 @@ FontClient::GlyphBufferData::GlyphBufferData()
   outlineOffsetX{0},
   outlineOffsetY{0},
   format{Pixel::A8},
+  compressionType(CompressionType::NO_COMPRESSION),
   isColorEmoji{false},
-  isColorBitmap{false}
+  isColorBitmap{false},
+  isBufferOwned{false}
 {
 }
 
 FontClient::GlyphBufferData::~GlyphBufferData()
 {
-}
+  if(isBufferOwned)
+  {
+    free(buffer);
+  }
+}
+
+size_t FontClient::GlyphBufferData::Compress(const uint8_t* const __restrict__ inBuffer, GlyphBufferData& __restrict__ outBufferData)
+{
+  size_t bufferSize                       = 0u;
+  uint8_t*& __restrict__ compressedBuffer = outBufferData.buffer;
+  switch(outBufferData.compressionType)
+  {
+    case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
+    {
+      bufferSize = outBufferData.width * outBufferData.height * Pixel::GetBytesPerPixel(outBufferData.format);
+
+      compressedBuffer = (uint8_t*)malloc(bufferSize);
+      if(DALI_UNLIKELY(compressedBuffer == nullptr))
+      {
+        return 0u;
+      }
+      outBufferData.isBufferOwned = true;
+
+      // Copy buffer without compress
+      memcpy(compressedBuffer, inBuffer, bufferSize);
+      break;
+    }
+    case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
+    {
+      const uint32_t widthByte       = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
+      const uint32_t componentCount  = (widthByte >> 1);
+      const bool     considerPadding = (widthByte & 1) ? true : false;
+
+      // For BIT_PER_PIXEL_4 type, we can know final compressed buffer size immediatly.
+      bufferSize       = outBufferData.height * (componentCount + (considerPadding ? 1 : 0));
+      compressedBuffer = (uint8_t*)malloc(bufferSize);
+      if(DALI_UNLIKELY(compressedBuffer == nullptr))
+      {
+        return 0u;
+      }
+      outBufferData.isBufferOwned = true;
+
+      uint8_t* __restrict__ outBufferPtr      = compressedBuffer;
+      const uint8_t* __restrict__ inBufferPtr = inBuffer;
+
+      // Compress for each line
+      for(uint32_t y = 0; y < outBufferData.height; ++y)
+      {
+        for(uint32_t x = 0; x < componentCount; ++x)
+        {
+          const uint8_t v0 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
+          const uint8_t v1 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
+
+          *(outBufferPtr++) = (v0 << 4) | v1;
+        }
+        if(considerPadding)
+        {
+          *(outBufferPtr++) = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
+        }
+      }
+      break;
+    }
+    case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
+    {
+      const uint32_t widthByte = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
+
+      // Allocate temperal buffer. Note that RLE4 can be bigger than original buffer.
+      uint8_t* __restrict__ tempBuffer = (uint8_t*)malloc(outBufferData.height * (widthByte + 1));
+      if(DALI_UNLIKELY(tempBuffer == nullptr))
+      {
+        return 0u;
+      }
+
+      uint8_t* __restrict__ outBufferPtr      = tempBuffer;
+      const uint8_t* __restrict__ inBufferPtr = inBuffer;
+
+      bufferSize = 0u;
+
+      // Compress for each line
+      for(uint32_t y = 0; y < outBufferData.height; ++y)
+      {
+        uint32_t encodedByte = 0;
+        while(encodedByte < widthByte)
+        {
+          // Case 1 : Remain only 1 byte
+          if(DALI_UNLIKELY(encodedByte + 1 == widthByte))
+          {
+            const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+            const uint8_t v0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
+            *(outBufferPtr++)   = v0;
+            ++encodedByte;
+            ++bufferSize;
+          }
+          // Case 2 : Remain only 2 byte
+          else if(DALI_UNLIKELY(encodedByte + 2 == widthByte))
+          {
+            const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+            const uint8_t v0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
+            const uint8_t prev1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+            const uint8_t v1    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev1) & 0x0f; // Intented underflow
+            encodedByte += 2;
+            if(v0 == v1)
+            {
+              *(outBufferPtr++) = 0x80 | v0;
+              ++bufferSize;
+            }
+            else
+            {
+              *(outBufferPtr++) = 0x10 | v0;
+              *(outBufferPtr++) = v1 << 4;
+              bufferSize += 2;
+            }
+          }
+          // Case 3 : Normal case. Remain byte bigger or equal than 3.
+          else
+          {
+            // Compress rule -
+            // Read 2 byte as v0 and v1.
+            // - If v0 == v1, We can compress. mark the first bit as 1. and remain 3 bit mark as the "runLength - 2".
+            //   runLength can be maximum 9.
+            // - If v0 != v1, We cannot compress. mark the first bit as 0. and remain 3 bit mark as the "(nonRunLength - 1) / 2"
+            //   Due to the BitPerPixel is 4, nonRunLength should be odd value.
+            //   nonRunLength cutted if v0 == v1.
+            //   nonRunLength can be maximum 15.
+
+            const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+            const uint8_t v0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
+            const uint8_t prev1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+            const uint8_t v1    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev1) & 0x0f; // Intented underflow
+            encodedByte += 2;
+            // We can compress by RLE
+            if(v0 == v1)
+            {
+              uint8_t runLength = 2;
+              while(encodedByte < widthByte && runLength < 9)
+              {
+                const uint8_t prev2 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+                const uint8_t v2    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prev2) & 0x0f; // Intented underflow
+                if(v2 == v0)
+                {
+                  ++inBufferPtr;
+                  ++encodedByte;
+                  ++runLength;
+                }
+                else
+                {
+                  break;
+                }
+              }
+
+              // Update (runLength - 2) result.
+              *(outBufferPtr++) = ((0x8 | (runLength - 2)) << 4) | v0;
+              ++bufferSize;
+            }
+            // We cannot compress by RLE.
+            else
+            {
+              // Read one more value.
+              const uint8_t prev2 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+              const uint8_t v2    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev2) & 0x0f; // Intented underflow
+              ++encodedByte;
+
+              uint8_t  nonRunLength          = 3;
+              uint8_t* nonRunLengthHeaderPtr = outBufferPtr;
+              *(outBufferPtr++)              = v0;
+              *(outBufferPtr++)              = (v1 << 4) | v2;
+              bufferSize += 2;
+              while(encodedByte < widthByte && nonRunLength < 15)
+              {
+                if(DALI_LIKELY(encodedByte + 1 < widthByte))
+                {
+                  const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+                  const uint8_t w0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prew0) & 0x0f; // Intented underflow
+                  const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr + 1 - widthByte));
+                  const uint8_t w1    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr + 1)) - prew1) & 0x0f; // Intented underflow
+                  if(w0 == w1)
+                  {
+                    // Stop non-compress logic.
+                    break;
+                  }
+                  else
+                  {
+                    ++bufferSize;
+                    *(outBufferPtr++) = (w0 << 4) | w1;
+                    inBufferPtr += 2;
+                    encodedByte += 2;
+                    nonRunLength += 2;
+                  }
+                }
+                else
+                {
+                  // Edge case. There is only one pixel remained.
+                  const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+                  const uint8_t w0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prew0) & 0x0f; // Intented underflow
+                  {
+                    ++bufferSize;
+                    *(outBufferPtr++) = (w0 << 4);
+                    ++encodedByte;
+                    ++inBufferPtr;
+                    // Increase nonRunLength 2 even latest value is invalid.
+                    nonRunLength += 2;
+                  }
+                }
+              }
+
+              // Update (nonRunLength-1)/2 result into header.
+              *(nonRunLengthHeaderPtr) |= (nonRunLength >> 1) << 4;
+            }
+          }
+        }
+      }
+
+      // Allocate and copy data
+      compressedBuffer = (uint8_t*)malloc(bufferSize);
+      if(DALI_UNLIKELY(compressedBuffer == nullptr))
+      {
+        free(tempBuffer);
+        return 0u;
+      }
+      outBufferData.isBufferOwned = true;
+
+      memcpy(compressedBuffer, tempBuffer, bufferSize);
+      free(tempBuffer);
+
+      break;
+    }
+    default:
+    {
+      break;
+    }
+  }
+
+  return bufferSize;
+}
+
+void FontClient::GlyphBufferData::Decompress(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer)
+{
+  if(DALI_UNLIKELY(outBuffer == nullptr))
+  {
+    return;
+  }
+
+  switch(inBufferData.compressionType)
+  {
+    case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
+    {
+      const auto bufferSize = inBufferData.width * inBufferData.height * Pixel::GetBytesPerPixel(inBufferData.format);
+
+      // Copy buffer without compress
+      memcpy(outBuffer, inBufferData.buffer, bufferSize);
+      break;
+    }
+    case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
+    {
+      const uint32_t widthByte       = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+      const uint32_t componentCount  = (widthByte >> 1);
+      const bool     considerPadding = (widthByte & 1) ? true : false;
+
+      uint8_t* __restrict__ outBufferPtr      = outBuffer;
+      const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
+
+      // Compress for each line
+      for(uint32_t y = 0; y < inBufferData.height; ++y)
+      {
+        for(uint32_t x = 0; x < componentCount; ++x)
+        {
+          const uint8_t v  = *(inBufferPtr++);
+          const uint8_t v0 = (v >> 4) & 0x0f;
+          const uint8_t v1 = v & 0x0f;
+
+          *(outBufferPtr++) = (v0 << 4) | v0;
+          *(outBufferPtr++) = (v1 << 4) | v1;
+        }
+        if(considerPadding)
+        {
+          const uint8_t v   = *(inBufferPtr++);
+          *(outBufferPtr++) = (v << 4) | v;
+        }
+      }
+      break;
+    }
+    case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
+    {
+      const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+
+      uint8_t* __restrict__ outBufferPtr      = outBuffer;
+      const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
+      // Compress for each line
+      for(uint32_t y = 0; y < inBufferData.height; ++y)
+      {
+        uint32_t x           = 0;
+        uint32_t decodedByte = 0;
+        while(decodedByte < widthByte)
+        {
+          const uint8_t v = *(inBufferPtr++);
+          ++x;
+          // Compress by RLE
+          if(v & 0x80)
+          {
+            const uint8_t runLength = ((v >> 4) & 0x07) + 2u;
+            decodedByte += runLength;
+            const uint8_t repeatValue = v & 0x0f;
+            for(uint8_t iter = 0; iter < runLength; ++iter)
+            {
+              const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+              const uint8_t v0    = (prev0 + repeatValue) & 0x0f;
+              *(outBufferPtr++)   = (v0 << 4) | v0;
+            }
+          }
+          // Not compress by RLE
+          else
+          {
+            const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
+            decodedByte += nonRunLength;
+            // First value.
+            const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+            const uint8_t v0    = (prev0 + (v & 0x0f)) & 0x0f;
+            *(outBufferPtr++)   = (v0 << 4) | v0;
+
+            const bool ignoreLastValue = decodedByte > widthByte ? true : false;
+            if(DALI_UNLIKELY(ignoreLastValue))
+            {
+              --decodedByte;
+              for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
+              {
+                const uint8_t w     = *(inBufferPtr++);
+                const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+                const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+                const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte + 1)) & 0x0f;
+                const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
+                ++x;
+
+                *(outBufferPtr++) = (w0 << 4) | w0;
+                *(outBufferPtr++) = (w1 << 4) | w1;
+              }
+              // Last value.
+              {
+                const uint8_t w     = ((*(inBufferPtr++)) >> 4) & 0x0f;
+                const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+                const uint8_t w0    = (prew0 + w) & 0x0f;
+                ++x;
+
+                *(outBufferPtr++) = (w0 << 4) | w0;
+              }
+            }
+            else
+            {
+              for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
+              {
+                const uint8_t w     = *(inBufferPtr++);
+                const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+                const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+                const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte + 1)) & 0x0f;
+                const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
+                ++x;
+
+                *(outBufferPtr++) = (w0 << 4) | w0;
+                *(outBufferPtr++) = (w1 << 4) | w1;
+              }
+            }
+          }
+        }
+      }
+      break;
+    }
+    default:
+    {
+      break;
+    }
+  }
+}
+
+void FontClient::GlyphBufferData::DecompressScanline(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer, uint32_t& __restrict__ offset)
+{
+  switch(inBufferData.compressionType)
+  {
+    case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
+    {
+      const auto bufferSize = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+
+      // Copy buffer without compress
+      memcpy(outBuffer, inBufferData.buffer + offset, bufferSize);
+
+      // Update offset
+      offset += bufferSize;
+      break;
+    }
+    case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
+    {
+      const uint32_t widthByte       = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+      const uint32_t componentCount  = (widthByte >> 1);
+      const bool     considerPadding = (widthByte & 1) ? true : false;
+
+      uint8_t* __restrict__ outBufferPtr      = outBuffer;
+      const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
+
+      // Decompress scanline
+      for(uint32_t x = 0; x < componentCount; ++x)
+      {
+        const uint8_t v  = *(inBufferPtr++);
+        const uint8_t v0 = (v >> 4) & 0x0f;
+        const uint8_t v1 = v & 0x0f;
+
+        *(outBufferPtr++) = (v0 << 4) | v0;
+        *(outBufferPtr++) = (v1 << 4) | v1;
+      }
+      if(considerPadding)
+      {
+        const uint8_t v   = *(inBufferPtr++);
+        *(outBufferPtr++) = (v << 4) | v;
+      }
+
+      // Update offset
+      offset += (widthByte + 1u) >> 1u;
+      break;
+    }
+    case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
+    {
+      const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+
+      uint8_t* __restrict__ outBufferPtr      = outBuffer;
+      const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
+
+      // If offset is zero, fill outBuffer as 0 first.
+      if(DALI_UNLIKELY(offset == 0))
+      {
+        memset(outBufferPtr, 0, widthByte);
+      }
+
+      // Decompress scanline
+      uint32_t decodedByte = 0;
+      while(decodedByte < widthByte)
+      {
+        const uint8_t v = *(inBufferPtr++);
+        ++offset;
+        // Compress by RLE
+        if(v & 0x80)
+        {
+          const uint8_t runLength = ((v >> 4) & 0x07) + 2u;
+          decodedByte += runLength;
+          const uint8_t repeatValue = (v & 0x0f);
+          for(uint8_t iter = 0; iter < runLength; ++iter)
+          {
+            const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
+            const uint8_t v0    = (prev0 + repeatValue) & 0x0f;
+            *(outBufferPtr++)   = (v0 << 4) | v0;
+          }
+        }
+        // Not compress by RLE
+        else
+        {
+          const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
+          decodedByte += nonRunLength;
+          // First value.
+          const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
+          const uint8_t v0    = (prev0 + (v & 0x0f)) & 0x0f;
+          *(outBufferPtr++)   = (v0 << 4) | v0;
+
+          const bool ignoreLastValue = decodedByte > widthByte ? true : false;
+          if(DALI_UNLIKELY(ignoreLastValue))
+          {
+            --decodedByte;
+            for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
+            {
+              const uint8_t w     = *(inBufferPtr++);
+              const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
+              const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+              const uint8_t prew1 = (*(outBufferPtr + 1)) & 0x0f;
+              const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
+              ++offset;
+
+              *(outBufferPtr++) = (w0 << 4) | w0;
+              *(outBufferPtr++) = (w1 << 4) | w1;
+            }
+            // Last value.
+            {
+              const uint8_t w     = ((*(inBufferPtr++)) >> 4) & 0x0f;
+              const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
+              const uint8_t w0    = (prew0 + w) & 0x0f;
+              ++offset;
+
+              *(outBufferPtr++) = (w0 << 4) | w0;
+            }
+          }
+          else
+          {
+            for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
+            {
+              const uint8_t w     = *(inBufferPtr++);
+              const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
+              const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+              const uint8_t prew1 = (*(outBufferPtr + 1)) & 0x0f;
+              const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
+              ++offset;
+
+              *(outBufferPtr++) = (w0 << 4) | w0;
+              *(outBufferPtr++) = (w1 << 4) | w1;
+            }
+          }
+        }
+      }
+      break;
+    }
+    default:
+    {
+      break;
+    }
+  }
+}
+
+// FontClient
 
 FontClient FontClient::Get()
 {
index 992f8aa..e21f3e6 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_PLATFORM_TEXT_ABSTRACTION_FONT_CLIENT_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.
@@ -95,14 +95,59 @@ public:
      */
     ~GlyphBufferData();
 
-    unsigned char* buffer;            ///< The glyph's bitmap buffer data.
-    unsigned int   width;             ///< The width of the bitmap.
-    unsigned int   height;            ///< The height of the bitmap.
-    int            outlineOffsetX;    ///< The additional horizontal offset to be added for the glyph's position for outline.
-    int            outlineOffsetY;    ///< The additional vertical offset to be added for the glyph's position for outline.
-    Pixel::Format  format;            ///< The pixel's format of the bitmap.
-    bool           isColorEmoji : 1;  ///< Whether the glyph is an emoji.
-    bool           isColorBitmap : 1; ///< Whether the glyph is a color bitmap.
+    // Compression method of buffer. Each buffer compressed line by line
+    enum class CompressionType
+    {
+      NO_COMPRESSION = 0, // No compression
+      BPP_4          = 1, // Compress as 4 bit. Color become value * 17 (0x00, 0x11, 0x22, ... 0xee, 0xff).
+                          // Only works for Pixel::L8 format
+      RLE_4 = 2,          // Compress as 4 bit, and Run-Length-Encode. For more high compress rate, we store difference between previous scanline.
+                          // Only works for Pixel::L8 format
+    };
+
+    /**
+     * @brief Helper static function to compress raw buffer from inBuffer to outBufferData.buffer.
+     * outBufferData will have it's own buffer.
+     *
+     * @pre outBufferData must not have it's own buffer.
+     * @param[in] inBuffer The input raw data.
+     * @param[in, out] outBufferData The output glyph buffer data.
+     * @return Size of compressed out buffer, Or 0 if compress failed.
+     */
+    static size_t Compress(const uint8_t* const inBuffer, GlyphBufferData& outBufferData);
+
+    /**
+     * @brief Helper static function to decompress raw buffer from inBuffer to outBufferPtr.
+     * If outBuffer is nullptr, Do nothing.
+     *
+     * @pre outBuffer memory should be allocated.
+     * @param[in] inBufferData The input glyph buffer data.
+     * @param[in, out] outBuffer The output pointer of raw buffer data.
+     */
+    static void Decompress(const GlyphBufferData& inBufferData, uint8_t* outBuffer);
+
+    /**
+     * @brief Special Helper static function to decompress raw buffer from inBuffer to outBuffer one scanline.
+     * After decompress one scanline successed, offset will be changed.
+     *
+     * @pre outBuffer memory should be allocated.
+     * @pre if inBufferData's compression type is RLE4, outBuffer memory should store the previous scanline data.
+     * @param[in] inBufferData The input glyph buffer data.
+     * @param[in, out] outBuffer The output pointer of raw buffer data.
+     * @param[in, out] offset The offset of input. It will be changed as next scanline's offset.
+     */
+    static void DecompressScanline(const GlyphBufferData& inBufferData, uint8_t* outBuffer, uint32_t& offset);
+
+    uint8_t*        buffer;              ///< The glyph's bitmap buffer data.
+    uint32_t        width;               ///< The width of the bitmap.
+    uint32_t        height;              ///< The height of the bitmap.
+    int             outlineOffsetX;      ///< The additional horizontal offset to be added for the glyph's position for outline.
+    int             outlineOffsetY;      ///< The additional vertical offset to be added for the glyph's position for outline.
+    Pixel::Format   format;              ///< The pixel's format of the bitmap.
+    CompressionType compressionType : 3; ///< The type of buffer compression.
+    bool            isColorEmoji : 1;    ///< Whether the glyph is an emoji.
+    bool            isColorBitmap : 1;   ///< Whether the glyph is a color bitmap.
+    bool            isBufferOwned : 1;   ///< Whether the glyph's bitmap buffer data owned by this class or not. Becareful when you use non-owned buffer data.
   };
 
   /**
index 7eff6f5..196e0d9 100644 (file)
@@ -249,6 +249,14 @@ public:
   }
 
   /**
+   * @copydoc Dali::Integration::SceneHolder::InterceptKeyEventSignal()
+   */
+  Dali::Integration::SceneHolder::KeyEventGeneratedSignalType& InterceptKeyEventSignal()
+  {
+    return mScene.InterceptKeyEventSignal();
+  }
+
+  /**
    * @copydoc Dali::Integration::SceneHolder::TouchedSignal()
    */
   Dali::Integration::SceneHolder::TouchEventSignalType& TouchedSignal()
index 6d4d282..0fbf66e 100644 (file)
@@ -119,6 +119,11 @@ SceneHolder::KeyEventGeneratedSignalType& SceneHolder::KeyEventGeneratedSignal()
   return GetImplementation(*this).KeyEventGeneratedSignal();
 }
 
+SceneHolder::KeyEventGeneratedSignalType& SceneHolder::InterceptKeyEventSignal()
+{
+  return GetImplementation(*this).InterceptKeyEventSignal();
+}
+
 SceneHolder::TouchEventSignalType& SceneHolder::TouchedSignal()
 {
   return GetImplementation(*this).TouchedSignal();
index d660509..eed2515 100644 (file)
@@ -188,6 +188,19 @@ public:
   KeyEventGeneratedSignalType& KeyEventGeneratedSignal();
 
   /**
+   * @brief This signal is emitted when key event is received.
+   * Intercepts KeyEvents in the window before dispatching KeyEvents to the control.
+   * If a KeyEvent is consumed, no KeyEvent is delivered to the control.
+   *
+   * A callback of the following type may be connected:
+   * @code
+   *   bool YourCallbackName(const KeyEvent& event);
+   * @endcode
+   * @return The signal to connect to
+   */
+  KeyEventGeneratedSignalType& InterceptKeyEventSignal();
+
+  /**
    * @brief This signal is emitted when the screen is touched and when the touch ends
    * (i.e. the down & up touch events only).
    *
index 2bdac51..675388e 100644 (file)
@@ -2058,6 +2058,57 @@ void LinearSample4BPP(const unsigned char* __restrict__ inPixels,
   LinearSampleGeneric<Pixel4Bytes, BilinearFilter4Bytes, true>(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions);
 }
 
+// Dispatch to a format-appropriate linear sampling function:
+void LinearSample(const unsigned char* __restrict__ inPixels,
+                  ImageDimensions inDimensions,
+                  unsigned int    inStride,
+                  Pixel::Format   pixelFormat,
+                  unsigned char* __restrict__ outPixels,
+                  ImageDimensions outDimensions)
+{
+  // Check the pixel format is one that is supported:
+  if(pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::RGB565)
+  {
+    switch(pixelFormat)
+    {
+      case Pixel::RGB888:
+      {
+        LinearSample3BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+        break;
+      }
+      case Pixel::RGBA8888:
+      {
+        LinearSample4BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+        break;
+      }
+      case Pixel::L8:
+      case Pixel::A8:
+      {
+        LinearSample1BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+        break;
+      }
+      case Pixel::LA88:
+      {
+        LinearSample2BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+        break;
+      }
+      case Pixel::RGB565:
+      {
+        LinearSampleRGB565(inPixels, inDimensions, inStride, outPixels, outDimensions);
+        break;
+      }
+      default:
+      {
+        DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+      }
+    }
+  }
+  else
+  {
+    DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not linear sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat));
+  }
+}
+
 void Resample(const unsigned char* __restrict__ inPixels,
               ImageDimensions inputDimensions,
               unsigned int    inputStride,
@@ -2257,43 +2308,29 @@ void LanczosSample1BPP(const unsigned char* __restrict__ inPixels,
   Resample(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions, Resampler::LANCZOS4, 1, false);
 }
 
-// Dispatch to a format-appropriate linear sampling function:
-void LinearSample(const unsigned char* __restrict__ inPixels,
-                  ImageDimensions inDimensions,
-                  unsigned int    inStride,
-                  Pixel::Format   pixelFormat,
-                  unsigned char* __restrict__ outPixels,
-                  ImageDimensions outDimensions)
+// Dispatch to a format-appropriate third-party resampling function:
+void LanczosSample(const unsigned char* __restrict__ inPixels,
+                   ImageDimensions inDimensions,
+                   unsigned int    inStride,
+                   Pixel::Format   pixelFormat,
+                   unsigned char* __restrict__ outPixels,
+                   ImageDimensions outDimensions)
 {
   // Check the pixel format is one that is supported:
-  if(pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::RGB565)
+  if(pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::BGRA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
   {
     switch(pixelFormat)
     {
-      case Pixel::RGB888:
-      {
-        LinearSample3BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
-        break;
-      }
       case Pixel::RGBA8888:
+      case Pixel::BGRA8888:
       {
-        LinearSample4BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+        LanczosSample4BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
         break;
       }
       case Pixel::L8:
       case Pixel::A8:
       {
-        LinearSample1BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
-        break;
-      }
-      case Pixel::LA88:
-      {
-        LinearSample2BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
-        break;
-      }
-      case Pixel::RGB565:
-      {
-        LinearSampleRGB565(inPixels, inDimensions, inStride, outPixels, outDimensions);
+        LanczosSample1BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
         break;
       }
       default:
@@ -2304,7 +2341,7 @@ void LinearSample(const unsigned char* __restrict__ inPixels,
   }
   else
   {
-    DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not linear sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat));
+    DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not lanczos sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat));
   }
 }
 
index 57d1255..2d27eac 100644 (file)
@@ -357,6 +357,26 @@ void LinearSample4BPP(const unsigned char* __restrict__ inPixels,
                       ImageDimensions desiredDimensions);
 
 /**
+ * @brief Resample input image to output image using a Lanczos algorithm.
+ *
+ * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
+ * separate buffer from the output buffer.
+ *
+ * @param[in] inPixels Pointer to the input image buffer.
+ * @param[in] inputDimensions The input dimensions of the image.
+ * @param[in] inputStride The input stride of the image.
+ * @param[in] pixelFormat The format of the image pointed at by pixels.
+ * @param[out] outPixels Pointer to the output image buffer.
+ * @param[in] desiredDimensions The output dimensions of the image.
+ */
+void LanczosSample(const unsigned char* __restrict__ inPixels,
+                   ImageDimensions inDimensions,
+                   unsigned int    inStride,
+                   Pixel::Format   pixelFormat,
+                   unsigned char* __restrict__ outPixels,
+                   ImageDimensions outDimensions);
+
+/**
  * @brief Resamples the input image with the Lanczos algorithm.
  *
  * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
@@ -701,12 +721,23 @@ inline unsigned int BilinearFilter1Component(unsigned int tl, unsigned int tr, u
  * @param y The value between [0..255]
  * @return (x*y)/255
  */
-inline uint8_t MultiplyAndNormalizeColor(const uint8_t& x, const uint8_t& y) noexcept
+inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
 {
   const uint32_t xy = static_cast<const uint32_t>(x) * y;
   return ((xy << 15) + (xy << 7) + xy) >> 23;
 }
 
+/**
+ * @brief Fast division by 17 and roundup. It will be useful when we compress 8bit luminance value as 4bit for text glyph.
+ *
+ * @param x The value between [0..255]
+ * @return round(x / 17.0f).(same as (x+8)/17)
+ */
+inline uint8_t CompressBitPerPixel8To4(const uint8_t x) noexcept
+{
+  return ((((static_cast<const uint16_t>(x) << 4) - x + (x >> 4)) >> 7) + 1) >> 1;
+}
+
 /**@}*/
 
 } /* namespace Platform */
index 107f4cd..9cd7681 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.
@@ -38,7 +38,7 @@ namespace
 {
 #define TBM_SURFACE_QUEUE_SIZE 3
 
-const char* SAMPLER_TYPE    = "samplerExternalOES";
+const char* SAMPLER_TYPE = "samplerExternalOES";
 
 // clang-format off
 int FORMATS_BLENDING_REQUIRED[] = {
@@ -124,19 +124,22 @@ void NativeImageSourceQueueTizen::Initialize(Dali::NativeImageSourceQueue::Color
 
     switch(colorFormat)
     {
-      case Dali::NativeImageSourceQueue::ColorFormat::RGBA8888:
+      case Dali::NativeImageSourceQueue::ColorFormat::RGBA8888: // TODO : Implement me after other codes fixed.
+      case Dali::NativeImageSourceQueue::ColorFormat::BGRA8888:
       {
         tbmFormat         = TBM_FORMAT_ARGB8888;
         mBlendingRequired = true;
         break;
       }
-      case Dali::NativeImageSourceQueue::ColorFormat::RGBX8888:
+      case Dali::NativeImageSourceQueue::ColorFormat::RGBX8888: // TODO : Implement me after other codes fixed.
+      case Dali::NativeImageSourceQueue::ColorFormat::BGRX8888:
       {
         tbmFormat         = TBM_FORMAT_XRGB8888;
         mBlendingRequired = false;
         break;
       }
-      case Dali::NativeImageSourceQueue::ColorFormat::RGB888:
+      case Dali::NativeImageSourceQueue::ColorFormat::RGB888: // TODO : Implement me after other codes fixed.
+      case Dali::NativeImageSourceQueue::ColorFormat::BGR888:
       {
         tbmFormat         = TBM_FORMAT_RGB888;
         mBlendingRequired = false;
index 4bccb0b..d58c3f1 100644 (file)
@@ -854,7 +854,6 @@ Devel::PixelBuffer RenderTextCairo(const TextAbstraction::TextRenderer::Paramete
 
           // Retrieve the image
           TextAbstraction::FontClient::GlyphBufferData data;
-          std::unique_ptr<GlyphBuffer>                 glyphBufferPtr(new GlyphBuffer(data, GlyphBuffer::FREE));
           if(isEmoji)
           {
             data.width  = parameters.glyphs[run.glyphIndex].width;
@@ -893,6 +892,24 @@ Devel::PixelBuffer RenderTextCairo(const TextAbstraction::TextRenderer::Paramete
             unsigned int       heightOut = data.height;
             const unsigned int pixelSize = Pixel::GetBytesPerPixel(data.format);
 
+            // If we need to decompress, create new memory and replace ownership.
+            if(data.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION)
+            {
+              uint8_t* newBuffer = (uint8_t*)malloc(widthOut * heightOut * pixelSize);
+              if(DALI_LIKELY(newBuffer != nullptr))
+              {
+                TextAbstraction::FontClient::GlyphBufferData::Decompress(data, newBuffer);
+                if(data.isBufferOwned)
+                {
+                  // Release previous buffer
+                  free(data.buffer);
+                }
+                data.isBufferOwned   = true;
+                data.buffer          = newBuffer;
+                data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+              }
+            }
+
             Dali::Internal::Platform::RotateByShear(data.buffer,
                                                     data.width,
                                                     data.height,
@@ -904,10 +921,15 @@ Devel::PixelBuffer RenderTextCairo(const TextAbstraction::TextRenderer::Paramete
                                                     heightOut);
             if(nullptr != pixelsOut)
             {
-              free(data.buffer);
-              data.buffer = pixelsOut;
-              data.width  = widthOut;
-              data.height = heightOut;
+              if(data.isBufferOwned)
+              {
+                free(data.buffer);
+              }
+              data.isBufferOwned   = true;
+              data.compressionType = Dali::TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+              data.buffer          = pixelsOut;
+              data.width           = widthOut;
+              data.height          = heightOut;
             }
 
             glyphX = centerX - 0.5 * static_cast<double>(data.width);
index 6512b95..972f323 100644 (file)
@@ -115,10 +115,7 @@ void BitmapFontCacheItem::CreateBitmap(
 
       data.isColorBitmap = font.isColorFont;
 
-      ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer());
-
-      // Sets the pixel format.
-      data.format = pixelBuffer.GetPixelFormat();
+      ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer(), pixelBuffer.GetPixelFormat());
       break;
     }
     ++index;
index 16b8044..c2486c1 100644 (file)
@@ -40,17 +40,17 @@ void EmbeddedItem::CreateBitmap(const std::vector<PixelBufferCacheItem>&
     Devel::PixelBuffer pixelBuffer = pixelBufferCache[pixelBufferId - 1u].pixelBuffer;
     if(pixelBuffer)
     {
-      ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer());
-
-      // Sets the pixel format.
-      data.format = pixelBuffer.GetPixelFormat();
+      ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer(), pixelBuffer.GetPixelFormat());
     }
   }
   else
   {
+    data.isBufferOwned   = true;
+    data.compressionType = Dali::TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+
     // Creates the output buffer
-    const unsigned int bufferSize = data.width * data.height * 4u;
-    data.buffer                   = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
+    const uint32_t bufferSize = data.width * data.height * 4u;
+    data.buffer               = (uint8_t*)malloc(bufferSize); // @note The caller is responsible for deallocating the bitmap data using free.
 
     memset(data.buffer, 0u, bufferSize);
 
index 9141ca1..fbe76aa 100644 (file)
@@ -129,7 +129,7 @@ const FontSlant::Type DefaultFontSlant()
  * @param[in] srcHeight The height of the bitmap.
  * @param[in] srcBuffer The buffer of the bitmap.
  */
-void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer)
+void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer, const Pixel::Format srcFormat)
 {
   // Set the input dimensions.
   const ImageDimensions inputDimensions(srcWidth, srcHeight);
@@ -141,22 +141,32 @@ void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned
   data.height = (data.height == 0) ? srcHeight : data.height;
   const ImageDimensions desiredDimensions(data.width, data.height);
 
+  data.format = srcFormat;
+
+  // Note we don't compress here
+  data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+
+  const uint32_t bytePerPixel = Dali::Pixel::GetBytesPerPixel(srcFormat);
+
   // Creates the output buffer
-  const unsigned int bufferSize = data.width * data.height * 4u;
-  data.buffer                   = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
+  const uint32_t bufferSize = data.width * data.height * bytePerPixel;
 
   if(inputDimensions == desiredDimensions)
   {
     // There isn't downscaling.
-    memcpy(data.buffer, srcBuffer, bufferSize);
+    data.isBufferOwned = false;
+    data.buffer        = const_cast<uint8_t*>(srcBuffer);
   }
   else
   {
-    Dali::Internal::Platform::LanczosSample4BPP(srcBuffer,
-                                                inputDimensions,
-                                                srcWidth,
-                                                data.buffer,
-                                                desiredDimensions);
+    data.isBufferOwned = true;
+    data.buffer        = (uint8_t*)malloc(bufferSize); // @note The caller is responsible for deallocating the bitmap data using free.
+    Dali::Internal::Platform::LanczosSample(srcBuffer,
+                                            inputDimensions,
+                                            srcWidth,
+                                            srcFormat,
+                                            data.buffer,
+                                            desiredDimensions);
   }
 }
 
@@ -171,6 +181,7 @@ void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned
  */
 void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap& srcBitmap, bool isShearRequired, bool moveBuffer)
 {
+  data.buffer = nullptr;
   if(srcBitmap.width * srcBitmap.rows > 0)
   {
     switch(srcBitmap.pixel_mode)
@@ -261,19 +272,21 @@ void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap
           data.height = height;
           data.format = Pixel::L8; // Sets the pixel format.
 
+          // Note we don't compress here
+          data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+
           if(moveBuffer)
           {
-            data.buffer = pixelsIn;
+            data.isBufferOwned = true;
+            data.buffer        = pixelsIn;
 
             // Happy trick for copyless convert bitmap!
             srcBitmap.buffer = nullptr;
           }
           else
           {
-            const unsigned int bufferSize = width * height;
-            data.buffer                   = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
-
-            memcpy(data.buffer, pixelsIn, bufferSize);
+            data.isBufferOwned = false;
+            data.buffer        = pixelsIn;
           }
 
           if(releaseRequiredPixelPtr)
@@ -290,10 +303,7 @@ void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap
         if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
         {
           // Color glyph doesn't support copyless convert bitmap. Just memcpy
-          ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer);
-
-          // Sets the pixel format.
-          data.format = Pixel::BGRA8888;
+          ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer, Pixel::BGRA8888);
         }
         break;
       }
index daf4c5b..c73e3da 100644 (file)
@@ -35,7 +35,8 @@ namespace Dali::TextAbstraction::Internal
 void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data,
                    unsigned int                                  srcWidth,
                    unsigned int                                  srcHeight,
-                   const unsigned char* const                    srcBuffer);
+                   const unsigned char* const                    srcBuffer,
+                   const Pixel::Format                           srcFormat);
 
 void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data,
                    FT_Bitmap&                                    srcBitmap,
index dd45035..35a5c72 100644 (file)
@@ -44,7 +44,7 @@ constexpr float MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE = 1.5f;
  * @brief Maximum size of glyph cache per each font face.
  */
 constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX         = 128;
-constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 2u;
+constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u;
 
 constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
 
@@ -54,13 +54,63 @@ constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
  * @note This value fixed when we call it first time.
  * @return The max size of glyph cache.
  */
-size_t GetMaxNumberOfGlyphCache()
+inline const size_t GetMaxNumberOfGlyphCache()
 {
   using Dali::EnvironmentVariable::GetEnvironmentVariable;
   static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV);
   static auto number       = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX;
   return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number;
 }
+
+/**
+ * @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 const 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 const 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(FT_Library&        freeTypeLibrary,
@@ -319,16 +369,17 @@ bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVe
  * @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
 {
   GlyphCacheManager::GlyphCacheData glyphData;
   FT_Error                          error;
+  FT_Int32                          loadFlag;
   // For the software italics.
   bool isShearRequired = false;
 
@@ -336,7 +387,7 @@ void FontFaceCacheItem::CreateBitmap(
   // Check to see if this is fixed size bitmap
   if(mIsFixedSizeBitmap)
   {
-    mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_COLOR, isBoldRequired, glyphData, error);
+    loadFlag = FT_LOAD_COLOR;
   }
   else
 #endif
@@ -344,8 +395,10 @@ 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?
-    mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_NO_AUTOHINT, isBoldRequired, glyphData, error);
+    loadFlag = FT_LOAD_NO_AUTOHINT;
   }
+  mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, glyphData, error);
+
   if(FT_Err_Ok == error)
   {
     if(isItalicRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_ITALIC))
@@ -354,9 +407,9 @@ void FontFaceCacheItem::CreateBitmap(
       isShearRequired = true;
     }
 
-    // Convert to bitmap if necessary
     if(!glyphData.mIsBitmap)
     {
+      // Convert to bitmap if necessary
       FT_Glyph glyph = glyphData.mGlyph;
 
       DALI_ASSERT_ALWAYS(glyph->format != FT_GLYPH_FORMAT_BITMAP && "Something wrong with cashing. Some bitmap glyph cached failed.");
@@ -417,29 +470,72 @@ void FontFaceCacheItem::CreateBitmap(
         }
       }
 
-      // 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);
-
-        if(isOutlineGlyph)
-        {
-          // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
-          data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
-          data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
-        }
-
-        // Move bitmap buffer into data.buffer
-        ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
+      const bool ableUseCachedRenderedGlyph = EnableCacheRenderedGlyph() && !isOutlineGlyph && !isShearRequired;
 
-        // Copied FT_Glyph object must be released with FT_Done_Glyph
-        FT_Done_Glyph(glyph);
+      // 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
       {
-        DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
+        // 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);
+
+          if(isOutlineGlyph)
+          {
+            // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
+            data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
+            data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
+          }
+
+          // If we can cache this bitmapGlyph, store it.
+          // Note : We will call this API once per each glyph.
+          if(ableUseCachedRenderedGlyph)
+          {
+            mGlyphCacheManager->CacheRenderedGlyphBuffer(glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
+
+            GlyphCacheManager::GlyphCacheData dummyData;
+            mGlyphCacheManager->GetGlyphCacheDataFromIndex(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
index daf0bf9..1ced0b5 100644 (file)
  * limitations under the License.
  */
 
+// CLASS HEADER
+#include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
+
 // INTERNAL INCLUDES
 #include <dali/integration-api/debug.h>
 #include <dali/internal/imaging/common/image-operations.h>
 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
-#include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
 
 // EXTERNAL INCLUDES
 #include FT_BITMAP_H
@@ -31,6 +33,7 @@ namespace Dali::TextAbstraction::Internal
 {
 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)
@@ -40,6 +43,7 @@ GlyphCacheManager::GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyp
 {
   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager Create with maximum size : %d\n", static_cast<int>(mGlyphCacheMaxSize));
 }
+
 GlyphCacheManager::~GlyphCacheManager()
 {
   while(!mLRUGlyphCache.IsEmpty())
@@ -53,11 +57,11 @@ GlyphCacheManager::~GlyphCacheManager()
 }
 
 bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
-  const GlyphIndex& index,
-  const FT_Int32&   flag,
-  const bool&       isBoldRequired,
-  GlyphCacheData&   glyphData,
-  FT_Error&         error)
+  const GlyphIndex index,
+  const FT_Int32   flag,
+  const bool       isBoldRequired,
+  GlyphCacheData&  glyphData,
+  FT_Error&        error)
 {
   // Append some error value here instead of FT_Err_Ok.
   error = static_cast<FT_Error>(-1);
@@ -102,11 +106,11 @@ bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
 }
 
 bool GlyphCacheManager::LoadGlyphDataFromIndex(
-  const GlyphIndex& index,
-  const FT_Int32&   flag,
-  const bool&       isBoldRequired,
-  GlyphCacheData&   glyphData,
-  FT_Error&         error)
+  const GlyphIndex index,
+  const FT_Int32   flag,
+  const bool       isBoldRequired,
+  GlyphCacheData&  glyphData,
+  FT_Error&        error)
 {
   error = FT_Load_Glyph(mFreeTypeFace, index, flag);
   if(FT_Err_Ok == error)
@@ -167,7 +171,7 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex(
       if(bufferSize > 0)
       {
         glyphData.mIsBitmap       = true;
-        glyphData.mBitmap->buffer = new uint8_t[bufferSize];
+        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);
       }
       else
@@ -191,12 +195,17 @@ bool GlyphCacheManager::LoadGlyphDataFromIndex(
 }
 
 void GlyphCacheManager::ResizeBitmapGlyph(
-  const GlyphIndex& index,
-  const FT_Int32&   flag,
-  const bool&       isBoldRequired,
-  const uint32_t&   desiredWidth,
-  const uint32_t&   desiredHeight)
+  const GlyphIndex index,
+  const FT_Int32   flag,
+  const bool       isBoldRequired,
+  const uint32_t   desiredWidth,
+  const uint32_t   desiredHeight)
 {
+  if(desiredWidth * desiredHeight <= 0)
+  {
+    // Skip this API if desired size is zero
+    return;
+  }
   FT_Error       error;
   GlyphCacheData originGlyphData;
   if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
@@ -206,6 +215,7 @@ void GlyphCacheManager::ResizeBitmapGlyph(
       const bool requiredResize = (originGlyphData.mBitmap->rows != desiredHeight) || (originGlyphData.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);
 
@@ -222,7 +232,7 @@ void GlyphCacheManager::ResizeBitmapGlyph(
           {
             if(destinationGlpyhData.mBitmap->pitch == static_cast<int>(destinationGlpyhData.mBitmap->width))
             {
-              desiredBuffer = new uint8_t[desiredWidth * desiredHeight];
+              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,
@@ -237,7 +247,7 @@ void GlyphCacheManager::ResizeBitmapGlyph(
           {
             if(destinationGlpyhData.mBitmap->pitch == static_cast<int>(destinationGlpyhData.mBitmap->width << 2u))
             {
-              desiredBuffer = new uint8_t[(desiredWidth * desiredHeight) << 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,
@@ -259,7 +269,7 @@ void GlyphCacheManager::ResizeBitmapGlyph(
         {
           // Success to resize bitmap glyph.
           // Release origin bitmap buffer.
-          delete[] destinationGlpyhData.mBitmap->buffer;
+          free(destinationGlpyhData.mBitmap->buffer);
 
           // Replace as desired buffer and size.
           destinationGlpyhData.mBitmap->buffer = desiredBuffer;
@@ -286,12 +296,116 @@ void GlyphCacheManager::ResizeBitmapGlyph(
   }
 }
 
+void GlyphCacheManager::CacheRenderedGlyphBuffer(
+  const GlyphIndex            index,
+  const FT_Int32              flag,
+  const bool                  isBoldRequired,
+  const FT_Bitmap&            srcBitmap,
+  const CompressionPolicyType policy)
+{
+  if(srcBitmap.width * srcBitmap.rows <= 0)
+  {
+    // Skip this API if rendered bitmap size is zero
+    return;
+  }
+  FT_Error       error;
+  GlyphCacheData originGlyphData;
+  if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
+  {
+    if(DALI_LIKELY(!originGlyphData.mIsBitmap && originGlyphData.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))
+      {
+        DALI_LOG_ERROR("Allocate GlyphBufferData failed\n");
+        return;
+      }
+
+      TextAbstraction::FontClient::GlyphBufferData& renderBuffer = *destinationGlpyhData.mRenderedBuffer;
+
+      // Set basic informations.
+      renderBuffer.width  = srcBitmap.width;
+      renderBuffer.height = srcBitmap.rows;
+
+      switch(srcBitmap.pixel_mode)
+      {
+        case FT_PIXEL_MODE_GRAY:
+        {
+          renderBuffer.format = Pixel::L8;
+
+          if(policy == CompressionPolicyType::SPEED)
+          {
+            // If policy is SPEED, we will not compress bitmap.
+            renderBuffer.compressionType = TextAbstraction::FontClient::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;
+            }
+            else
+            {
+              renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4;
+            }
+          }
+
+          const auto compressedBufferSize = TextAbstraction::FontClient::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;
+            return;
+          }
+          break;
+        }
+#ifdef FREETYPE_BITMAP_SUPPORT
+        case FT_PIXEL_MODE_BGRA:
+        {
+          // Copy buffer without compress
+          renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+          renderBuffer.format          = Pixel::BGRA8888;
+
+          const auto compressedBufferSize = TextAbstraction::FontClient::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;
+            return;
+          }
+          break;
+        }
+#endif
+        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;
+          break;
+        }
+      }
+    }
+  }
+}
+
 void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
 {
   if(mIsBitmap && mBitmap)
   {
     // Created FT_Bitmap object must be released with FT_Bitmap_Done
-    delete[] mBitmap->buffer;
+    free(mBitmap->buffer); // This buffer created by malloc
+
     delete mBitmap;
     mBitmap = nullptr;
   }
@@ -301,6 +415,11 @@ void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
     FT_Done_Glyph(mGlyph);
     mGlyph = nullptr;
   }
+
+  if(mRenderedBuffer)
+  {
+    delete mRenderedBuffer;
+  }
 }
 
 } // namespace Dali::TextAbstraction::Internal
index 5b8d135..fd0eb05 100644 (file)
  */
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-client.h> // For GlyphBufferData
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
 #include <dali/internal/text/text-abstraction/plugin/lru-cache-container.h>
 
 // EXTERNAL INCLUDES
-
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include FT_GLYPH_H
@@ -66,12 +67,21 @@ public:
     FT_Int32          mStyleFlags{0};  // Get from FT_Face
     bool              mIsBitmap{false};
 
+    TextAbstraction::FontClient::GlyphBufferData* mRenderedBuffer{nullptr}; // Rendered glyph buffer. Cached only if system allow to cache and we rendered it before. Otherwise, just nullptr
+
     /**
      * @brief Release the memory of loaded mGlyph / mBitmap.
      */
     void ReleaseGlyphData();
   };
 
+  // Compression priority of rendered glyph buffer.
+  enum class CompressionPolicyType
+  {
+    SPEED  = 0,
+    MEMORY = 1,
+  };
+
 public:
   // Public API area.
 
@@ -86,11 +96,11 @@ public:
    * @return True if load successfully. False if something error occured.
    */
   bool GetGlyphCacheDataFromIndex(
-    const GlyphIndex& index,
-    const FT_Int32&   flag,
-    const bool&       isBoldRequired,
-    GlyphCacheData&   data,
-    FT_Error&         error);
+    const GlyphIndex index,
+    const FT_Int32   flag,
+    const bool       isBoldRequired,
+    GlyphCacheData&  data,
+    FT_Error&        error);
 
   /**
    * @brief Load GlyphCacheData from face. The result will not be cached.
@@ -104,11 +114,11 @@ public:
    * @return True if load successfully. False if something error occured.
    */
   bool LoadGlyphDataFromIndex(
-    const GlyphIndex& index,
-    const FT_Int32&   flag,
-    const bool&       isBoldRequired,
-    GlyphCacheData&   data,
-    FT_Error&         error);
+    const GlyphIndex index,
+    const FT_Int32   flag,
+    const bool       isBoldRequired,
+    GlyphCacheData&  data,
+    FT_Error&        error);
 
   /**
    * @brief Resize bitmap glyph. The result will change cached glyph bitmap information.
@@ -121,11 +131,28 @@ public:
    * @param[in] desiredHeight Desired height of bitmap.
    */
   void ResizeBitmapGlyph(
-    const GlyphIndex& index,
-    const FT_Int32&   flag,
-    const bool&       isBoldRequired,
-    const uint32_t&   desiredWidth,
-    const uint32_t&   desiredHeight);
+    const GlyphIndex index,
+    const FT_Int32   flag,
+    const bool       isBoldRequired,
+    const uint32_t   desiredWidth,
+    const uint32_t   desiredHeight);
+
+  /**
+   * @brief Cache rendered glyph bitmap. The result will change cached glyph information.
+   * If glyph is not single color glyph, or we already cached buffer before, nothing happened.
+   *
+   * @param[in] index Index of glyph in this face.
+   * @param[in] flag Flag when we load the glyph.
+   * @param[in] isBoldRequired True if we require some software bold.
+   * @param[in] srcBitmap Rendered glyph bitmap.
+   * @param[in] policy Compress behavior policy.
+   */
+  void CacheRenderedGlyphBuffer(
+    const GlyphIndex            index,
+    const FT_Int32              flag,
+    const bool                  isBoldRequired,
+    const FT_Bitmap&            srcBitmap,
+    const CompressionPolicyType policy);
 
 private:
   // Private struct area.
@@ -141,7 +168,7 @@ private:
     {
     }
 
-    GlyphCacheKey(const GlyphIndex& index, const FT_Int32& flag, const bool& boldRequired)
+    GlyphCacheKey(const GlyphIndex index, const FT_Int32 flag, const bool boldRequired)
     : mIndex(index),
       mFlag(flag),
       mIsBoldRequired(boldRequired)
index d6f8d23..5c84f67 100644 (file)
  */
 
 // EXTERNAL INCLUDES
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/common/vector-wrapper.h>
 #include <limits> // for std::numeric_limits
 #include <unordered_map>
-#include <vector>
 
 namespace Dali::TextAbstraction::Internal
 {
index 3c2e234..136f20a 100644 (file)
@@ -45,7 +45,8 @@ DisplayConnection* DisplayConnectionEcoreWl::New()
 DisplayConnectionEcoreWl::DisplayConnectionEcoreWl()
 : mDisplay(NULL),
   mSurfaceType(RenderSurfaceInterface::WINDOW_RENDER_SURFACE),
-  mGraphics(nullptr)
+  mGraphics(nullptr),
+  mBufMgr(nullptr)
 {
 }
 
@@ -106,6 +107,12 @@ void DisplayConnectionEcoreWl::SetGraphicsInterface(GraphicsInterface& graphics)
 
 EGLNativeDisplayType DisplayConnectionEcoreWl::GetNativeDisplay()
 {
+  mBufMgr = tbm_bufmgr_init(-1);  // -1 is meaningless. The parameter in this function is deprecated.
+  if(mBufMgr == nullptr)
+  {
+    DALI_LOG_ERROR("Fail to init tbm buf mgr\n");
+    return nullptr;
+  }
   return reinterpret_cast<EGLNativeDisplayType>(tbm_dummy_display_create());
 }
 
@@ -115,6 +122,11 @@ void DisplayConnectionEcoreWl::ReleaseNativeDisplay()
   {
     tbm_dummy_display_destroy(reinterpret_cast<tbm_dummy_display*>(mDisplay));
   }
+
+  if(mBufMgr != nullptr)
+  {
+    tbm_bufmgr_deinit(mBufMgr);
+  }
 }
 
 } // namespace Adaptor
index 191a9ee..06a3dad 100644 (file)
@@ -18,6 +18,9 @@
  *
  */
 
+// EXTERNAL INCLUDES
+#include <tbm_bufmgr.h>
+
 // INTERNAL INCLUDES
 #include <dali/internal/window-system/common/display-connection-impl.h>
 
@@ -102,6 +105,7 @@ private:
   EGLNativeDisplayType               mDisplay;     ///< Wayland-display for rendering
   Dali::RenderSurfaceInterface::Type mSurfaceType; ///< The surface type
   GraphicsInterface*                 mGraphics;    ///< The graphics interface
+  tbm_bufmgr                         mBufMgr;      ///< For creating tbm_dummy_display
 };
 
 } // namespace Adaptor
index e71ab8d..ccf2eee 100644 (file)
@@ -27,7 +27,7 @@ namespace Dali
 {
 const unsigned int ADAPTOR_MAJOR_VERSION = 2;
 const unsigned int ADAPTOR_MINOR_VERSION = 1;
-const unsigned int ADAPTOR_MICRO_VERSION = 28;
+const unsigned int ADAPTOR_MICRO_VERSION = 29;
 const char* const  ADAPTOR_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index 5be309c..9f9648e 100644 (file)
@@ -17,7 +17,7 @@
 
 Name:       dali2-adaptor
 Summary:    The DALi Tizen Adaptor
-Version:    2.1.28
+Version:    2.1.29
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT