Make PixelData flag that we release buffer after texture upload
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / pixel-buffer-impl.cpp
index 6ed2e31..3afdce5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 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.
 #include <dali/internal/imaging/common/pixel-buffer-impl.h>
 
 // EXTERNAL INCLUDES
+#include <dali/integration-api/pixel-data-integ.h>
 #include <stdlib.h>
 #include <cstring>
 
 // INTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
 #include <dali/internal/imaging/common/alpha-mask.h>
 #include <dali/internal/imaging/common/gaussian-blur.h>
 #include <dali/internal/imaging/common/image-operations.h>
@@ -36,6 +38,10 @@ namespace Adaptor
 {
 namespace
 {
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gPixelBufferFilter = Debug::Filter::New(Debug::NoLogging, false, "DALI_LOG_PIXEL_BUFFER_SIZE");
+#endif
+
 const float TWO_PI = 2.f * Math::PI; ///< 360 degrees in radians
 // based on W3C Recommendations (https://www.w3.org/TR/AERT/#color-contrast)
 constexpr uint32_t BRIGHTNESS_CONSTANT_R = 299;
@@ -43,16 +49,22 @@ constexpr uint32_t BRIGHTNESS_CONSTANT_G = 587;
 constexpr uint32_t BRIGHTNESS_CONSTANT_B = 114;
 } // namespace
 
-PixelBuffer::PixelBuffer(unsigned char*      buffer,
-                         unsigned int        bufferSize,
-                         unsigned int        width,
-                         unsigned int        height,
+#if defined(DEBUG_ENABLED)
+uint32_t PixelBuffer::gPixelBufferAllocationTotal{0};
+#endif
+
+PixelBuffer::PixelBuffer(uint8_t*            buffer,
+                         uint32_t            bufferSize,
+                         uint32_t            width,
+                         uint32_t            height,
+                         uint32_t            stride,
                          Dali::Pixel::Format pixelFormat)
 : mMetadata(),
   mBuffer(buffer),
   mBufferSize(bufferSize),
   mWidth(width),
   mHeight(height),
+  mStride(stride ? stride : width),
   mPixelFormat(pixelFormat),
   mPreMultiplied(false)
 {
@@ -63,85 +75,115 @@ PixelBuffer::~PixelBuffer()
   ReleaseBuffer();
 }
 
-PixelBufferPtr PixelBuffer::New(unsigned int        width,
-                                unsigned int        height,
+PixelBufferPtr PixelBuffer::New(uint32_t            width,
+                                uint32_t            height,
                                 Dali::Pixel::Format pixelFormat)
 {
-  unsigned int   bufferSize = width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat);
-  unsigned char* buffer     = NULL;
+  uint32_t bufferSize = width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat);
+  uint8_t* buffer     = NULL;
   if(bufferSize > 0)
   {
-    buffer = static_cast<unsigned char*>(malloc(bufferSize));
+    buffer = static_cast<uint8_t*>(malloc(bufferSize));
+#if defined(DEBUG_ENABLED)
+    gPixelBufferAllocationTotal += bufferSize;
+#endif
   }
-  return new PixelBuffer(buffer, bufferSize, width, height, pixelFormat);
+  DALI_LOG_INFO(gPixelBufferFilter, Debug::Concise, "Allocated PixelBuffer of size %u\n", bufferSize);
+
+  return new PixelBuffer(buffer, bufferSize, width, height, width, pixelFormat);
 }
 
-PixelBufferPtr PixelBuffer::New(unsigned char*      buffer,
-                                unsigned int        bufferSize,
-                                unsigned int        width,
-                                unsigned int        height,
+PixelBufferPtr PixelBuffer::New(uint8_t*            buffer,
+                                uint32_t            bufferSize,
+                                uint32_t            width,
+                                uint32_t            height,
+                                uint32_t            stride,
                                 Dali::Pixel::Format pixelFormat)
 {
-  return new PixelBuffer(buffer, bufferSize, width, height, pixelFormat);
+  return new PixelBuffer(buffer, bufferSize, width, height, stride, pixelFormat);
 }
 
-Dali::PixelData PixelBuffer::Convert(PixelBuffer& pixelBuffer)
+Dali::PixelData PixelBuffer::Convert(PixelBuffer& pixelBuffer, bool releaseAfterUpload)
 {
-  Dali::PixelData pixelData = Dali::PixelData::New(pixelBuffer.mBuffer,
-                                                   pixelBuffer.mBufferSize,
-                                                   pixelBuffer.mWidth,
-                                                   pixelBuffer.mHeight,
-                                                   pixelBuffer.mPixelFormat,
-                                                   Dali::PixelData::FREE);
-  pixelBuffer.mBuffer       = NULL;
-  pixelBuffer.mWidth        = 0;
-  pixelBuffer.mHeight       = 0;
-  pixelBuffer.mBufferSize   = 0;
+#if defined(DEBUG_ENABLED)
+  gPixelBufferAllocationTotal -= pixelBuffer.mBufferSize;
+#endif
+  Dali::PixelData pixelData;
+  if(releaseAfterUpload)
+  {
+    pixelData = Dali::Integration::NewPixelDataWithReleaseAfterUpload(pixelBuffer.mBuffer,
+                                                                      pixelBuffer.mBufferSize,
+                                                                      pixelBuffer.mWidth,
+                                                                      pixelBuffer.mHeight,
+                                                                      pixelBuffer.mStride,
+                                                                      pixelBuffer.mPixelFormat,
+                                                                      Dali::PixelData::FREE);
+  }
+  else
+  {
+    pixelData = Dali::PixelData::New(pixelBuffer.mBuffer,
+                                     pixelBuffer.mBufferSize,
+                                     pixelBuffer.mWidth,
+                                     pixelBuffer.mHeight,
+                                     pixelBuffer.mStride,
+                                     pixelBuffer.mPixelFormat,
+                                     Dali::PixelData::FREE);
+  }
+  pixelBuffer.mBuffer     = NULL;
+  pixelBuffer.mWidth      = 0;
+  pixelBuffer.mHeight     = 0;
+  pixelBuffer.mBufferSize = 0;
+  pixelBuffer.mStride     = 0;
 
   return pixelData;
 }
 
-unsigned int PixelBuffer::GetWidth() const
+uint32_t PixelBuffer::GetWidth() const
 {
   return mWidth;
 }
 
-unsigned int PixelBuffer::GetHeight() const
+uint32_t PixelBuffer::GetHeight() const
 {
   return mHeight;
 }
 
+uint32_t PixelBuffer::GetStride() const
+{
+  return mStride;
+}
+
 Dali::Pixel::Format PixelBuffer::GetPixelFormat() const
 {
   return mPixelFormat;
 }
 
-unsigned char* PixelBuffer::GetBuffer() const
+uint8_t* PixelBuffer::GetBuffer() const
 {
   return mBuffer;
 }
 
-const unsigned char* const PixelBuffer::GetConstBuffer() const
+const uint8_t* PixelBuffer::GetConstBuffer() const
 {
   return mBuffer;
 }
 
-unsigned int PixelBuffer::GetBufferSize() const
+uint32_t PixelBuffer::GetBufferSize() const
 {
   return mBufferSize;
 }
 
 Dali::PixelData PixelBuffer::CreatePixelData() const
 {
-  unsigned char* destBuffer = NULL;
+  uint8_t* destBuffer = NULL;
 
   if(mBufferSize > 0)
   {
-    destBuffer = static_cast<unsigned char*>(malloc(mBufferSize));
+    destBuffer = static_cast<uint8_t*>(malloc(mBufferSize));
     memcpy(destBuffer, mBuffer, mBufferSize);
   }
 
-  Dali::PixelData pixelData = Dali::PixelData::New(destBuffer, mBufferSize, mWidth, mHeight, mPixelFormat, Dali::PixelData::FREE);
+  Dali::PixelData pixelData = Dali::PixelData::New(destBuffer, mBufferSize, mWidth, mHeight, mStride, mPixelFormat, Dali::PixelData::FREE);
   return pixelData;
 }
 
@@ -201,6 +243,7 @@ void PixelBuffer::TakeOwnershipOfBuffer(PixelBuffer& pixelBuffer)
   mBufferSize         = pixelBuffer.mBufferSize;
   mWidth              = pixelBuffer.mWidth;
   mHeight             = pixelBuffer.mHeight;
+  mStride             = pixelBuffer.mStride;
   mPixelFormat        = pixelBuffer.mPixelFormat;
 }
 
@@ -208,7 +251,11 @@ void PixelBuffer::ReleaseBuffer()
 {
   if(mBuffer)
   {
+#if defined(DEBUG_ENABLED)
+    gPixelBufferAllocationTotal -= mBufferSize;
+#endif
     free(mBuffer);
+    mBuffer = nullptr;
   }
 }
 
@@ -217,6 +264,9 @@ void PixelBuffer::AllocateFixedSize(uint32_t size)
   ReleaseBuffer();
   mBuffer     = reinterpret_cast<unsigned char*>(malloc(size));
   mBufferSize = size;
+#if defined(DEBUG_ENABLED)
+  gPixelBufferAllocationTotal += size;
+#endif
 }
 
 bool PixelBuffer::Rotate(Degree angle)
