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