[4.0] PixelBuffer::Rotate() amendments.
[platform/core/uifw/dali-adaptor.git] / adaptors / 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 "pixel-buffer-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <stdlib.h>
23 #include <cstring>
24
25 // INTERNAL INCLUDES
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>
31
32 namespace Dali
33 {
34
35 namespace Internal
36 {
37
38 namespace Adaptor
39 {
40
41 namespace
42 {
43 const float TWO_PI = 2.f * Math::PI; ///< 360 degrees in radians
44 } // namespace
45
46 PixelBuffer::PixelBuffer( unsigned char* buffer,
47                           unsigned int bufferSize,
48                           unsigned int width,
49                           unsigned int height,
50                           Dali::Pixel::Format pixelFormat )
51 : mMetadata(),
52   mBuffer( buffer ),
53   mBufferSize( bufferSize ),
54   mWidth( width ),
55   mHeight( height ),
56   mPixelFormat( pixelFormat )
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 unsigned int PixelBuffer::GetBufferSize() const
124 {
125   return mBufferSize;
126 }
127
128 Dali::PixelData PixelBuffer::CreatePixelData() const
129 {
130   unsigned char* destBuffer = NULL;
131
132   if( mBufferSize > 0 )
133   {
134     destBuffer = static_cast<unsigned char*>( malloc( mBufferSize ) );
135     memcpy( destBuffer, mBuffer, mBufferSize );
136   }
137
138   Dali::PixelData pixelData = Dali::PixelData::New( destBuffer, mBufferSize,
139                                                     mWidth, mHeight,
140                                                     mPixelFormat,
141                                                     Dali::PixelData::FREE );
142   return pixelData;
143 }
144
145 void PixelBuffer::ApplyMask( const PixelBuffer& inMask, float contentScale, bool cropToMask )
146 {
147   if( cropToMask )
148   {
149     // First scale this buffer by the contentScale, and crop to the mask size
150     // If it's too small, then scale the mask to match the image size
151     // Then apply the mask
152     ScaleAndCrop( contentScale, ImageDimensions( inMask.GetWidth(), inMask.GetHeight() ) );
153
154     if( inMask.mWidth > mWidth || inMask.mHeight > mHeight )
155     {
156       PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
157       ApplyMaskInternal( *mask );
158     }
159     else
160     {
161       ApplyMaskInternal( inMask );
162     }
163   }
164   else
165   {
166     // First, scale the mask to match the image size,
167     // then apply the mask.
168     PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
169     ApplyMaskInternal( *mask );
170   }
171 }
172
173 void PixelBuffer::ApplyMaskInternal( const PixelBuffer& mask )
174 {
175   int byteOffset=0;
176   int bitMask=0;
177
178   Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
179   if( Dali::Pixel::HasAlpha( mPixelFormat ) && bitMask == 255 )
180   {
181     ApplyMaskToAlphaChannel( *this, mask );
182   }
183   else
184   {
185     PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer( *this, mask );
186     TakeOwnershipOfBuffer( *newPixelBuffer );
187     // On leaving scope, newPixelBuffer will get destroyed.
188   }
189 }
190
191 void PixelBuffer::TakeOwnershipOfBuffer( PixelBuffer& pixelBuffer )
192 {
193   ReleaseBuffer();
194
195   // Take ownership of new buffer
196   mBuffer = pixelBuffer.mBuffer;
197   pixelBuffer.mBuffer = NULL;
198   mBufferSize = pixelBuffer.mBufferSize;
199   mWidth = pixelBuffer.mWidth;
200   mHeight = pixelBuffer.mHeight;
201   mPixelFormat = pixelBuffer.mPixelFormat;
202 }
203
204 void PixelBuffer::ReleaseBuffer()
205 {
206   if( mBuffer )
207   {
208     free( mBuffer );
209   }
210 }
211
212 void PixelBuffer::AllocateFixedSize( uint32_t size )
213 {
214   ReleaseBuffer();
215   mBuffer = reinterpret_cast<unsigned char*>(malloc( size ));
216   mBufferSize = size;
217 }
218
219 bool PixelBuffer::Rotate( Degree angle )
220 {
221   // Check first if Rotate() can perform the operation in the current pixel buffer.
222
223   bool validPixelFormat = false;
224   switch( mPixelFormat )
225   {
226     case Pixel::A8:
227     case Pixel::L8:
228     case Pixel::LA88:
229     case Pixel::RGB888:
230     case Pixel::RGB8888:
231     case Pixel::BGR8888:
232     case Pixel::RGBA8888:
233     case Pixel::BGRA8888: // FALL THROUGH
234     {
235       validPixelFormat = true;
236       break;
237     }
238     default:
239     {
240       // This pixel format is not supported for this operation.
241       validPixelFormat = false;
242       break;
243     }
244   }
245
246   if( !validPixelFormat )
247   {
248     // Can't rotate the pixel buffer with the current pixel format.
249     DALI_LOG_ERROR( "Can't rotate the pixel buffer with the current pixel format\n" );
250     return false;
251   }
252
253   float radians = Radian( angle ).radian;
254
255   // Transform the input angle into the range [0..2PI]
256   radians = fmod( radians, TWO_PI );
257   radians += ( radians < 0.f ) ? TWO_PI : 0.f;
258
259   if( radians < Dali::Math::MACHINE_EPSILON_10 )
260   {
261     // Nothing to do if the angle is zero.
262     return true;
263   }
264
265   const unsigned int pixelSize = Pixel::GetBytesPerPixel( mPixelFormat );
266
267   uint8_t* pixelsOut = nullptr;
268   Platform::RotateByShear( mBuffer,
269                            mWidth,
270                            mHeight,
271                            pixelSize,
272                            radians,
273                            pixelsOut,
274                            mWidth,
275                            mHeight );
276
277   // Check whether the rotation succedded and set the new pixel buffer data.
278   const bool success = nullptr != pixelsOut;
279
280   if( success )
281   {
282     // Release the memory of the current pixel buffer.
283     ReleaseBuffer();
284
285     // Set the new pixel buffer.
286     mBuffer = pixelsOut;
287     pixelsOut = nullptr;
288     mBufferSize = mWidth * mHeight * pixelSize;
289   }
290
291   return success;
292 }
293
294 void PixelBuffer::ScaleAndCrop( float scaleFactor, ImageDimensions cropDimensions )
295 {
296   ImageDimensions outDimensions( float(mWidth) * scaleFactor,
297                                  float(mHeight) * scaleFactor );
298
299   if( outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight )
300   {
301     Resize( outDimensions );
302   }
303
304   ImageDimensions postCropDimensions(
305     std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
306     std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
307
308   if( postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
309       postCropDimensions.GetHeight() < outDimensions.GetHeight() )
310   {
311     uint16_t x = ( outDimensions.GetWidth()  - postCropDimensions.GetWidth() ) / 2;
312     uint16_t y = ( outDimensions.GetHeight() - postCropDimensions.GetHeight() ) / 2;
313     Crop( x, y, postCropDimensions );
314   }
315 }
316
317 void PixelBuffer::Crop( uint16_t x, uint16_t y, ImageDimensions cropDimensions )
318 {
319   PixelBufferPtr outBuffer = NewCrop( *this, x, y, cropDimensions );
320   TakeOwnershipOfBuffer( *outBuffer );
321 }
322
323 PixelBufferPtr PixelBuffer::NewCrop( const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions )
324 {
325   PixelBufferPtr outBuffer = PixelBuffer::New( cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat() );
326   int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
327   int srcStride = inBuffer.mWidth * bytesPerPixel;
328   int destStride = cropDimensions.GetWidth() * bytesPerPixel;
329
330   // Clamp crop to right edge
331   if( x + cropDimensions.GetWidth() > inBuffer.mWidth )
332   {
333     destStride = ( inBuffer.mWidth - x ) * bytesPerPixel;
334   }
335
336   int srcOffset = x * bytesPerPixel + y * srcStride;
337   int destOffset = 0;
338   unsigned char* destBuffer = outBuffer->mBuffer;
339
340   // Clamp crop to last row
341   unsigned int endRow = y + cropDimensions.GetHeight();
342   if( endRow > inBuffer.mHeight )
343   {
344     endRow = inBuffer.mHeight - 1 ;
345   }
346   for( uint16_t row = y; row < endRow; ++row )
347   {
348     memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride );
349     srcOffset += srcStride;
350     destOffset += destStride;
351   }
352   return outBuffer;
353
354 }
355
356 void PixelBuffer::SetMetadata( const Property::Map& map )
357 {
358   mMetadata.reset(new Property::Map(map));
359 }
360
361 bool PixelBuffer::GetMetadata(Property::Map& outMetadata) const
362 {
363   if( !mMetadata )
364   {
365     return false;
366   }
367   outMetadata = *mMetadata;
368   return true;
369 }
370
371 void PixelBuffer::SetMetadata(std::unique_ptr<Property::Map> metadata)
372 {
373   mMetadata = std::move(metadata);
374 }
375
376 void PixelBuffer::Resize( ImageDimensions outDimensions )
377 {
378   if( mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight() )
379   {
380     PixelBufferPtr outBuffer = NewResize( *this, outDimensions );
381     TakeOwnershipOfBuffer( *outBuffer );
382   }
383 }
384
385 PixelBufferPtr PixelBuffer::NewResize( const PixelBuffer& inBuffer, ImageDimensions outDimensions )
386 {
387   PixelBufferPtr outBuffer = PixelBuffer::New( outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat() );
388   ImageDimensions inDimensions( inBuffer.mWidth, inBuffer.mHeight );
389
390   bool hasAlpha = Pixel::HasAlpha( inBuffer.mPixelFormat );
391   int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
392
393   Resampler::Filter filterType = Resampler::LANCZOS4;
394   if( inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight() )
395   {
396     filterType = Resampler::MITCHELL;
397   }
398
399   // This method only really works for 8 bit wide channels.
400   // (But could be expanded to work)
401   if( inBuffer.mPixelFormat == Pixel::A8 ||
402       inBuffer.mPixelFormat == Pixel::L8 ||
403       inBuffer.mPixelFormat == Pixel::LA88 ||
404       inBuffer.mPixelFormat == Pixel::RGB888 ||
405       inBuffer.mPixelFormat == Pixel::RGB8888 ||
406       inBuffer.mPixelFormat == Pixel::BGR8888 ||
407       inBuffer.mPixelFormat == Pixel::RGBA8888 ||
408       inBuffer.mPixelFormat == Pixel::BGRA8888 )
409   {
410     Dali::Internal::Platform::Resample( inBuffer.mBuffer, inDimensions,
411                                         outBuffer->GetBuffer(), outDimensions,
412                                         filterType, bytesPerPixel, hasAlpha );
413   }
414   else
415   {
416     DALI_LOG_ERROR( "Trying to resize an image with too narrow a channel width" );
417   }
418
419   return outBuffer;
420 }
421
422 void PixelBuffer::ApplyGaussianBlur( const float blurRadius )
423 {
424   // This method only works for pixel buffer in RGBA format.
425   if( mWidth > 0 && mHeight > 0 && mPixelFormat == Pixel::RGBA8888 )
426   {
427     if ( blurRadius > Math::MACHINE_EPSILON_1 )
428     {
429       PerformGaussianBlurRGBA( *this, blurRadius );
430     }
431   }
432   else
433   {
434     DALI_LOG_ERROR( "Trying to apply gaussian blur to an empty pixel buffer or a pixel buffer not in RGBA format" );
435   }
436 }
437
438 void PixelBuffer::MultiplyColorByAlpha()
439 {
440   auto bytesPerPixel = Pixel::GetBytesPerPixel( mPixelFormat );
441
442   // Compressed textures have unknown size of the pixel. Alpha premultiplication
443   // must be skipped in such case
444   if( Pixel::GetBytesPerPixel(mPixelFormat) && Pixel::HasAlpha(mPixelFormat) )
445   {
446     unsigned char* pixel = mBuffer;
447     const unsigned int bufferSize = mWidth * mHeight;
448
449     for( unsigned int i=0; i<bufferSize; ++i )
450     {
451       unsigned int alpha = ReadChannel( pixel, mPixelFormat, Adaptor::ALPHA );
452       {
453         auto red       = ReadChannel( pixel, mPixelFormat, Adaptor::RED);
454         auto green     = ReadChannel( pixel, mPixelFormat, Adaptor::GREEN);
455         auto blue      = ReadChannel( pixel, mPixelFormat, Adaptor::BLUE);
456         auto luminance = ReadChannel( pixel, mPixelFormat, Adaptor::LUMINANCE);
457         WriteChannel( pixel, mPixelFormat, Adaptor::RED, red*alpha / 255 );
458         WriteChannel( pixel, mPixelFormat, Adaptor::GREEN, green*alpha/255 );
459         WriteChannel( pixel, mPixelFormat, Adaptor::BLUE, blue*alpha/255 );
460         WriteChannel( pixel, mPixelFormat, Adaptor::LUMINANCE, luminance*alpha/255 );
461       }
462       pixel += bytesPerPixel;
463     }
464   }
465 }
466
467 }// namespace Adaptor
468 }// namespace Internal
469 }// namespace Dali