e69f2c8dc2f70bbbafed36b540adc7299368649c
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / pixel-buffer-impl.cpp
1 /*
2  * Copyright (c) 2021 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/alpha-mask.h>
27 #include <dali/internal/imaging/common/gaussian-blur.h>
28 #include <dali/internal/imaging/common/image-operations.h>
29 #include <dali/internal/imaging/common/pixel-manipulation.h>
30
31 namespace Dali
32 {
33 namespace Internal
34 {
35 namespace Adaptor
36 {
37 namespace
38 {
39 const float TWO_PI = 2.f * Math::PI; ///< 360 degrees in radians
40 // based on W3C Recommendations (https://www.w3.org/TR/AERT/#color-contrast)
41 constexpr uint32_t BRIGHTNESS_CONSTANT_R = 299;
42 constexpr uint32_t BRIGHTNESS_CONSTANT_G = 587;
43 constexpr uint32_t BRIGHTNESS_CONSTANT_B = 114;
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   mPreMultiplied(false)
58 {
59 }
60
61 PixelBuffer::~PixelBuffer()
62 {
63   ReleaseBuffer();
64 }
65
66 PixelBufferPtr PixelBuffer::New(unsigned int        width,
67                                 unsigned int        height,
68                                 Dali::Pixel::Format pixelFormat)
69 {
70   unsigned int   bufferSize = width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat);
71   unsigned char* buffer     = NULL;
72   if(bufferSize > 0)
73   {
74     buffer = static_cast<unsigned char*>(malloc(bufferSize));
75   }
76   return new PixelBuffer(buffer, bufferSize, width, height, pixelFormat);
77 }
78
79 PixelBufferPtr PixelBuffer::New(unsigned char*      buffer,
80                                 unsigned int        bufferSize,
81                                 unsigned int        width,
82                                 unsigned int        height,
83                                 Dali::Pixel::Format pixelFormat)
84 {
85   return new PixelBuffer(buffer, bufferSize, width, height, pixelFormat);
86 }
87
88 Dali::PixelData PixelBuffer::Convert(PixelBuffer& pixelBuffer)
89 {
90   Dali::PixelData pixelData = Dali::PixelData::New(pixelBuffer.mBuffer,
91                                                    pixelBuffer.mBufferSize,
92                                                    pixelBuffer.mWidth,
93                                                    pixelBuffer.mHeight,
94                                                    pixelBuffer.mPixelFormat,
95                                                    Dali::PixelData::FREE);
96   pixelBuffer.mBuffer       = NULL;
97   pixelBuffer.mWidth        = 0;
98   pixelBuffer.mHeight       = 0;
99   pixelBuffer.mBufferSize   = 0;
100
101   return pixelData;
102 }
103
104 unsigned int PixelBuffer::GetWidth() const
105 {
106   return mWidth;
107 }
108
109 unsigned int PixelBuffer::GetHeight() const
110 {
111   return mHeight;
112 }
113
114 Dali::Pixel::Format PixelBuffer::GetPixelFormat() const
115 {
116   return mPixelFormat;
117 }
118
119 unsigned char* PixelBuffer::GetBuffer() const
120 {
121   return mBuffer;
122 }
123
124 const unsigned char* const PixelBuffer::GetConstBuffer() const
125 {
126   return mBuffer;
127 }
128
129 unsigned int PixelBuffer::GetBufferSize() const
130 {
131   return mBufferSize;
132 }
133
134 Dali::PixelData PixelBuffer::CreatePixelData() const
135 {
136   unsigned char* destBuffer = NULL;
137
138   if(mBufferSize > 0)
139   {
140     destBuffer = static_cast<unsigned char*>(malloc(mBufferSize));
141     memcpy(destBuffer, mBuffer, mBufferSize);
142   }
143
144   Dali::PixelData pixelData = Dali::PixelData::New(destBuffer, mBufferSize, mWidth, mHeight, mPixelFormat, Dali::PixelData::FREE);
145   return pixelData;
146 }
147
148 void PixelBuffer::ApplyMask(const PixelBuffer& inMask, float contentScale, bool cropToMask)
149 {
150   if(cropToMask)
151   {
152     // First scale this buffer by the contentScale, and crop to the mask size
153     // If it's too small, then scale the mask to match the image size
154     // Then apply the mask
155     ScaleAndCrop(contentScale, ImageDimensions(inMask.GetWidth(), inMask.GetHeight()));
156
157     if(inMask.mWidth > mWidth || inMask.mHeight > mHeight)
158     {
159       PixelBufferPtr mask = NewResize(inMask, ImageDimensions(mWidth, mHeight));
160       ApplyMaskInternal(*mask);
161     }
162     else
163     {
164       ApplyMaskInternal(inMask);
165     }
166   }
167   else
168   {
169     // First, scale the mask to match the image size,
170     // then apply the mask.
171     PixelBufferPtr mask = NewResize(inMask, ImageDimensions(mWidth, mHeight));
172     ApplyMaskInternal(*mask);
173   }
174 }
175
176 void PixelBuffer::ApplyMaskInternal(const PixelBuffer& mask)
177 {
178   int byteOffset = 0;
179   int bitMask    = 0;
180
181   Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
182   if(Dali::Pixel::HasAlpha(mPixelFormat) && bitMask == 255)
183   {
184     ApplyMaskToAlphaChannel(*this, mask);
185   }
186   else
187   {
188     PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer(*this, mask);
189     TakeOwnershipOfBuffer(*newPixelBuffer);
190     // On leaving scope, newPixelBuffer will get destroyed.
191   }
192 }
193
194 void PixelBuffer::TakeOwnershipOfBuffer(PixelBuffer& pixelBuffer)
195 {
196   ReleaseBuffer();
197
198   // Take ownership of new buffer
199   mBuffer             = pixelBuffer.mBuffer;
200   pixelBuffer.mBuffer = NULL;
201   mBufferSize         = pixelBuffer.mBufferSize;
202   mWidth              = pixelBuffer.mWidth;
203   mHeight             = pixelBuffer.mHeight;
204   mPixelFormat        = pixelBuffer.mPixelFormat;
205 }
206
207 void PixelBuffer::ReleaseBuffer()
208 {
209   if(mBuffer)
210   {
211     free(mBuffer);
212   }
213 }
214
215 void PixelBuffer::AllocateFixedSize(uint32_t size)
216 {
217   ReleaseBuffer();
218   mBuffer     = reinterpret_cast<unsigned char*>(malloc(size));
219   mBufferSize = size;
220 }
221
222 bool PixelBuffer::Rotate(Degree angle)
223 {
224   // Check first if Rotate() can perform the operation in the current pixel buffer.
225
226   bool validPixelFormat = false;
227   switch(mPixelFormat)
228   {
229     case Pixel::A8:
230     case Pixel::L8:
231     case Pixel::LA88:
232     case Pixel::RGB888:
233     case Pixel::RGB8888:
234     case Pixel::BGR8888:
235     case Pixel::RGBA8888:
236     case Pixel::BGRA8888: // FALL THROUGH
237     {
238       validPixelFormat = true;
239       break;
240     }
241     default:
242     {
243       // This pixel format is not supported for this operation.
244       validPixelFormat = false;
245       break;
246     }
247   }
248
249   if(!validPixelFormat)
250   {
251     // Can't rotate the pixel buffer with the current pixel format.
252     DALI_LOG_ERROR("Can't rotate the pixel buffer with the current pixel format\n");
253     return false;
254   }
255
256   float radians = Radian(angle).radian;
257
258   // Transform the input angle into the range [0..2PI]
259   radians = fmod(radians, TWO_PI);
260   radians += (radians < 0.f) ? TWO_PI : 0.f;
261
262   if(radians < Dali::Math::MACHINE_EPSILON_10)
263   {
264     // Nothing to do if the angle is zero.
265     return true;
266   }
267
268   const unsigned int pixelSize = Pixel::GetBytesPerPixel(mPixelFormat);
269
270   uint8_t* pixelsOut = nullptr;
271   Platform::RotateByShear(mBuffer,
272                           mWidth,
273                           mHeight,
274                           pixelSize,
275                           radians,
276                           pixelsOut,
277                           mWidth,
278                           mHeight);
279
280   // Check whether the rotation succedded and set the new pixel buffer data.
281   const bool success = nullptr != pixelsOut;
282
283   if(success)
284   {
285     // Release the memory of the current pixel buffer.
286     ReleaseBuffer();
287
288     // Set the new pixel buffer.
289     mBuffer     = pixelsOut;
290     pixelsOut   = nullptr;
291     mBufferSize = mWidth * mHeight * pixelSize;
292   }
293
294   return success;
295 }
296
297 void PixelBuffer::ScaleAndCrop(float scaleFactor, ImageDimensions cropDimensions)
298 {
299   ImageDimensions outDimensions(float(mWidth) * scaleFactor,
300                                 float(mHeight) * scaleFactor);
301
302   if(outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight)
303   {
304     Resize(outDimensions);
305   }
306
307   ImageDimensions postCropDimensions(
308     std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
309     std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
310
311   if(postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
312      postCropDimensions.GetHeight() < outDimensions.GetHeight())
313   {
314     uint16_t x = (outDimensions.GetWidth() - postCropDimensions.GetWidth()) / 2;
315     uint16_t y = (outDimensions.GetHeight() - postCropDimensions.GetHeight()) / 2;
316     Crop(x, y, postCropDimensions);
317   }
318 }
319
320 void PixelBuffer::Crop(uint16_t x, uint16_t y, ImageDimensions cropDimensions)
321 {
322   PixelBufferPtr outBuffer = NewCrop(*this, x, y, cropDimensions);
323   TakeOwnershipOfBuffer(*outBuffer);
324 }
325
326 PixelBufferPtr PixelBuffer::NewCrop(const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions)
327 {
328   PixelBufferPtr outBuffer     = PixelBuffer::New(cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat());
329   int            bytesPerPixel = Pixel::GetBytesPerPixel(inBuffer.mPixelFormat);
330   int            srcStride     = inBuffer.mWidth * bytesPerPixel;
331   int            destStride    = cropDimensions.GetWidth() * bytesPerPixel;
332
333   // Clamp crop to right edge
334   if(x + cropDimensions.GetWidth() > inBuffer.mWidth)
335   {
336     destStride = (inBuffer.mWidth - x) * bytesPerPixel;
337   }
338
339   int            srcOffset  = x * bytesPerPixel + y * srcStride;
340   int            destOffset = 0;
341   unsigned char* destBuffer = outBuffer->mBuffer;
342
343   // Clamp crop to last row
344   unsigned int endRow = y + cropDimensions.GetHeight();
345   if(endRow > inBuffer.mHeight)
346   {
347     endRow = inBuffer.mHeight - 1;
348   }
349   for(uint16_t row = y; row < endRow; ++row)
350   {
351     memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride);
352     srcOffset += srcStride;
353     destOffset += destStride;
354   }
355   return outBuffer;
356 }
357
358 void PixelBuffer::SetMetadata(const Property::Map& map)
359 {
360   mMetadata.reset(new Property::Map(map));
361 }
362
363 bool PixelBuffer::GetMetadata(Property::Map& outMetadata) const
364 {
365   if(!mMetadata)
366   {
367     return false;
368   }
369   outMetadata = *mMetadata;
370   return true;
371 }
372
373 void PixelBuffer::SetMetadata(std::unique_ptr<Property::Map> metadata)
374 {
375   mMetadata = std::move(metadata);
376 }
377
378 void PixelBuffer::Resize(ImageDimensions outDimensions)
379 {
380   if(mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight())
381   {
382     PixelBufferPtr outBuffer = NewResize(*this, outDimensions);
383     TakeOwnershipOfBuffer(*outBuffer);
384   }
385 }
386
387 PixelBufferPtr PixelBuffer::NewResize(const PixelBuffer& inBuffer, ImageDimensions outDimensions)
388 {
389   PixelBufferPtr  outBuffer = PixelBuffer::New(outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat());
390   ImageDimensions inDimensions(inBuffer.mWidth, inBuffer.mHeight);
391
392   bool hasAlpha      = Pixel::HasAlpha(inBuffer.mPixelFormat);
393   int  bytesPerPixel = Pixel::GetBytesPerPixel(inBuffer.mPixelFormat);
394
395   Resampler::Filter filterType = Resampler::LANCZOS4;
396   if(inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight())
397   {
398     filterType = Resampler::MITCHELL;
399   }
400
401   // This method only really works for 8 bit wide channels.
402   // (But could be expanded to work)
403   if(inBuffer.mPixelFormat == Pixel::A8 ||
404      inBuffer.mPixelFormat == Pixel::L8 ||
405      inBuffer.mPixelFormat == Pixel::LA88 ||
406      inBuffer.mPixelFormat == Pixel::RGB888 ||
407      inBuffer.mPixelFormat == Pixel::RGB8888 ||
408      inBuffer.mPixelFormat == Pixel::BGR8888 ||
409      inBuffer.mPixelFormat == Pixel::RGBA8888 ||
410      inBuffer.mPixelFormat == Pixel::BGRA8888)
411   {
412     Dali::Internal::Platform::Resample(inBuffer.mBuffer, inDimensions, outBuffer->GetBuffer(), outDimensions, 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   mPreMultiplied = true;
466 }
467
468 bool PixelBuffer::IsAlphaPreMultiplied() const
469 {
470   return mPreMultiplied;
471 }
472
473 uint32_t PixelBuffer::GetBrightness() const
474 {
475   uint32_t brightness = 0;
476
477   uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
478   if(bytesPerPixel)
479   {
480     unsigned char* pixel      = mBuffer;
481     const uint32_t bufferSize = mWidth * mHeight;
482     uint64_t       red        = 0;
483     uint64_t       green      = 0;
484     uint64_t       blue       = 0;
485
486     for(uint32_t i = 0; i < bufferSize; ++i)
487     {
488       red += ReadChannel(pixel, mPixelFormat, Adaptor::RED);
489       green += ReadChannel(pixel, mPixelFormat, Adaptor::GREEN);
490       blue += ReadChannel(pixel, mPixelFormat, Adaptor::BLUE);
491       pixel += bytesPerPixel;
492     }
493     // http://www.w3.org/TR/AERT#color-contrast
494     brightness = (red * BRIGHTNESS_CONSTANT_R + green * BRIGHTNESS_CONSTANT_G + blue * BRIGHTNESS_CONSTANT_B) / (1000uLL * bufferSize);
495   }
496
497   return brightness;
498 }
499
500 } // namespace Adaptor
501 } // namespace Internal
502 } // namespace Dali