Initial copy from dali-adaptor repository
[platform/core/uifw/dali-adaptor-legacy.git] / dali / internal / imaging / common / pixel-buffer-impl.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/imaging/common/pixel-buffer-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <stdlib.h>
23 #include <cstring>
24
25 // INTERNAL INCLUDES
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>
30
31 namespace Dali
32 {
33
34 namespace Internal
35 {
36
37 namespace Adaptor
38 {
39
40 namespace
41 {
42 const float TWO_PI = 2.f * Math::PI; ///< 360 degrees in radians
43 } // namespace
44
45 PixelBuffer::PixelBuffer( unsigned char* buffer,
46                           unsigned int bufferSize,
47                           unsigned int width,
48                           unsigned int height,
49                           Dali::Pixel::Format pixelFormat )
50 : mMetadata(),
51   mBuffer( buffer ),
52   mBufferSize( bufferSize ),
53   mWidth( width ),
54   mHeight( height ),
55   mPixelFormat( pixelFormat ),
56   mPreMultiplied( false )
57 {
58 }
59
60 PixelBuffer::~PixelBuffer()
61 {
62   ReleaseBuffer();
63 }
64
65 PixelBufferPtr PixelBuffer::New( unsigned int width,
66                                  unsigned int height,
67                                  Dali::Pixel::Format pixelFormat )
68 {
69   unsigned int bufferSize = width * height * Dali::Pixel::GetBytesPerPixel( pixelFormat );
70   unsigned char* buffer = NULL;
71   if( bufferSize > 0 )
72   {
73     buffer = static_cast<unsigned char*>( malloc ( bufferSize ) );
74   }
75   return new PixelBuffer( buffer, bufferSize, width, height, pixelFormat );
76 }
77
78 PixelBufferPtr PixelBuffer::New( unsigned char* buffer,
79                                  unsigned int bufferSize,
80                                  unsigned int width,
81                                  unsigned int height,
82                                  Dali::Pixel::Format pixelFormat )
83 {
84   return new PixelBuffer( buffer, bufferSize, width, height, pixelFormat );
85 }
86
87 Dali::PixelData PixelBuffer::Convert( PixelBuffer& pixelBuffer )
88 {
89   Dali::PixelData pixelData = Dali::PixelData::New( pixelBuffer.mBuffer,
90                                                     pixelBuffer.mBufferSize,
91                                                     pixelBuffer.mWidth,
92                                                     pixelBuffer.mHeight,
93                                                     pixelBuffer.mPixelFormat,
94                                                     Dali::PixelData::FREE );
95   pixelBuffer.mBuffer = NULL;
96   pixelBuffer.mWidth = 0;
97   pixelBuffer.mHeight = 0;
98   pixelBuffer.mBufferSize = 0;
99
100   return pixelData;
101 }
102
103 unsigned int PixelBuffer::GetWidth() const
104 {
105   return mWidth;
106 }
107
108 unsigned int PixelBuffer::GetHeight() const
109 {
110   return mHeight;
111 }
112
113 Dali::Pixel::Format PixelBuffer::GetPixelFormat() const
114 {
115   return mPixelFormat;
116 }
117
118 unsigned char* PixelBuffer::GetBuffer() const
119 {
120   return mBuffer;
121 }
122
123 const unsigned char* const PixelBuffer::GetConstBuffer() const
124 {
125   return mBuffer;
126 }
127
128 unsigned int PixelBuffer::GetBufferSize() const
129 {
130   return mBufferSize;
131 }
132
133 Dali::PixelData PixelBuffer::CreatePixelData() const
134 {
135   unsigned char* destBuffer = NULL;
136
137   if( mBufferSize > 0 )
138   {
139     destBuffer = static_cast<unsigned char*>( malloc( mBufferSize ) );
140     memcpy( destBuffer, mBuffer, mBufferSize );
141   }
142
143   Dali::PixelData pixelData = Dali::PixelData::New( destBuffer, mBufferSize,
144                                                     mWidth, mHeight,
145                                                     mPixelFormat,
146                                                     Dali::PixelData::FREE );
147   return pixelData;
148 }
149
150 void PixelBuffer::ApplyMask( const PixelBuffer& inMask, float contentScale, bool cropToMask )
151 {
152   if( cropToMask )
153   {
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() ) );
158
159     if( inMask.mWidth > mWidth || inMask.mHeight > mHeight )
160     {
161       PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
162       ApplyMaskInternal( *mask );
163     }
164     else
165     {
166       ApplyMaskInternal( inMask );
167     }
168   }
169   else
170   {
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 );
175   }
176 }
177
178 void PixelBuffer::ApplyMaskInternal( const PixelBuffer& mask )
179 {
180   int byteOffset=0;
181   int bitMask=0;
182
183   Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
184   if( Dali::Pixel::HasAlpha( mPixelFormat ) && bitMask == 255 )
185   {
186     ApplyMaskToAlphaChannel( *this, mask );
187   }
188   else
189   {
190     PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer( *this, mask );
191     TakeOwnershipOfBuffer( *newPixelBuffer );
192     // On leaving scope, newPixelBuffer will get destroyed.
193   }
194 }
195
196 void PixelBuffer::TakeOwnershipOfBuffer( PixelBuffer& pixelBuffer )
197 {
198   ReleaseBuffer();
199
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;
207 }
208
209 void PixelBuffer::ReleaseBuffer()
210 {
211   if( mBuffer )
212   {
213     free( mBuffer );
214   }
215 }
216
217 void PixelBuffer::AllocateFixedSize( uint32_t size )
218 {
219   ReleaseBuffer();
220   mBuffer = reinterpret_cast<unsigned char*>(malloc( size ));
221   mBufferSize = size;
222 }
223
224 bool PixelBuffer::Rotate( Degree angle )
225 {
226   // Check first if Rotate() can perform the operation in the current pixel buffer.
227
228   bool validPixelFormat = false;
229   switch( mPixelFormat )
230   {
231     case Pixel::A8:
232     case Pixel::L8:
233     case Pixel::LA88:
234     case Pixel::RGB888:
235     case Pixel::RGB8888:
236     case Pixel::BGR8888:
237     case Pixel::RGBA8888:
238     case Pixel::BGRA8888: // FALL THROUGH
239     {
240       validPixelFormat = true;
241       break;
242     }
243     default:
244     {
245       // This pixel format is not supported for this operation.
246       validPixelFormat = false;
247       break;
248     }
249   }
250
251   if( !validPixelFormat )
252   {
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" );
255     return false;
256   }
257
258   float radians = Radian( angle ).radian;
259
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;
263
264   if( radians < Dali::Math::MACHINE_EPSILON_10 )
265   {
266     // Nothing to do if the angle is zero.
267     return true;
268   }
269
270   const unsigned int pixelSize = Pixel::GetBytesPerPixel( mPixelFormat );
271
272   uint8_t* pixelsOut = nullptr;
273   Platform::RotateByShear( mBuffer,
274                            mWidth,
275                            mHeight,
276                            pixelSize,
277                            radians,
278                            pixelsOut,
279                            mWidth,
280                            mHeight );
281
282   // Check whether the rotation succedded and set the new pixel buffer data.
283   const bool success = nullptr != pixelsOut;
284
285   if( success )
286   {
287     // Release the memory of the current pixel buffer.
288     ReleaseBuffer();
289
290     // Set the new pixel buffer.
291     mBuffer = pixelsOut;
292     pixelsOut = nullptr;
293     mBufferSize = mWidth * mHeight * pixelSize;
294   }
295
296   return success;
297 }
298
299 void PixelBuffer::ScaleAndCrop( float scaleFactor, ImageDimensions cropDimensions )
300 {
301   ImageDimensions outDimensions( float(mWidth) * scaleFactor,
302                                  float(mHeight) * scaleFactor );
303
304   if( outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight )
305   {
306     Resize( outDimensions );
307   }
308
309   ImageDimensions postCropDimensions(
310     std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
311     std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
312
313   if( postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
314       postCropDimensions.GetHeight() < outDimensions.GetHeight() )
315   {
316     uint16_t x = ( outDimensions.GetWidth()  - postCropDimensions.GetWidth() ) / 2;
317     uint16_t y = ( outDimensions.GetHeight() - postCropDimensions.GetHeight() ) / 2;
318     Crop( x, y, postCropDimensions );
319   }
320 }
321
322 void PixelBuffer::Crop( uint16_t x, uint16_t y, ImageDimensions cropDimensions )
323 {
324   PixelBufferPtr outBuffer = NewCrop( *this, x, y, cropDimensions );
325   TakeOwnershipOfBuffer( *outBuffer );
326 }
327
328 PixelBufferPtr PixelBuffer::NewCrop( const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions )
329 {
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;
334
335   // Clamp crop to right edge
336   if( x + cropDimensions.GetWidth() > inBuffer.mWidth )
337   {
338     destStride = ( inBuffer.mWidth - x ) * bytesPerPixel;
339   }
340
341   int srcOffset = x * bytesPerPixel + y * srcStride;
342   int destOffset = 0;
343   unsigned char* destBuffer = outBuffer->mBuffer;
344
345   // Clamp crop to last row
346   unsigned int endRow = y + cropDimensions.GetHeight();
347   if( endRow > inBuffer.mHeight )
348   {
349     endRow = inBuffer.mHeight - 1 ;
350   }
351   for( uint16_t row = y; row < endRow; ++row )
352   {
353     memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride );
354     srcOffset += srcStride;
355     destOffset += destStride;
356   }
357   return outBuffer;
358
359 }
360
361 void PixelBuffer::SetMetadata( const Property::Map& map )
362 {
363   mMetadata.reset(new Property::Map(map));
364 }
365
366 bool PixelBuffer::GetMetadata(Property::Map& outMetadata) const
367 {
368   if( !mMetadata )
369   {
370     return false;
371   }
372   outMetadata = *mMetadata;
373   return true;
374 }
375
376 void PixelBuffer::SetMetadata(std::unique_ptr<Property::Map> metadata)
377 {
378   mMetadata = std::move(metadata);
379 }
380
381 void PixelBuffer::Resize( ImageDimensions outDimensions )
382 {
383   if( mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight() )
384   {
385     PixelBufferPtr outBuffer = NewResize( *this, outDimensions );
386     TakeOwnershipOfBuffer( *outBuffer );
387   }
388 }
389
390 PixelBufferPtr PixelBuffer::NewResize( const PixelBuffer& inBuffer, ImageDimensions outDimensions )
391 {
392   PixelBufferPtr outBuffer = PixelBuffer::New( outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat() );
393   ImageDimensions inDimensions( inBuffer.mWidth, inBuffer.mHeight );
394
395   bool hasAlpha = Pixel::HasAlpha( inBuffer.mPixelFormat );
396   int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
397
398   Resampler::Filter filterType = Resampler::LANCZOS4;
399   if( inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight() )
400   {
401     filterType = Resampler::MITCHELL;
402   }
403
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 )
414   {
415     Dali::Internal::Platform::Resample( inBuffer.mBuffer, inDimensions,
416                                         outBuffer->GetBuffer(), outDimensions,
417                                         filterType, bytesPerPixel, hasAlpha );
418   }
419   else
420   {
421     DALI_LOG_ERROR( "Trying to resize an image with too narrow a channel width" );
422   }
423
424   return outBuffer;
425 }
426
427 void PixelBuffer::ApplyGaussianBlur( const float blurRadius )
428 {
429   // This method only works for pixel buffer in RGBA format.
430   if( mWidth > 0 && mHeight > 0 && mPixelFormat == Pixel::RGBA8888 )
431   {
432     if ( blurRadius > Math::MACHINE_EPSILON_1 )
433     {
434       PerformGaussianBlurRGBA( *this, blurRadius );
435     }
436   }
437   else
438   {
439     DALI_LOG_ERROR( "Trying to apply gaussian blur to an empty pixel buffer or a pixel buffer not in RGBA format" );
440   }
441 }
442
443 void PixelBuffer::MultiplyColorByAlpha()
444 {
445   auto bytesPerPixel = Pixel::GetBytesPerPixel( mPixelFormat );
446
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) )
450   {
451     unsigned char* pixel = mBuffer;
452     const unsigned int bufferSize = mWidth * mHeight;
453
454     for( unsigned int i=0; i<bufferSize; ++i )
455     {
456       unsigned int alpha = ReadChannel( pixel, mPixelFormat, Adaptor::ALPHA );
457       {
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 );
466       }
467       pixel += bytesPerPixel;
468     }
469   }
470   mPreMultiplied = true;
471 }
472
473 bool PixelBuffer::IsAlphaPreMultiplied() const
474 {
475   return mPreMultiplied;
476 }
477
478 }// namespace Adaptor
479 }// namespace Internal
480 }// namespace Dali