/*
- * 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>
{
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;
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)
{
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;
}
mBufferSize = pixelBuffer.mBufferSize;
mWidth = pixelBuffer.mWidth;
mHeight = pixelBuffer.mHeight;
+ mStride = pixelBuffer.mStride;
mPixelFormat = pixelBuffer.mPixelFormat;
}
{
if(mBuffer)
{
+#if defined(DEBUG_ENABLED)
+ gPixelBufferAllocationTotal -= mBufferSize;
+#endif
free(mBuffer);
+ mBuffer = nullptr;
}
}
ReleaseBuffer();
mBuffer = reinterpret_cast<unsigned char*>(malloc(size));
mBufferSize = size;
+#if defined(DEBUG_ENABLED)
+ gPixelBufferAllocationTotal += size;
+#endif
}
bool PixelBuffer::Rotate(Degree angle)
Platform::RotateByShear(mBuffer,
mWidth,
mHeight,
+ mStride,
pixelSize,
radians,
pixelsOut,
mBuffer = pixelsOut;
pixelsOut = nullptr;
mBufferSize = mWidth * mHeight * pixelSize;
+ mStride = mWidth; // The buffer is tightly packed.
+
+#if defined(DEBUG_ENABLED)
+ gPixelBufferAllocationTotal += mBufferSize;
+#endif
}
return success;
{
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)
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;
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
{
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
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;