@@ -271,6 +321,7 @@ bool PixelBuffer::Rotate(Degree angle)
   Platform::RotateByShear(mBuffer,
                           mWidth,
                           mHeight,
+                          mStride,
                           pixelSize,
                           radians,
                           pixelsOut,
@@ -289,6 +340,11 @@ bool PixelBuffer::Rotate(Degree angle)
     mBuffer     = pixelsOut;
     pixelsOut   = nullptr;
     mBufferSize = mWidth * mHeight * pixelSize;
+    mStride     = mWidth; // The buffer is tightly packed.
+
+#if defined(DEBUG_ENABLED)
+    gPixelBufferAllocationTotal += mBufferSize;
+#endif
   }
 
   return success;
@@ -327,8 +383,8 @@ PixelBufferPtr PixelBuffer::NewCrop(const PixelBuffer& inBuffer, uint16_t x, uin
 {
   PixelBufferPtr outBuffer     = PixelBuffer::New(cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat());
   int            bytesPerPixel = Pixel::GetBytesPerPixel(inBuffer.mPixelFormat);
-  int            srcStride     = inBuffer.mWidth * bytesPerPixel;
-  int            destStride    = cropDimensions.GetWidth() * bytesPerPixel;
+  int            srcStride     = inBuffer.mStride * bytesPerPixel;
+  int            destStride    = cropDimensions.GetWidth() * bytesPerPixel; // The destination buffer is tightly packed
 
   // Clamp crop to right edge
   if(x + cropDimensions.GetWidth() > inBuffer.mWidth)
@@ -336,12 +392,12 @@ PixelBufferPtr PixelBuffer::NewCrop(const PixelBuffer& inBuffer, uint16_t x, uin
     destStride = (inBuffer.mWidth - x) * bytesPerPixel;
   }
 
-  int            srcOffset  = x * bytesPerPixel + y * srcStride;
-  int            destOffset = 0;
-  unsigned char* destBuffer = outBuffer->mBuffer;
+  int      srcOffset  = x * bytesPerPixel + y * srcStride;
+  int      destOffset = 0;
+  uint8_t* destBuffer = outBuffer->mBuffer;
 
   // Clamp crop to last row
-  unsigned int endRow = y + cropDimensions.GetHeight();
+  uint16_t endRow = y + cropDimensions.GetHeight();
   if(endRow > inBuffer.mHeight)
   {
     endRow = inBuffer.mHeight - 1;
@@ -409,7 +465,7 @@ PixelBufferPtr PixelBuffer::NewResize(const PixelBuffer& inBuffer, ImageDimensio
      inBuffer.mPixelFormat == Pixel::RGBA8888 ||
      inBuffer.mPixelFormat == Pixel::BGRA8888)
   {
-    Dali::Internal::Platform::Resample(inBuffer.mBuffer, inDimensions, outBuffer->GetBuffer(), outDimensions, filterType, bytesPerPixel, hasAlpha);
+    Dali::Internal::Platform::Resample(inBuffer.mBuffer, inDimensions, inBuffer.mStride, outBuffer->GetBuffer(), outDimensions, filterType, bytesPerPixel, hasAlpha);
   }
   else
   {
@@ -437,32 +493,57 @@ void PixelBuffer::ApplyGaussianBlur(const float blurRadius)
 
 void PixelBuffer::MultiplyColorByAlpha()
 {
-  auto bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
-
   // Compressed textures have unknown size of the pixel. Alpha premultiplication
   // must be skipped in such case
-  if(Pixel::GetBytesPerPixel(mPixelFormat) && Pixel::HasAlpha(mPixelFormat))
+  if(!Pixel::IsCompressed(mPixelFormat) && Pixel::HasAlpha(mPixelFormat))
   {
-    unsigned char*     pixel      = mBuffer;
-    const unsigned int bufferSize = mWidth * mHeight;
+    auto bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
+
+    uint8_t*       pixel       = mBuffer;
+    const uint32_t strideBytes = mStride * bytesPerPixel;
+    const uint32_t widthBytes  = mWidth * bytesPerPixel;
+
+    // Collect all valid channel list before lookup whole buffer
+    std::vector<Channel> validChannelList;
+    for(const Channel& channel : {Adaptor::RED, Adaptor::GREEN, Adaptor::BLUE, Adaptor::LUMINANCE})
+    {
+      if(HasChannel(mPixelFormat, channel))
+      {
+        validChannelList.emplace_back(channel);
+      }
+    }
 
-    for(unsigned int i = 0; i < bufferSize; ++i)
+    if(DALI_LIKELY(!validChannelList.empty()))
     {
-      unsigned int alpha = ReadChannel(pixel, mPixelFormat, Adaptor::ALPHA);
+      for(uint32_t y = 0; y < mHeight; y++)
       {
-        auto red       = ReadChannel(pixel, mPixelFormat, Adaptor::RED);
-        auto green     = ReadChannel(pixel, mPixelFormat, Adaptor::GREEN);
-        auto blue      = ReadChannel(pixel, mPixelFormat, Adaptor::BLUE);
-        auto luminance = ReadChannel(pixel, mPixelFormat, Adaptor::LUMINANCE);
-        WriteChannel(pixel, mPixelFormat, Adaptor::RED, red * alpha / 255);
-        WriteChannel(pixel, mPixelFormat, Adaptor::GREEN, green * alpha / 255);
-        WriteChannel(pixel, mPixelFormat, Adaptor::BLUE, blue * alpha / 255);
-        WriteChannel(pixel, mPixelFormat, Adaptor::LUMINANCE, luminance * alpha / 255);
+        for(uint32_t x = 0; x < widthBytes; x += bytesPerPixel)
+        {
+          uint32_t alpha = ReadChannel(&pixel[x], mPixelFormat, Adaptor::ALPHA);
+          if(alpha < 255)
+          {
+            // If alpha is 255, we don't need to change color. Skip current pixel
+            // But if alpha is not 255, we should change color.
+            if(alpha > 0)
+            {
+              for(const Channel& channel : validChannelList)
+              {
+                auto color = ReadChannel(&pixel[x], mPixelFormat, channel);
+                WriteChannel(&pixel[x], mPixelFormat, channel, Platform::MultiplyAndNormalizeColor(color, alpha));
+              }
+            }
+            else
+            {
+              // If alpha is 0, just set all pixel as zero.
+              memset(&pixel[x], 0, bytesPerPixel);
+            }
+          }
+        }
+        pixel += strideBytes;
       }
-      pixel += bytesPerPixel;
     }
+    mPreMultiplied = true;
   }
-  mPreMultiplied = true;
 }
 
 bool PixelBuffer::IsAlphaPreMultiplied() const
@@ -472,30 +553,33 @@ bool PixelBuffer::IsAlphaPreMultiplied() const
 
 uint32_t PixelBuffer::GetBrightness() const
 {
-  uint32_t brightness = 0;
-
+  uint32_t brightness    = 0;
   uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
-  if(bytesPerPixel)
+
+  if(bytesPerPixel && mWidth && mHeight)
   {
-    unsigned char* pixel      = mBuffer;
-    const uint32_t bufferSize = mWidth * mHeight;
+    uint8_t*       pixel       = mBuffer;
+    const uint32_t strideBytes = mStride * bytesPerPixel;
+    const uint32_t widthBytes  = mWidth * bytesPerPixel;
+    const uint32_t bufferSize  = mWidth * mHeight;
 
-    if(bufferSize) // avoid division by zero to calculate brightness
-    {
-      uint64_t red   = 0;
-      uint64_t green = 0;
-      uint64_t blue  = 0;
+    uint64_t red   = 0;
+    uint64_t green = 0;
+    uint64_t blue  = 0;
 
-      for(uint32_t i = 0; i < bufferSize; ++i)
+    for(uint32_t y = 0; y < mHeight; y++)
+    {
+      for(uint32_t x = 0; x < widthBytes; x += bytesPerPixel)
       {
-        red += ReadChannel(pixel, mPixelFormat, Adaptor::RED);
-        green += ReadChannel(pixel, mPixelFormat, Adaptor::GREEN);
-        blue += ReadChannel(pixel, mPixelFormat, Adaptor::BLUE);
-        pixel += bytesPerPixel;
+        red += ReadChannel(&pixel[x], mPixelFormat, Adaptor::RED);
+        green += ReadChannel(&pixel[x], mPixelFormat, Adaptor::GREEN);
+        blue += ReadChannel(&pixel[x], mPixelFormat, Adaptor::BLUE);
       }
-      // http://www.w3.org/TR/AERT#color-contrast
-      brightness = (red * BRIGHTNESS_CONSTANT_R + green * BRIGHTNESS_CONSTANT_G + blue * BRIGHTNESS_CONSTANT_B) / (1000uLL * bufferSize);
+      pixel += strideBytes;
     }
+
+    // http://www.w3.org/TR/AERT#color-contrast
+    brightness = (red * BRIGHTNESS_CONSTANT_R + green * BRIGHTNESS_CONSTANT_G + blue * BRIGHTNESS_CONSTANT_B) / (1000uLL * bufferSize);
   }
 
   return brightness;