2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/imaging/common/pixel-buffer-impl.h>
22 #include <dali/integration-api/pixel-data-integ.h>
27 #include <dali/integration-api/debug.h>
28 #include <dali/internal/imaging/common/alpha-mask.h>
29 #include <dali/internal/imaging/common/gaussian-blur.h>
30 #include <dali/internal/imaging/common/image-operations.h>
31 #include <dali/internal/imaging/common/pixel-manipulation.h>
41 #if defined(DEBUG_ENABLED)
42 Debug::Filter* gPixelBufferFilter = Debug::Filter::New(Debug::NoLogging, false, "DALI_LOG_PIXEL_BUFFER_SIZE");
45 const float TWO_PI = 2.f * Math::PI; ///< 360 degrees in radians
46 // based on W3C Recommendations (https://www.w3.org/TR/AERT/#color-contrast)
47 constexpr uint32_t BRIGHTNESS_CONSTANT_R = 299;
48 constexpr uint32_t BRIGHTNESS_CONSTANT_G = 587;
49 constexpr uint32_t BRIGHTNESS_CONSTANT_B = 114;
52 #if defined(DEBUG_ENABLED)
53 uint32_t PixelBuffer::gPixelBufferAllocationTotal{0};
56 PixelBuffer::PixelBuffer(uint8_t* buffer,
61 Dali::Pixel::Format pixelFormat)
64 mBufferSize(bufferSize),
67 mStride(stride ? stride : width),
68 mPixelFormat(pixelFormat),
73 PixelBuffer::~PixelBuffer()
78 PixelBufferPtr PixelBuffer::New(uint32_t width,
80 Dali::Pixel::Format pixelFormat)
82 uint32_t bufferSize = width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat);
83 uint8_t* buffer = NULL;
86 buffer = static_cast<uint8_t*>(malloc(bufferSize));
87 if(DALI_UNLIKELY(!buffer))
89 DALI_LOG_ERROR("malloc is failed. request malloc size : %u\n", bufferSize);
91 #if defined(DEBUG_ENABLED)
92 gPixelBufferAllocationTotal += bufferSize;
95 DALI_LOG_INFO(gPixelBufferFilter, Debug::Concise, "Allocated PixelBuffer of size %u\n", bufferSize);
97 return new PixelBuffer(buffer, bufferSize, width, height, width, pixelFormat);
100 PixelBufferPtr PixelBuffer::New(uint8_t* buffer,
105 Dali::Pixel::Format pixelFormat)
107 return new PixelBuffer(buffer, bufferSize, width, height, stride, pixelFormat);
110 Dali::PixelData PixelBuffer::Convert(PixelBuffer& pixelBuffer, bool releaseAfterUpload)
112 #if defined(DEBUG_ENABLED)
113 gPixelBufferAllocationTotal -= pixelBuffer.mBufferSize;
115 Dali::PixelData pixelData;
116 if(releaseAfterUpload)
118 pixelData = Dali::Integration::NewPixelDataWithReleaseAfterUpload(pixelBuffer.mBuffer,
119 pixelBuffer.mBufferSize,
123 pixelBuffer.mPixelFormat,
124 Dali::PixelData::FREE);
128 pixelData = Dali::PixelData::New(pixelBuffer.mBuffer,
129 pixelBuffer.mBufferSize,
133 pixelBuffer.mPixelFormat,
134 Dali::PixelData::FREE);
136 pixelBuffer.mBuffer = NULL;
137 pixelBuffer.mWidth = 0;
138 pixelBuffer.mHeight = 0;
139 pixelBuffer.mBufferSize = 0;
140 pixelBuffer.mStride = 0;
145 uint32_t PixelBuffer::GetWidth() const
150 uint32_t PixelBuffer::GetHeight() const
155 uint32_t PixelBuffer::GetStride() const
160 Dali::Pixel::Format PixelBuffer::GetPixelFormat() const
165 uint8_t* PixelBuffer::GetBuffer() const
170 const uint8_t* PixelBuffer::GetConstBuffer() const
175 uint32_t PixelBuffer::GetBufferSize() const
180 Dali::PixelData PixelBuffer::CreatePixelData() const
182 uint8_t* destBuffer = NULL;
186 destBuffer = static_cast<uint8_t*>(malloc(mBufferSize));
187 if(DALI_UNLIKELY(!destBuffer))
189 DALI_LOG_ERROR("malloc is failed. request malloc size : %u\n", mBufferSize);
190 return Dali::PixelData();
192 memcpy(destBuffer, mBuffer, mBufferSize);
195 Dali::PixelData pixelData = Dali::PixelData::New(destBuffer, mBufferSize, mWidth, mHeight, mStride, mPixelFormat, Dali::PixelData::FREE);
199 void PixelBuffer::ApplyMask(const PixelBuffer& inMask, float contentScale, bool cropToMask)
203 // First scale this buffer by the contentScale, and crop to the mask size
204 // If it's too small, then scale the mask to match the image size
205 // Then apply the mask
206 ScaleAndCrop(contentScale, ImageDimensions(inMask.GetWidth(), inMask.GetHeight()));
208 if(inMask.mWidth > mWidth || inMask.mHeight > mHeight)
210 PixelBufferPtr mask = NewResize(inMask, ImageDimensions(mWidth, mHeight));
211 ApplyMaskInternal(*mask);
215 ApplyMaskInternal(inMask);
220 // First, scale the mask to match the image size,
221 // then apply the mask.
222 PixelBufferPtr mask = NewResize(inMask, ImageDimensions(mWidth, mHeight));
223 ApplyMaskInternal(*mask);
227 void PixelBuffer::ApplyMaskInternal(const PixelBuffer& mask)
232 Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
233 if(Dali::Pixel::HasAlpha(mPixelFormat) && bitMask == 255)
235 ApplyMaskToAlphaChannel(*this, mask);
239 PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer(*this, mask);
240 TakeOwnershipOfBuffer(*newPixelBuffer);
241 // On leaving scope, newPixelBuffer will get destroyed.
245 void PixelBuffer::TakeOwnershipOfBuffer(PixelBuffer& pixelBuffer)
249 // Take ownership of new buffer
250 mBuffer = pixelBuffer.mBuffer;
251 pixelBuffer.mBuffer = NULL;
252 mBufferSize = pixelBuffer.mBufferSize;
253 mWidth = pixelBuffer.mWidth;
254 mHeight = pixelBuffer.mHeight;
255 mStride = pixelBuffer.mStride;
256 mPixelFormat = pixelBuffer.mPixelFormat;
259 void PixelBuffer::ReleaseBuffer()
263 #if defined(DEBUG_ENABLED)
264 gPixelBufferAllocationTotal -= mBufferSize;
271 void PixelBuffer::AllocateFixedSize(uint32_t size)
274 mBuffer = reinterpret_cast<unsigned char*>(malloc(size));
276 if(DALI_UNLIKELY(!mBuffer))
278 DALI_LOG_ERROR("malloc is failed. request malloc size : %u\n", mBufferSize);
280 #if defined(DEBUG_ENABLED)
281 gPixelBufferAllocationTotal += size;
285 bool PixelBuffer::Rotate(Degree angle)
287 // Check first if Rotate() can perform the operation in the current pixel buffer.
289 bool validPixelFormat = false;
298 case Pixel::RGBA8888:
299 case Pixel::BGRA8888: // FALL THROUGH
301 validPixelFormat = true;
306 // This pixel format is not supported for this operation.
307 validPixelFormat = false;
312 if(!validPixelFormat)
314 // Can't rotate the pixel buffer with the current pixel format.
315 DALI_LOG_ERROR("Can't rotate the pixel buffer with the current pixel format\n");
319 float radians = Radian(angle).radian;
321 // Transform the input angle into the range [0..2PI]
322 radians = fmod(radians, TWO_PI);
323 radians += (radians < 0.f) ? TWO_PI : 0.f;
325 if(radians < Dali::Math::MACHINE_EPSILON_10)
327 // Nothing to do if the angle is zero.
331 const unsigned int pixelSize = Pixel::GetBytesPerPixel(mPixelFormat);
333 uint8_t* pixelsOut = nullptr;
334 Platform::RotateByShear(mBuffer,
344 // Check whether the rotation succedded and set the new pixel buffer data.
345 const bool success = nullptr != pixelsOut;
349 // Release the memory of the current pixel buffer.
352 // Set the new pixel buffer.
355 mBufferSize = mWidth * mHeight * pixelSize;
356 mStride = mWidth; // The buffer is tightly packed.
358 #if defined(DEBUG_ENABLED)
359 gPixelBufferAllocationTotal += mBufferSize;
366 void PixelBuffer::ScaleAndCrop(float scaleFactor, ImageDimensions cropDimensions)
368 ImageDimensions outDimensions(float(mWidth) * scaleFactor,
369 float(mHeight) * scaleFactor);
371 if(outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight)
373 Resize(outDimensions);
376 ImageDimensions postCropDimensions(
377 std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
378 std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
380 if(postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
381 postCropDimensions.GetHeight() < outDimensions.GetHeight())
383 uint16_t x = (outDimensions.GetWidth() - postCropDimensions.GetWidth()) / 2;
384 uint16_t y = (outDimensions.GetHeight() - postCropDimensions.GetHeight()) / 2;
385 Crop(x, y, postCropDimensions);
389 void PixelBuffer::Crop(uint16_t x, uint16_t y, ImageDimensions cropDimensions)
391 PixelBufferPtr outBuffer = NewCrop(*this, x, y, cropDimensions);
392 TakeOwnershipOfBuffer(*outBuffer);
395 PixelBufferPtr PixelBuffer::NewCrop(const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions)
397 PixelBufferPtr outBuffer = PixelBuffer::New(cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat());
398 int bytesPerPixel = Pixel::GetBytesPerPixel(inBuffer.mPixelFormat);
399 int srcStride = inBuffer.mStride * bytesPerPixel;
400 int destStride = cropDimensions.GetWidth() * bytesPerPixel; // The destination buffer is tightly packed
402 // Clamp crop to right edge
403 if(x + cropDimensions.GetWidth() > inBuffer.mWidth)
405 destStride = (inBuffer.mWidth - x) * bytesPerPixel;
408 int srcOffset = x * bytesPerPixel + y * srcStride;
410 uint8_t* destBuffer = outBuffer->mBuffer;
412 // Clamp crop to last row
413 uint16_t endRow = y + cropDimensions.GetHeight();
414 if(endRow > inBuffer.mHeight)
416 endRow = inBuffer.mHeight - 1;
418 for(uint16_t row = y; row < endRow; ++row)
420 memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride);
421 srcOffset += srcStride;
422 destOffset += destStride;
427 void PixelBuffer::SetMetadata(const Property::Map& map)
429 mMetadata.reset(new Property::Map(map));
432 bool PixelBuffer::GetMetadata(Property::Map& outMetadata) const
438 outMetadata = *mMetadata;
442 void PixelBuffer::SetMetadata(std::unique_ptr<Property::Map> metadata)
444 mMetadata = std::move(metadata);
447 void PixelBuffer::Resize(ImageDimensions outDimensions)
449 if(mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight())
451 PixelBufferPtr outBuffer = NewResize(*this, outDimensions);
452 TakeOwnershipOfBuffer(*outBuffer);
456 PixelBufferPtr PixelBuffer::NewResize(const PixelBuffer& inBuffer, ImageDimensions outDimensions)
458 PixelBufferPtr outBuffer = PixelBuffer::New(outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat());
459 ImageDimensions inDimensions(inBuffer.mWidth, inBuffer.mHeight);
461 bool hasAlpha = Pixel::HasAlpha(inBuffer.mPixelFormat);
462 int bytesPerPixel = Pixel::GetBytesPerPixel(inBuffer.mPixelFormat);
464 Resampler::Filter filterType = Resampler::LANCZOS4;
465 if(inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight())
467 filterType = Resampler::MITCHELL;
470 // This method only really works for 8 bit wide channels.
471 // (But could be expanded to work)
472 if(inBuffer.mPixelFormat == Pixel::A8 ||
473 inBuffer.mPixelFormat == Pixel::L8 ||
474 inBuffer.mPixelFormat == Pixel::LA88 ||
475 inBuffer.mPixelFormat == Pixel::RGB888 ||
476 inBuffer.mPixelFormat == Pixel::RGB8888 ||
477 inBuffer.mPixelFormat == Pixel::BGR8888 ||
478 inBuffer.mPixelFormat == Pixel::RGBA8888 ||
479 inBuffer.mPixelFormat == Pixel::BGRA8888)
481 Dali::Internal::Platform::Resample(inBuffer.mBuffer, inDimensions, inBuffer.mStride, outBuffer->GetBuffer(), outDimensions, filterType, bytesPerPixel, hasAlpha);
485 DALI_LOG_ERROR("Trying to resize an image with too narrow a channel width");
491 void PixelBuffer::ApplyGaussianBlur(const float blurRadius)
493 // This method only works for pixel buffer in RGBA format.
494 if(mWidth > 0 && mHeight > 0 && mPixelFormat == Pixel::RGBA8888)
496 if(blurRadius > Math::MACHINE_EPSILON_1)
498 PerformGaussianBlurRGBA(*this, blurRadius);
503 DALI_LOG_ERROR("Trying to apply gaussian blur to an empty pixel buffer or a pixel buffer not in RGBA format");
507 void PixelBuffer::MultiplyColorByAlpha()
509 // Compressed textures have unknown size of the pixel. Alpha premultiplication
510 // must be skipped in such case
511 if(!Pixel::IsCompressed(mPixelFormat) && Pixel::HasAlpha(mPixelFormat))
513 auto bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
515 uint8_t* pixel = mBuffer;
516 const uint32_t strideBytes = mStride * bytesPerPixel;
517 const uint32_t widthBytes = mWidth * bytesPerPixel;
519 // Collect all valid channel list before lookup whole buffer
520 std::vector<Channel> validChannelList;
521 for(const Channel& channel : {Adaptor::RED, Adaptor::GREEN, Adaptor::BLUE, Adaptor::LUMINANCE})
523 if(HasChannel(mPixelFormat, channel))
525 validChannelList.emplace_back(channel);
529 if(DALI_LIKELY(!validChannelList.empty()))
531 for(uint32_t y = 0; y < mHeight; y++)
533 for(uint32_t x = 0; x < widthBytes; x += bytesPerPixel)
535 uint32_t alpha = ReadChannel(&pixel[x], mPixelFormat, Adaptor::ALPHA);
538 // If alpha is 255, we don't need to change color. Skip current pixel
539 // But if alpha is not 255, we should change color.
542 for(const Channel& channel : validChannelList)
544 auto color = ReadChannel(&pixel[x], mPixelFormat, channel);
545 WriteChannel(&pixel[x], mPixelFormat, channel, Platform::MultiplyAndNormalizeColor(color, alpha));
550 // If alpha is 0, just set all pixel as zero.
551 memset(&pixel[x], 0, bytesPerPixel);
555 pixel += strideBytes;
558 mPreMultiplied = true;
562 bool PixelBuffer::IsAlphaPreMultiplied() const
564 return mPreMultiplied;
567 uint32_t PixelBuffer::GetBrightness() const
569 uint32_t brightness = 0;
570 uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
572 if(bytesPerPixel && mWidth && mHeight)
574 uint8_t* pixel = mBuffer;
575 const uint32_t strideBytes = mStride * bytesPerPixel;
576 const uint32_t widthBytes = mWidth * bytesPerPixel;
577 const uint32_t bufferSize = mWidth * mHeight;
583 for(uint32_t y = 0; y < mHeight; y++)
585 for(uint32_t x = 0; x < widthBytes; x += bytesPerPixel)
587 red += ReadChannel(&pixel[x], mPixelFormat, Adaptor::RED);
588 green += ReadChannel(&pixel[x], mPixelFormat, Adaptor::GREEN);
589 blue += ReadChannel(&pixel[x], mPixelFormat, Adaptor::BLUE);
591 pixel += strideBytes;
594 // http://www.w3.org/TR/AERT#color-contrast
595 brightness = (red * BRIGHTNESS_CONSTANT_R + green * BRIGHTNESS_CONSTANT_G + blue * BRIGHTNESS_CONSTANT_B) / (1000uLL * bufferSize);
601 } // namespace Adaptor
602 } // namespace Internal