2 * Copyright (c) 2017 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 "pixel-buffer-impl.h"
26 #include "pixel-manipulation.h"
27 #include "alpha-mask.h"
28 #include "gaussian-blur.h"
29 #include <platform-abstractions/portable/image-operations.h>
30 #include <platform-abstractions/portable/pixel-manipulation.h>
41 PixelBuffer::PixelBuffer( unsigned char* buffer,
42 unsigned int bufferSize,
45 Dali::Pixel::Format pixelFormat )
48 mBufferSize( bufferSize ),
51 mPixelFormat( pixelFormat )
55 PixelBuffer::~PixelBuffer()
60 PixelBufferPtr PixelBuffer::New( unsigned int width,
62 Dali::Pixel::Format pixelFormat )
64 unsigned int bufferSize = width * height * Dali::Pixel::GetBytesPerPixel( pixelFormat );
65 unsigned char* buffer = NULL;
68 buffer = static_cast<unsigned char*>( malloc ( bufferSize ) );
70 return new PixelBuffer( buffer, bufferSize, width, height, pixelFormat );
73 PixelBufferPtr PixelBuffer::New( unsigned char* buffer,
74 unsigned int bufferSize,
77 Dali::Pixel::Format pixelFormat )
79 return new PixelBuffer( buffer, bufferSize, width, height, pixelFormat );
82 Dali::PixelData PixelBuffer::Convert( PixelBuffer& pixelBuffer )
84 Dali::PixelData pixelData = Dali::PixelData::New( pixelBuffer.mBuffer,
85 pixelBuffer.mBufferSize,
88 pixelBuffer.mPixelFormat,
89 Dali::PixelData::FREE );
90 pixelBuffer.mBuffer = NULL;
91 pixelBuffer.mWidth = 0;
92 pixelBuffer.mHeight = 0;
93 pixelBuffer.mBufferSize = 0;
98 unsigned int PixelBuffer::GetWidth() const
103 unsigned int PixelBuffer::GetHeight() const
108 Dali::Pixel::Format PixelBuffer::GetPixelFormat() const
113 unsigned char* PixelBuffer::GetBuffer() const
118 unsigned int PixelBuffer::GetBufferSize() const
123 Dali::PixelData PixelBuffer::CreatePixelData() const
125 unsigned char* destBuffer = NULL;
127 if( mBufferSize > 0 )
129 destBuffer = static_cast<unsigned char*>( malloc( mBufferSize ) );
130 memcpy( destBuffer, mBuffer, mBufferSize );
133 Dali::PixelData pixelData = Dali::PixelData::New( destBuffer, mBufferSize,
136 Dali::PixelData::FREE );
140 void PixelBuffer::ApplyMask( const PixelBuffer& inMask, float contentScale, bool cropToMask )
144 // First scale this buffer by the contentScale, and crop to the mask size
145 // If it's too small, then scale the mask to match the image size
146 // Then apply the mask
147 ScaleAndCrop( contentScale, ImageDimensions( inMask.GetWidth(), inMask.GetHeight() ) );
149 if( inMask.mWidth > mWidth || inMask.mHeight > mHeight )
151 PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
152 ApplyMaskInternal( *mask );
156 ApplyMaskInternal( inMask );
161 // First, scale the mask to match the image size,
162 // then apply the mask.
163 PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
164 ApplyMaskInternal( *mask );
168 void PixelBuffer::ApplyMaskInternal( const PixelBuffer& mask )
173 Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
174 if( Dali::Pixel::HasAlpha( mPixelFormat ) && bitMask == 255 )
176 ApplyMaskToAlphaChannel( *this, mask );
180 PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer( *this, mask );
181 TakeOwnershipOfBuffer( *newPixelBuffer );
182 // On leaving scope, newPixelBuffer will get destroyed.
186 void PixelBuffer::TakeOwnershipOfBuffer( PixelBuffer& pixelBuffer )
190 // Take ownership of new buffer
191 mBuffer = pixelBuffer.mBuffer;
192 pixelBuffer.mBuffer = NULL;
193 mBufferSize = pixelBuffer.mBufferSize;
194 mWidth = pixelBuffer.mWidth;
195 mHeight = pixelBuffer.mHeight;
196 mPixelFormat = pixelBuffer.mPixelFormat;
199 void PixelBuffer::ReleaseBuffer()
207 void PixelBuffer::ScaleAndCrop( float scaleFactor, ImageDimensions cropDimensions )
209 ImageDimensions outDimensions( float(mWidth) * scaleFactor,
210 float(mHeight) * scaleFactor );
212 if( outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight )
214 Resize( outDimensions );
217 ImageDimensions postCropDimensions(
218 std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
219 std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
221 if( postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
222 postCropDimensions.GetHeight() < outDimensions.GetHeight() )
224 uint16_t x = ( outDimensions.GetWidth() - postCropDimensions.GetWidth() ) / 2;
225 uint16_t y = ( outDimensions.GetHeight() - postCropDimensions.GetHeight() ) / 2;
226 Crop( x, y, postCropDimensions );
230 void PixelBuffer::Crop( uint16_t x, uint16_t y, ImageDimensions cropDimensions )
232 PixelBufferPtr outBuffer = NewCrop( *this, x, y, cropDimensions );
233 TakeOwnershipOfBuffer( *outBuffer );
236 PixelBufferPtr PixelBuffer::NewCrop( const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions )
238 PixelBufferPtr outBuffer = PixelBuffer::New( cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat() );
239 int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
240 int srcStride = inBuffer.mWidth * bytesPerPixel;
241 int destStride = cropDimensions.GetWidth() * bytesPerPixel;
243 // Clamp crop to right edge
244 if( x + cropDimensions.GetWidth() > inBuffer.mWidth )
246 destStride = ( inBuffer.mWidth - x ) * bytesPerPixel;
249 int srcOffset = x * bytesPerPixel + y * srcStride;
251 unsigned char* destBuffer = outBuffer->mBuffer;
253 // Clamp crop to last row
254 unsigned int endRow = y + cropDimensions.GetHeight();
255 if( endRow > inBuffer.mHeight )
257 endRow = inBuffer.mHeight - 1 ;
259 for( uint16_t row = y; row < endRow; ++row )
261 memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride );
262 srcOffset += srcStride;
263 destOffset += destStride;
269 void PixelBuffer::SetMetadata( const Property::Map& map )
271 mMetadata.reset(new Property::Map(map));
274 bool PixelBuffer::GetMetadata(Property::Map& outMetadata) const
280 outMetadata = *mMetadata;
284 void PixelBuffer::SetMetadata(std::unique_ptr<Property::Map> metadata)
286 mMetadata = std::move(metadata);
289 void PixelBuffer::Resize( ImageDimensions outDimensions )
291 if( mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight() )
293 PixelBufferPtr outBuffer = NewResize( *this, outDimensions );
294 TakeOwnershipOfBuffer( *outBuffer );
298 PixelBufferPtr PixelBuffer::NewResize( const PixelBuffer& inBuffer, ImageDimensions outDimensions )
300 PixelBufferPtr outBuffer = PixelBuffer::New( outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat() );
301 ImageDimensions inDimensions( inBuffer.mWidth, inBuffer.mHeight );
303 bool hasAlpha = Pixel::HasAlpha( inBuffer.mPixelFormat );
304 int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
306 Resampler::Filter filterType = Resampler::LANCZOS4;
307 if( inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight() )
309 filterType = Resampler::MITCHELL;
312 // This method only really works for 8 bit wide channels.
313 // (But could be expanded to work)
314 if( inBuffer.mPixelFormat == Pixel::A8 ||
315 inBuffer.mPixelFormat == Pixel::L8 ||
316 inBuffer.mPixelFormat == Pixel::LA88 ||
317 inBuffer.mPixelFormat == Pixel::RGB888 ||
318 inBuffer.mPixelFormat == Pixel::RGB8888 ||
319 inBuffer.mPixelFormat == Pixel::BGR8888 ||
320 inBuffer.mPixelFormat == Pixel::RGBA8888 ||
321 inBuffer.mPixelFormat == Pixel::BGRA8888 )
323 Dali::Internal::Platform::Resample( inBuffer.mBuffer, inDimensions,
324 outBuffer->GetBuffer(), outDimensions,
325 filterType, bytesPerPixel, hasAlpha );
329 DALI_LOG_ERROR( "Trying to resize an image with too narrow a channel width" );
335 void PixelBuffer::ApplyGaussianBlur( const float blurRadius )
337 // This method only works for pixel buffer in RGBA format.
338 if( mWidth > 0 && mHeight > 0 && mPixelFormat == Pixel::RGBA8888 )
340 if ( blurRadius > Math::MACHINE_EPSILON_1 )
342 PerformGaussianBlurRGBA( *this, blurRadius );
347 DALI_LOG_ERROR( "Trying to apply gaussian blur to an empty pixel buffer or a pixel buffer not in RGBA format" );
351 void PixelBuffer::MultiplyColorByAlpha()
353 auto bytesPerPixel = Pixel::GetBytesPerPixel( mPixelFormat );
355 if( Pixel::HasAlpha(mPixelFormat) )
357 unsigned char* pixel = mBuffer;
358 const unsigned int bufferSize = mWidth * mHeight;
360 for( unsigned int i=0; i<bufferSize; ++i )
362 unsigned int alpha = ReadChannel( pixel, mPixelFormat, Adaptor::ALPHA );
364 auto red = ReadChannel( pixel, mPixelFormat, Adaptor::RED);
365 auto green = ReadChannel( pixel, mPixelFormat, Adaptor::GREEN);
366 auto blue = ReadChannel( pixel, mPixelFormat, Adaptor::BLUE);
367 auto luminance = ReadChannel( pixel, mPixelFormat, Adaptor::LUMINANCE);
368 WriteChannel( pixel, mPixelFormat, Adaptor::RED, red*alpha / 255 );
369 WriteChannel( pixel, mPixelFormat, Adaptor::GREEN, green*alpha/255 );
370 WriteChannel( pixel, mPixelFormat, Adaptor::BLUE, blue*alpha/255 );
371 WriteChannel( pixel, mPixelFormat, Adaptor::LUMINANCE, luminance*alpha/255 );
373 pixel += bytesPerPixel;
381 }// namespace Adaptor
382 }// namespace Internal