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