[4.0] Exposing Exif Image metadata
[platform/core/uifw/dali-adaptor.git] / adaptors / common / pixel-buffer-impl.cpp
1 /*
2  * Copyright (c) 2017 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
31 namespace Dali
32 {
33
34 namespace Internal
35 {
36
37 namespace Adaptor
38 {
39
40 PixelBuffer::PixelBuffer( unsigned char* buffer,
41                           unsigned int bufferSize,
42                           unsigned int width,
43                           unsigned int height,
44                           Dali::Pixel::Format pixelFormat )
45 : mMetadata(),
46   mBuffer( buffer ),
47   mBufferSize( bufferSize ),
48   mWidth( width ),
49   mHeight( height ),
50   mPixelFormat( pixelFormat )
51 {
52 }
53
54 PixelBuffer::~PixelBuffer()
55 {
56   ReleaseBuffer();
57 }
58
59 PixelBufferPtr PixelBuffer::New( unsigned int width,
60                                  unsigned int height,
61                                  Dali::Pixel::Format pixelFormat )
62 {
63   unsigned int bufferSize = width * height * Dali::Pixel::GetBytesPerPixel( pixelFormat );
64   unsigned char* buffer = NULL;
65   if( bufferSize > 0 )
66   {
67     buffer = static_cast<unsigned char*>( malloc ( bufferSize ) );
68   }
69   return new PixelBuffer( buffer, bufferSize, width, height, pixelFormat );
70 }
71
72 PixelBufferPtr PixelBuffer::New( unsigned char* buffer,
73                                  unsigned int bufferSize,
74                                  unsigned int width,
75                                  unsigned int height,
76                                  Dali::Pixel::Format pixelFormat )
77 {
78   return new PixelBuffer( buffer, bufferSize, width, height, pixelFormat );
79 }
80
81 Dali::PixelData PixelBuffer::Convert( PixelBuffer& pixelBuffer )
82 {
83   Dali::PixelData pixelData = Dali::PixelData::New( pixelBuffer.mBuffer,
84                                                     pixelBuffer.mBufferSize,
85                                                     pixelBuffer.mWidth,
86                                                     pixelBuffer.mHeight,
87                                                     pixelBuffer.mPixelFormat,
88                                                     Dali::PixelData::FREE );
89   pixelBuffer.mBuffer = NULL;
90   pixelBuffer.mWidth = 0;
91   pixelBuffer.mHeight = 0;
92   pixelBuffer.mBufferSize = 0;
93
94   return pixelData;
95 }
96
97 unsigned int PixelBuffer::GetWidth() const
98 {
99   return mWidth;
100 }
101
102 unsigned int PixelBuffer::GetHeight() const
103 {
104   return mHeight;
105 }
106
107 Dali::Pixel::Format PixelBuffer::GetPixelFormat() const
108 {
109   return mPixelFormat;
110 }
111
112 unsigned char* PixelBuffer::GetBuffer() const
113 {
114   return mBuffer;
115 }
116
117 unsigned int PixelBuffer::GetBufferSize() const
118 {
119   return mBufferSize;
120 }
121
122 Dali::PixelData PixelBuffer::CreatePixelData() const
123 {
124   unsigned char* destBuffer = NULL;
125
126   if( mBufferSize > 0 )
127   {
128     destBuffer = static_cast<unsigned char*>( malloc( mBufferSize ) );
129     memcpy( destBuffer, mBuffer, mBufferSize );
130   }
131
132   Dali::PixelData pixelData = Dali::PixelData::New( destBuffer, mBufferSize,
133                                                     mWidth, mHeight,
134                                                     mPixelFormat,
135                                                     Dali::PixelData::FREE );
136   return pixelData;
137 }
138
139 void PixelBuffer::ApplyMask( const PixelBuffer& inMask, float contentScale, bool cropToMask )
140 {
141   if( cropToMask )
142   {
143     // First scale this buffer by the contentScale, and crop to the mask size
144     // If it's too small, then scale the mask to match the image size
145     // Then apply the mask
146     ScaleAndCrop( contentScale, ImageDimensions( inMask.GetWidth(), inMask.GetHeight() ) );
147
148     if( inMask.mWidth > mWidth || inMask.mHeight > mHeight )
149     {
150       PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
151       ApplyMaskInternal( *mask );
152     }
153     else
154     {
155       ApplyMaskInternal( inMask );
156     }
157   }
158   else
159   {
160     // First, scale the mask to match the image size,
161     // then apply the mask.
162     PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
163     ApplyMaskInternal( *mask );
164   }
165 }
166
167 void PixelBuffer::ApplyMaskInternal( const PixelBuffer& mask )
168 {
169   int byteOffset=0;
170   int bitMask=0;
171
172   Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
173   if( Dali::Pixel::HasAlpha( mPixelFormat ) && bitMask == 255 )
174   {
175     ApplyMaskToAlphaChannel( *this, mask );
176   }
177   else
178   {
179     PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer( *this, mask );
180     TakeOwnershipOfBuffer( *newPixelBuffer );
181     // On leaving scope, newPixelBuffer will get destroyed.
182   }
183 }
184
185 void PixelBuffer::TakeOwnershipOfBuffer( PixelBuffer& pixelBuffer )
186 {
187   ReleaseBuffer();
188
189   // Take ownership of new buffer
190   mBuffer = pixelBuffer.mBuffer;
191   pixelBuffer.mBuffer = NULL;
192   mBufferSize = pixelBuffer.mBufferSize;
193   mWidth = pixelBuffer.mWidth;
194   mHeight = pixelBuffer.mHeight;
195   mPixelFormat = pixelBuffer.mPixelFormat;
196 }
197
198 void PixelBuffer::ReleaseBuffer()
199 {
200   if( mBuffer )
201   {
202     free( mBuffer );
203   }
204 }
205
206 void PixelBuffer::ScaleAndCrop( float scaleFactor, ImageDimensions cropDimensions )
207 {
208   ImageDimensions outDimensions( float(mWidth) * scaleFactor,
209                                  float(mHeight) * scaleFactor );
210
211   if( outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight )
212   {
213     Resize( outDimensions );
214   }
215
216   ImageDimensions postCropDimensions(
217     std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
218     std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
219
220   if( postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
221       postCropDimensions.GetHeight() < outDimensions.GetHeight() )
222   {
223     uint16_t x = ( outDimensions.GetWidth()  - postCropDimensions.GetWidth() ) / 2;
224     uint16_t y = ( outDimensions.GetHeight() - postCropDimensions.GetHeight() ) / 2;
225     Crop( x, y, postCropDimensions );
226   }
227 }
228
229 void PixelBuffer::Crop( uint16_t x, uint16_t y, ImageDimensions cropDimensions )
230 {
231   PixelBufferPtr outBuffer = NewCrop( *this, x, y, cropDimensions );
232   TakeOwnershipOfBuffer( *outBuffer );
233 }
234
235 PixelBufferPtr PixelBuffer::NewCrop( const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions )
236 {
237   PixelBufferPtr outBuffer = PixelBuffer::New( cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat() );
238   int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
239   int srcStride = inBuffer.mWidth * bytesPerPixel;
240   int destStride = cropDimensions.GetWidth() * bytesPerPixel;
241
242   // Clamp crop to right edge
243   if( x + cropDimensions.GetWidth() > inBuffer.mWidth )
244   {
245     destStride = ( inBuffer.mWidth - x ) * bytesPerPixel;
246   }
247
248   int srcOffset = x * bytesPerPixel + y * srcStride;
249   int destOffset = 0;
250   unsigned char* destBuffer = outBuffer->mBuffer;
251
252   // Clamp crop to last row
253   unsigned int endRow = y + cropDimensions.GetHeight();
254   if( endRow > inBuffer.mHeight )
255   {
256     endRow = inBuffer.mHeight - 1 ;
257   }
258   for( uint16_t row = y; row < endRow; ++row )
259   {
260     memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride );
261     srcOffset += srcStride;
262     destOffset += destStride;
263   }
264   return outBuffer;
265
266 }
267
268 void PixelBuffer::SetMetadata( const Property::Map& map )
269 {
270   mMetadata.reset(new Property::Map(map));
271 }
272
273 bool PixelBuffer::GetMetadata(Property::Map& outMetadata) const
274 {
275   if( !mMetadata )
276   {
277     return false;
278   }
279   outMetadata = *mMetadata;
280   return true;
281 }
282
283 void PixelBuffer::SetMetadata(std::unique_ptr<Property::Map> metadata)
284 {
285   mMetadata = std::move(metadata);
286 }
287
288 void PixelBuffer::Resize( ImageDimensions outDimensions )
289 {
290   if( mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight() )
291   {
292     PixelBufferPtr outBuffer = NewResize( *this, outDimensions );
293     TakeOwnershipOfBuffer( *outBuffer );
294   }
295 }
296
297 PixelBufferPtr PixelBuffer::NewResize( const PixelBuffer& inBuffer, ImageDimensions outDimensions )
298 {
299   PixelBufferPtr outBuffer = PixelBuffer::New( outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat() );
300   ImageDimensions inDimensions( inBuffer.mWidth, inBuffer.mHeight );
301
302   bool hasAlpha = Pixel::HasAlpha( inBuffer.mPixelFormat );
303   int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
304
305   Resampler::Filter filterType = Resampler::LANCZOS4;
306   if( inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight() )
307   {
308     filterType = Resampler::MITCHELL;
309   }
310
311   // This method only really works for 8 bit wide channels.
312   // (But could be expanded to work)
313   if( inBuffer.mPixelFormat == Pixel::A8 ||
314       inBuffer.mPixelFormat == Pixel::L8 ||
315       inBuffer.mPixelFormat == Pixel::LA88 ||
316       inBuffer.mPixelFormat == Pixel::RGB888 ||
317       inBuffer.mPixelFormat == Pixel::RGB8888 ||
318       inBuffer.mPixelFormat == Pixel::BGR8888 ||
319       inBuffer.mPixelFormat == Pixel::RGBA8888 ||
320       inBuffer.mPixelFormat == Pixel::BGRA8888 )
321   {
322     Dali::Internal::Platform::Resample( inBuffer.mBuffer, inDimensions,
323                                         outBuffer->GetBuffer(), outDimensions,
324                                         filterType, bytesPerPixel, hasAlpha );
325   }
326   else
327   {
328     DALI_LOG_ERROR( "Trying to resize an image with too narrow a channel width" );
329   }
330
331   return outBuffer;
332 }
333
334 void PixelBuffer::ApplyGaussianBlur( const float blurRadius )
335 {
336   // This method only works for pixel buffer in RGBA format.
337   if( mWidth > 0 && mHeight > 0 && mPixelFormat == Pixel::RGBA8888 )
338   {
339     if ( blurRadius > Math::MACHINE_EPSILON_1 )
340     {
341       PerformGaussianBlurRGBA( *this, blurRadius );
342     }
343   }
344   else
345   {
346     DALI_LOG_ERROR( "Trying to apply gaussian blur to an empty pixel buffer or a pixel buffer not in RGBA format" );
347   }
348 }
349
350 }// namespace Adaptor
351 }// namespace Internal
352 }// namespace Dali