2 * Copyright (c) 2022 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>
26 #include <dali/internal/imaging/common/alpha-mask.h>
27 #include <dali/internal/imaging/common/gaussian-blur.h>
28 #include <dali/internal/imaging/common/image-operations.h>
29 #include <dali/internal/imaging/common/pixel-manipulation.h>
39 const float TWO_PI = 2.f * Math::PI; ///< 360 degrees in radians
40 // based on W3C Recommendations (https://www.w3.org/TR/AERT/#color-contrast)
41 constexpr uint32_t BRIGHTNESS_CONSTANT_R = 299;
42 constexpr uint32_t BRIGHTNESS_CONSTANT_G = 587;
43 constexpr uint32_t BRIGHTNESS_CONSTANT_B = 114;
46 PixelBuffer::PixelBuffer(unsigned char* buffer,
47 unsigned int bufferSize,
51 Dali::Pixel::Format pixelFormat)
54 mBufferSize(bufferSize),
57 mStride(stride ? stride : width),
58 mPixelFormat(pixelFormat),
63 PixelBuffer::~PixelBuffer()
68 PixelBufferPtr PixelBuffer::New(unsigned int width,
70 Dali::Pixel::Format pixelFormat)
72 unsigned int bufferSize = width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat);
73 unsigned char* buffer = NULL;
76 buffer = static_cast<unsigned char*>(malloc(bufferSize));
78 return new PixelBuffer(buffer, bufferSize, width, height, width, pixelFormat);
81 PixelBufferPtr PixelBuffer::New(unsigned char* buffer,
82 unsigned int bufferSize,
86 Dali::Pixel::Format pixelFormat)
88 return new PixelBuffer(buffer, bufferSize, width, height, stride, pixelFormat);
91 Dali::PixelData PixelBuffer::Convert(PixelBuffer& pixelBuffer)
93 Dali::PixelData pixelData = Dali::PixelData::New(pixelBuffer.mBuffer,
94 pixelBuffer.mBufferSize,
98 pixelBuffer.mPixelFormat,
99 Dali::PixelData::FREE);
100 pixelBuffer.mBuffer = NULL;
101 pixelBuffer.mWidth = 0;
102 pixelBuffer.mHeight = 0;
103 pixelBuffer.mBufferSize = 0;
104 pixelBuffer.mStride = 0;
109 unsigned int PixelBuffer::GetWidth() const
114 unsigned int PixelBuffer::GetHeight() const
119 uint32_t PixelBuffer::GetStride() const
124 Dali::Pixel::Format PixelBuffer::GetPixelFormat() const
129 unsigned char* PixelBuffer::GetBuffer() const
134 const unsigned char* const PixelBuffer::GetConstBuffer() const
139 unsigned int PixelBuffer::GetBufferSize() const
144 Dali::PixelData PixelBuffer::CreatePixelData() const
146 unsigned char* destBuffer = NULL;
150 destBuffer = static_cast<unsigned char*>(malloc(mBufferSize));
151 memcpy(destBuffer, mBuffer, mBufferSize);
154 Dali::PixelData pixelData = Dali::PixelData::New(destBuffer, mBufferSize, mWidth, mHeight, mStride, mPixelFormat, Dali::PixelData::FREE);
158 void PixelBuffer::ApplyMask(const PixelBuffer& inMask, float contentScale, bool cropToMask)
162 // First scale this buffer by the contentScale, and crop to the mask size
163 // If it's too small, then scale the mask to match the image size
164 // Then apply the mask
165 ScaleAndCrop(contentScale, ImageDimensions(inMask.GetWidth(), inMask.GetHeight()));
167 if(inMask.mWidth > mWidth || inMask.mHeight > mHeight)
169 PixelBufferPtr mask = NewResize(inMask, ImageDimensions(mWidth, mHeight));
170 ApplyMaskInternal(*mask);
174 ApplyMaskInternal(inMask);
179 // First, scale the mask to match the image size,
180 // then apply the mask.
181 PixelBufferPtr mask = NewResize(inMask, ImageDimensions(mWidth, mHeight));
182 ApplyMaskInternal(*mask);
186 void PixelBuffer::ApplyMaskInternal(const PixelBuffer& mask)
191 Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
192 if(Dali::Pixel::HasAlpha(mPixelFormat) && bitMask == 255)
194 ApplyMaskToAlphaChannel(*this, mask);
198 PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer(*this, mask);
199 TakeOwnershipOfBuffer(*newPixelBuffer);
200 // On leaving scope, newPixelBuffer will get destroyed.
204 void PixelBuffer::TakeOwnershipOfBuffer(PixelBuffer& pixelBuffer)
208 // Take ownership of new buffer
209 mBuffer = pixelBuffer.mBuffer;
210 pixelBuffer.mBuffer = NULL;
211 mBufferSize = pixelBuffer.mBufferSize;
212 mWidth = pixelBuffer.mWidth;
213 mHeight = pixelBuffer.mHeight;
214 mStride = pixelBuffer.mStride;
215 mPixelFormat = pixelBuffer.mPixelFormat;
218 void PixelBuffer::ReleaseBuffer()
226 void PixelBuffer::AllocateFixedSize(uint32_t size)
229 mBuffer = reinterpret_cast<unsigned char*>(malloc(size));
233 bool PixelBuffer::Rotate(Degree angle)
235 // Check first if Rotate() can perform the operation in the current pixel buffer.
237 bool validPixelFormat = false;
246 case Pixel::RGBA8888:
247 case Pixel::BGRA8888: // FALL THROUGH
249 validPixelFormat = true;
254 // This pixel format is not supported for this operation.
255 validPixelFormat = false;
260 if(!validPixelFormat)
262 // Can't rotate the pixel buffer with the current pixel format.
263 DALI_LOG_ERROR("Can't rotate the pixel buffer with the current pixel format\n");
267 float radians = Radian(angle).radian;
269 // Transform the input angle into the range [0..2PI]
270 radians = fmod(radians, TWO_PI);
271 radians += (radians < 0.f) ? TWO_PI : 0.f;
273 if(radians < Dali::Math::MACHINE_EPSILON_10)
275 // Nothing to do if the angle is zero.
279 const unsigned int pixelSize = Pixel::GetBytesPerPixel(mPixelFormat);
281 uint8_t* pixelsOut = nullptr;
282 Platform::RotateByShear(mBuffer,
292 // Check whether the rotation succedded and set the new pixel buffer data.
293 const bool success = nullptr != pixelsOut;
297 // Release the memory of the current pixel buffer.
300 // Set the new pixel buffer.
303 mBufferSize = mWidth * mHeight * pixelSize;
304 mStride = mWidth; // The buffer is tightly packed.
310 void PixelBuffer::ScaleAndCrop(float scaleFactor, ImageDimensions cropDimensions)
312 ImageDimensions outDimensions(float(mWidth) * scaleFactor,
313 float(mHeight) * scaleFactor);
315 if(outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight)
317 Resize(outDimensions);
320 ImageDimensions postCropDimensions(
321 std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
322 std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
324 if(postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
325 postCropDimensions.GetHeight() < outDimensions.GetHeight())
327 uint16_t x = (outDimensions.GetWidth() - postCropDimensions.GetWidth()) / 2;
328 uint16_t y = (outDimensions.GetHeight() - postCropDimensions.GetHeight()) / 2;
329 Crop(x, y, postCropDimensions);
333 void PixelBuffer::Crop(uint16_t x, uint16_t y, ImageDimensions cropDimensions)
335 PixelBufferPtr outBuffer = NewCrop(*this, x, y, cropDimensions);
336 TakeOwnershipOfBuffer(*outBuffer);
339 PixelBufferPtr PixelBuffer::NewCrop(const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions)
341 PixelBufferPtr outBuffer = PixelBuffer::New(cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat());
342 int bytesPerPixel = Pixel::GetBytesPerPixel(inBuffer.mPixelFormat);
343 int srcStride = inBuffer.mStride * bytesPerPixel;
344 int destStride = cropDimensions.GetWidth() * bytesPerPixel; // The destination buffer is tightly packed
346 // Clamp crop to right edge
347 if(x + cropDimensions.GetWidth() > inBuffer.mWidth)
349 destStride = (inBuffer.mWidth - x) * bytesPerPixel;
352 int srcOffset = x * bytesPerPixel + y * srcStride;
354 unsigned char* destBuffer = outBuffer->mBuffer;
356 // Clamp crop to last row
357 unsigned int endRow = y + cropDimensions.GetHeight();
358 if(endRow > inBuffer.mHeight)
360 endRow = inBuffer.mHeight - 1;
362 for(uint16_t row = y; row < endRow; ++row)
364 memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride);
365 srcOffset += srcStride;
366 destOffset += destStride;
371 void PixelBuffer::SetMetadata(const Property::Map& map)
373 mMetadata.reset(new Property::Map(map));
376 bool PixelBuffer::GetMetadata(Property::Map& outMetadata) const
382 outMetadata = *mMetadata;
386 void PixelBuffer::SetMetadata(std::unique_ptr<Property::Map> metadata)
388 mMetadata = std::move(metadata);
391 void PixelBuffer::Resize(ImageDimensions outDimensions)
393 if(mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight())
395 PixelBufferPtr outBuffer = NewResize(*this, outDimensions);
396 TakeOwnershipOfBuffer(*outBuffer);
400 PixelBufferPtr PixelBuffer::NewResize(const PixelBuffer& inBuffer, ImageDimensions outDimensions)
402 PixelBufferPtr outBuffer = PixelBuffer::New(outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat());
403 ImageDimensions inDimensions(inBuffer.mWidth, inBuffer.mHeight);
405 bool hasAlpha = Pixel::HasAlpha(inBuffer.mPixelFormat);
406 int bytesPerPixel = Pixel::GetBytesPerPixel(inBuffer.mPixelFormat);
408 Resampler::Filter filterType = Resampler::LANCZOS4;
409 if(inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight())
411 filterType = Resampler::MITCHELL;
414 // This method only really works for 8 bit wide channels.
415 // (But could be expanded to work)
416 if(inBuffer.mPixelFormat == Pixel::A8 ||
417 inBuffer.mPixelFormat == Pixel::L8 ||
418 inBuffer.mPixelFormat == Pixel::LA88 ||
419 inBuffer.mPixelFormat == Pixel::RGB888 ||
420 inBuffer.mPixelFormat == Pixel::RGB8888 ||
421 inBuffer.mPixelFormat == Pixel::BGR8888 ||
422 inBuffer.mPixelFormat == Pixel::RGBA8888 ||
423 inBuffer.mPixelFormat == Pixel::BGRA8888)
425 Dali::Internal::Platform::Resample(inBuffer.mBuffer, inDimensions, inBuffer.mStride, outBuffer->GetBuffer(), outDimensions, filterType, bytesPerPixel, hasAlpha);
429 DALI_LOG_ERROR("Trying to resize an image with too narrow a channel width");
435 void PixelBuffer::ApplyGaussianBlur(const float blurRadius)
437 // This method only works for pixel buffer in RGBA format.
438 if(mWidth > 0 && mHeight > 0 && mPixelFormat == Pixel::RGBA8888)
440 if(blurRadius > Math::MACHINE_EPSILON_1)
442 PerformGaussianBlurRGBA(*this, blurRadius);
447 DALI_LOG_ERROR("Trying to apply gaussian blur to an empty pixel buffer or a pixel buffer not in RGBA format");
451 void PixelBuffer::MultiplyColorByAlpha()
453 auto bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
455 // Compressed textures have unknown size of the pixel. Alpha premultiplication
456 // must be skipped in such case
457 if(Pixel::GetBytesPerPixel(mPixelFormat) && Pixel::HasAlpha(mPixelFormat))
459 unsigned char* pixel = mBuffer;
460 const unsigned int strideBytes = mStride * bytesPerPixel;
461 const unsigned int widthBytes = mWidth * bytesPerPixel;
463 // Collect all valid channel list before lookup whole buffer
464 std::vector<Channel> validChannelList;
465 for(const Channel& channel : {Adaptor::RED, Adaptor::GREEN, Adaptor::BLUE, Adaptor::LUMINANCE})
467 if(HasChannel(mPixelFormat, channel))
469 validChannelList.emplace_back(channel);
473 if(DALI_LIKELY(!validChannelList.empty()))
475 for(unsigned int y = 0; y < mHeight; y++)
477 for(unsigned int x = 0; x < widthBytes; x += bytesPerPixel)
479 unsigned int alpha = ReadChannel(&pixel[x], mPixelFormat, Adaptor::ALPHA);
482 // If alpha is 255, we don't need to change color. Skip current pixel
483 // But if alpha is not 255, we should change color.
486 for(const Channel& channel : validChannelList)
488 auto color = ReadChannel(&pixel[x], mPixelFormat, channel);
489 WriteChannel(&pixel[x], mPixelFormat, channel, color * alpha / 255);
494 // If alpha is 0, just set all pixel as zero.
495 memset(&pixel[x], 0, bytesPerPixel);
499 pixel += strideBytes;
502 mPreMultiplied = true;
506 bool PixelBuffer::IsAlphaPreMultiplied() const
508 return mPreMultiplied;
511 uint32_t PixelBuffer::GetBrightness() const
513 uint32_t brightness = 0;
514 uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
516 if(bytesPerPixel && mWidth && mHeight)
518 unsigned char* pixel = mBuffer;
519 const uint32_t strideBytes = mStride * bytesPerPixel;
520 const uint32_t widthBytes = mWidth * bytesPerPixel;
521 const uint32_t bufferSize = mWidth * mHeight;
527 for(unsigned int y = 0; y < mHeight; y++)
529 for(unsigned int x = 0; x < widthBytes; x += bytesPerPixel)
531 red += ReadChannel(&pixel[x], mPixelFormat, Adaptor::RED);
532 green += ReadChannel(&pixel[x], mPixelFormat, Adaptor::GREEN);
533 blue += ReadChannel(&pixel[x], mPixelFormat, Adaptor::BLUE);
535 pixel += strideBytes;
538 // http://www.w3.org/TR/AERT#color-contrast
539 brightness = (red * BRIGHTNESS_CONSTANT_R + green * BRIGHTNESS_CONSTANT_G + blue * BRIGHTNESS_CONSTANT_B) / (1000uLL * bufferSize);
545 } // namespace Adaptor
546 } // namespace Internal