2 * Copyright (c) 2018 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/pixel-manipulation.h>
27 #include <dali/internal/imaging/common/alpha-mask.h>
28 #include <dali/internal/imaging/common/gaussian-blur.h>
29 #include <dali/internal/imaging/common/image-operations.h>
42 const float TWO_PI = 2.f * Math::PI; ///< 360 degrees in radians
45 PixelBuffer::PixelBuffer( unsigned char* buffer,
46 unsigned int bufferSize,
49 Dali::Pixel::Format pixelFormat )
52 mBufferSize( bufferSize ),
55 mPixelFormat( pixelFormat ),
56 mPreMultiplied( false )
60 PixelBuffer::~PixelBuffer()
65 PixelBufferPtr PixelBuffer::New( unsigned int width,
67 Dali::Pixel::Format pixelFormat )
69 unsigned int bufferSize = width * height * Dali::Pixel::GetBytesPerPixel( pixelFormat );
70 unsigned char* buffer = NULL;
73 buffer = static_cast<unsigned char*>( malloc ( bufferSize ) );
75 return new PixelBuffer( buffer, bufferSize, width, height, pixelFormat );
78 PixelBufferPtr PixelBuffer::New( unsigned char* buffer,
79 unsigned int bufferSize,
82 Dali::Pixel::Format pixelFormat )
84 return new PixelBuffer( buffer, bufferSize, width, height, pixelFormat );
87 Dali::PixelData PixelBuffer::Convert( PixelBuffer& pixelBuffer )
89 Dali::PixelData pixelData = Dali::PixelData::New( pixelBuffer.mBuffer,
90 pixelBuffer.mBufferSize,
93 pixelBuffer.mPixelFormat,
94 Dali::PixelData::FREE );
95 pixelBuffer.mBuffer = NULL;
96 pixelBuffer.mWidth = 0;
97 pixelBuffer.mHeight = 0;
98 pixelBuffer.mBufferSize = 0;
103 unsigned int PixelBuffer::GetWidth() const
108 unsigned int PixelBuffer::GetHeight() const
113 Dali::Pixel::Format PixelBuffer::GetPixelFormat() const
118 unsigned char* PixelBuffer::GetBuffer() const
123 const unsigned char* const PixelBuffer::GetConstBuffer() const
128 unsigned int PixelBuffer::GetBufferSize() const
133 Dali::PixelData PixelBuffer::CreatePixelData() const
135 unsigned char* destBuffer = NULL;
137 if( mBufferSize > 0 )
139 destBuffer = static_cast<unsigned char*>( malloc( mBufferSize ) );
140 memcpy( destBuffer, mBuffer, mBufferSize );
143 Dali::PixelData pixelData = Dali::PixelData::New( destBuffer, mBufferSize,
146 Dali::PixelData::FREE );
150 void PixelBuffer::ApplyMask( const PixelBuffer& inMask, float contentScale, bool cropToMask )
154 // First scale this buffer by the contentScale, and crop to the mask size
155 // If it's too small, then scale the mask to match the image size
156 // Then apply the mask
157 ScaleAndCrop( contentScale, ImageDimensions( inMask.GetWidth(), inMask.GetHeight() ) );
159 if( inMask.mWidth > mWidth || inMask.mHeight > mHeight )
161 PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
162 ApplyMaskInternal( *mask );
166 ApplyMaskInternal( inMask );
171 // First, scale the mask to match the image size,
172 // then apply the mask.
173 PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
174 ApplyMaskInternal( *mask );
178 void PixelBuffer::ApplyMaskInternal( const PixelBuffer& mask )
183 Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
184 if( Dali::Pixel::HasAlpha( mPixelFormat ) && bitMask == 255 )
186 ApplyMaskToAlphaChannel( *this, mask );
190 PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer( *this, mask );
191 TakeOwnershipOfBuffer( *newPixelBuffer );
192 // On leaving scope, newPixelBuffer will get destroyed.
196 void PixelBuffer::TakeOwnershipOfBuffer( PixelBuffer& pixelBuffer )
200 // Take ownership of new buffer
201 mBuffer = pixelBuffer.mBuffer;
202 pixelBuffer.mBuffer = NULL;
203 mBufferSize = pixelBuffer.mBufferSize;
204 mWidth = pixelBuffer.mWidth;
205 mHeight = pixelBuffer.mHeight;
206 mPixelFormat = pixelBuffer.mPixelFormat;
209 void PixelBuffer::ReleaseBuffer()
217 void PixelBuffer::AllocateFixedSize( uint32_t size )
220 mBuffer = reinterpret_cast<unsigned char*>(malloc( size ));
224 bool PixelBuffer::Rotate( Degree angle )
226 // Check first if Rotate() can perform the operation in the current pixel buffer.
228 bool validPixelFormat = false;
229 switch( mPixelFormat )
237 case Pixel::RGBA8888:
238 case Pixel::BGRA8888: // FALL THROUGH
240 validPixelFormat = true;
245 // This pixel format is not supported for this operation.
246 validPixelFormat = false;
251 if( !validPixelFormat )
253 // Can't rotate the pixel buffer with the current pixel format.
254 DALI_LOG_ERROR( "Can't rotate the pixel buffer with the current pixel format\n" );
258 float radians = Radian( angle ).radian;
260 // Transform the input angle into the range [0..2PI]
261 radians = fmod( radians, TWO_PI );
262 radians += ( radians < 0.f ) ? TWO_PI : 0.f;
264 if( radians < Dali::Math::MACHINE_EPSILON_10 )
266 // Nothing to do if the angle is zero.
270 const unsigned int pixelSize = Pixel::GetBytesPerPixel( mPixelFormat );
272 uint8_t* pixelsOut = nullptr;
273 Platform::RotateByShear( mBuffer,
282 // Check whether the rotation succedded and set the new pixel buffer data.
283 const bool success = nullptr != pixelsOut;
287 // Release the memory of the current pixel buffer.
290 // Set the new pixel buffer.
293 mBufferSize = mWidth * mHeight * pixelSize;
299 void PixelBuffer::ScaleAndCrop( float scaleFactor, ImageDimensions cropDimensions )
301 ImageDimensions outDimensions( float(mWidth) * scaleFactor,
302 float(mHeight) * scaleFactor );
304 if( outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight )
306 Resize( outDimensions );
309 ImageDimensions postCropDimensions(
310 std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
311 std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
313 if( postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
314 postCropDimensions.GetHeight() < outDimensions.GetHeight() )
316 uint16_t x = ( outDimensions.GetWidth() - postCropDimensions.GetWidth() ) / 2;
317 uint16_t y = ( outDimensions.GetHeight() - postCropDimensions.GetHeight() ) / 2;
318 Crop( x, y, postCropDimensions );
322 void PixelBuffer::Crop( uint16_t x, uint16_t y, ImageDimensions cropDimensions )
324 PixelBufferPtr outBuffer = NewCrop( *this, x, y, cropDimensions );
325 TakeOwnershipOfBuffer( *outBuffer );
328 PixelBufferPtr PixelBuffer::NewCrop( const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions )
330 PixelBufferPtr outBuffer = PixelBuffer::New( cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat() );
331 int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
332 int srcStride = inBuffer.mWidth * bytesPerPixel;
333 int destStride = cropDimensions.GetWidth() * bytesPerPixel;
335 // Clamp crop to right edge
336 if( x + cropDimensions.GetWidth() > inBuffer.mWidth )
338 destStride = ( inBuffer.mWidth - x ) * bytesPerPixel;
341 int srcOffset = x * bytesPerPixel + y * srcStride;
343 unsigned char* destBuffer = outBuffer->mBuffer;
345 // Clamp crop to last row
346 unsigned int endRow = y + cropDimensions.GetHeight();
347 if( endRow > inBuffer.mHeight )
349 endRow = inBuffer.mHeight - 1 ;
351 for( uint16_t row = y; row < endRow; ++row )
353 memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride );
354 srcOffset += srcStride;
355 destOffset += destStride;
361 void PixelBuffer::SetMetadata( const Property::Map& map )
363 mMetadata.reset(new Property::Map(map));
366 bool PixelBuffer::GetMetadata(Property::Map& outMetadata) const
372 outMetadata = *mMetadata;
376 void PixelBuffer::SetMetadata(std::unique_ptr<Property::Map> metadata)
378 mMetadata = std::move(metadata);
381 void PixelBuffer::Resize( ImageDimensions outDimensions )
383 if( mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight() )
385 PixelBufferPtr outBuffer = NewResize( *this, outDimensions );
386 TakeOwnershipOfBuffer( *outBuffer );
390 PixelBufferPtr PixelBuffer::NewResize( const PixelBuffer& inBuffer, ImageDimensions outDimensions )
392 PixelBufferPtr outBuffer = PixelBuffer::New( outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat() );
393 ImageDimensions inDimensions( inBuffer.mWidth, inBuffer.mHeight );
395 bool hasAlpha = Pixel::HasAlpha( inBuffer.mPixelFormat );
396 int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
398 Resampler::Filter filterType = Resampler::LANCZOS4;
399 if( inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight() )
401 filterType = Resampler::MITCHELL;
404 // This method only really works for 8 bit wide channels.
405 // (But could be expanded to work)
406 if( inBuffer.mPixelFormat == Pixel::A8 ||
407 inBuffer.mPixelFormat == Pixel::L8 ||
408 inBuffer.mPixelFormat == Pixel::LA88 ||
409 inBuffer.mPixelFormat == Pixel::RGB888 ||
410 inBuffer.mPixelFormat == Pixel::RGB8888 ||
411 inBuffer.mPixelFormat == Pixel::BGR8888 ||
412 inBuffer.mPixelFormat == Pixel::RGBA8888 ||
413 inBuffer.mPixelFormat == Pixel::BGRA8888 )
415 Dali::Internal::Platform::Resample( inBuffer.mBuffer, inDimensions,
416 outBuffer->GetBuffer(), outDimensions,
417 filterType, bytesPerPixel, hasAlpha );
421 DALI_LOG_ERROR( "Trying to resize an image with too narrow a channel width" );
427 void PixelBuffer::ApplyGaussianBlur( const float blurRadius )
429 // This method only works for pixel buffer in RGBA format.
430 if( mWidth > 0 && mHeight > 0 && mPixelFormat == Pixel::RGBA8888 )
432 if ( blurRadius > Math::MACHINE_EPSILON_1 )
434 PerformGaussianBlurRGBA( *this, blurRadius );
439 DALI_LOG_ERROR( "Trying to apply gaussian blur to an empty pixel buffer or a pixel buffer not in RGBA format" );
443 void PixelBuffer::MultiplyColorByAlpha()
445 auto bytesPerPixel = Pixel::GetBytesPerPixel( mPixelFormat );
447 // Compressed textures have unknown size of the pixel. Alpha premultiplication
448 // must be skipped in such case
449 if( Pixel::GetBytesPerPixel(mPixelFormat) && Pixel::HasAlpha(mPixelFormat) )
451 unsigned char* pixel = mBuffer;
452 const unsigned int bufferSize = mWidth * mHeight;
454 for( unsigned int i=0; i<bufferSize; ++i )
456 unsigned int alpha = ReadChannel( pixel, mPixelFormat, Adaptor::ALPHA );
458 auto red = ReadChannel( pixel, mPixelFormat, Adaptor::RED);
459 auto green = ReadChannel( pixel, mPixelFormat, Adaptor::GREEN);
460 auto blue = ReadChannel( pixel, mPixelFormat, Adaptor::BLUE);
461 auto luminance = ReadChannel( pixel, mPixelFormat, Adaptor::LUMINANCE);
462 WriteChannel( pixel, mPixelFormat, Adaptor::RED, red*alpha / 255 );
463 WriteChannel( pixel, mPixelFormat, Adaptor::GREEN, green*alpha/255 );
464 WriteChannel( pixel, mPixelFormat, Adaptor::BLUE, blue*alpha/255 );
465 WriteChannel( pixel, mPixelFormat, Adaptor::LUMINANCE, luminance*alpha/255 );
467 pixel += bytesPerPixel;
470 mPreMultiplied = true;
473 bool PixelBuffer::IsAlphaPreMultiplied() const
475 return mPreMultiplied;
478 }// namespace Adaptor
479 }// namespace Internal