Add stride to PixelBuffer
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / pixel-buffer-impl.cpp
1 /*
2  * Copyright (c) 2022 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                          unsigned int        stride,
51                          Dali::Pixel::Format pixelFormat)
52 : mMetadata(),
53   mBuffer(buffer),
54   mBufferSize(bufferSize),
55   mWidth(width),
56   mHeight(height),
57   mStride(stride ? stride : width),
58   mPixelFormat(pixelFormat),
59   mPreMultiplied(false)
60 {
61 }
62
63 PixelBuffer::~PixelBuffer()
64 {
65   ReleaseBuffer();
66 }
67
68 PixelBufferPtr PixelBuffer::New(unsigned int        width,
69                                 unsigned int        height,
70                                 Dali::Pixel::Format pixelFormat)
71 {
72   unsigned int   bufferSize = width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat);
73   unsigned char* buffer     = NULL;
74   if(bufferSize > 0)
75   {
76     buffer = static_cast<unsigned char*>(malloc(bufferSize));
77   }
78   return new PixelBuffer(buffer, bufferSize, width, height, width, pixelFormat);
79 }
80
81 PixelBufferPtr PixelBuffer::New(unsigned char*      buffer,
82                                 unsigned int        bufferSize,
83                                 unsigned int        width,
84                                 unsigned int        height,
85                                 unsigned int        stride,
86                                 Dali::Pixel::Format pixelFormat)
87 {
88   return new PixelBuffer(buffer, bufferSize, width, height, stride, pixelFormat);
89 }
90
91 Dali::PixelData PixelBuffer::Convert(PixelBuffer& pixelBuffer)
92 {
93   Dali::PixelData pixelData = Dali::PixelData::New(pixelBuffer.mBuffer,
94                                                    pixelBuffer.mBufferSize,
95                                                    pixelBuffer.mWidth,
96                                                    pixelBuffer.mHeight,
97                                                    pixelBuffer.mStride,
98                                                    pixelBuffer.mPixelFormat,
99                                                    Dali::PixelData::FREE);
100   pixelBuffer.mBuffer       = NULL;
101   pixelBuffer.mWidth        = 0;
102   pixelBuffer.mHeight       = 0;
103   pixelBuffer.mBufferSize   = 0;
104   pixelBuffer.mStride       = 0;
105
106   return pixelData;
107 }
108
109 unsigned int PixelBuffer::GetWidth() const
110 {
111   return mWidth;
112 }
113
114 unsigned int PixelBuffer::GetHeight() const
115 {
116   return mHeight;
117 }
118
119 uint32_t PixelBuffer::GetStride() const
120 {
121   return mStride;
122 }
123
124 Dali::Pixel::Format PixelBuffer::GetPixelFormat() const
125 {
126   return mPixelFormat;
127 }
128
129 unsigned char* PixelBuffer::GetBuffer() const
130 {
131   return mBuffer;
132 }
133
134 const unsigned char* const PixelBuffer::GetConstBuffer() const
135 {
136   return mBuffer;
137 }
138
139 unsigned int PixelBuffer::GetBufferSize() const
140 {
141   return mBufferSize;
142 }
143
144 Dali::PixelData PixelBuffer::CreatePixelData() const
145 {
146   unsigned char* destBuffer = NULL;
147
148   if(mBufferSize > 0)
149   {
150     destBuffer = static_cast<unsigned char*>(malloc(mBufferSize));
151     memcpy(destBuffer, mBuffer, mBufferSize);
152   }
153
154   Dali::PixelData pixelData = Dali::PixelData::New(destBuffer, mBufferSize, mWidth, mHeight, mStride, mPixelFormat, Dali::PixelData::FREE);
155   return pixelData;
156 }
157
158 void PixelBuffer::ApplyMask(const PixelBuffer& inMask, float contentScale, bool cropToMask)
159 {
160   if(cropToMask)
161   {
162     // First scale this buffer by the contentScale, and crop to the mask size
163     // If it's too small, then scale the mask to match the image size
164     // Then apply the mask
165     ScaleAndCrop(contentScale, ImageDimensions(inMask.GetWidth(), inMask.GetHeight()));
166
167     if(inMask.mWidth > mWidth || inMask.mHeight > mHeight)
168     {
169       PixelBufferPtr mask = NewResize(inMask, ImageDimensions(mWidth, mHeight));
170       ApplyMaskInternal(*mask);
171     }
172     else
173     {
174       ApplyMaskInternal(inMask);
175     }
176   }
177   else
178   {
179     // First, scale the mask to match the image size,
180     // then apply the mask.
181     PixelBufferPtr mask = NewResize(inMask, ImageDimensions(mWidth, mHeight));
182     ApplyMaskInternal(*mask);
183   }
184 }
185
186 void PixelBuffer::ApplyMaskInternal(const PixelBuffer& mask)
187 {
188   int byteOffset = 0;
189   int bitMask    = 0;
190
191   Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
192   if(Dali::Pixel::HasAlpha(mPixelFormat) && bitMask == 255)
193   {
194     ApplyMaskToAlphaChannel(*this, mask);
195   }
196   else
197   {
198     PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer(*this, mask);
199     TakeOwnershipOfBuffer(*newPixelBuffer);
200     // On leaving scope, newPixelBuffer will get destroyed.
201   }
202 }
203
204 void PixelBuffer::TakeOwnershipOfBuffer(PixelBuffer& pixelBuffer)
205 {
206   ReleaseBuffer();
207
208   // Take ownership of new buffer
209   mBuffer             = pixelBuffer.mBuffer;
210   pixelBuffer.mBuffer = NULL;
211   mBufferSize         = pixelBuffer.mBufferSize;
212   mWidth              = pixelBuffer.mWidth;
213   mHeight             = pixelBuffer.mHeight;
214   mStride             = pixelBuffer.mStride;
215   mPixelFormat        = pixelBuffer.mPixelFormat;
216 }
217
218 void PixelBuffer::ReleaseBuffer()
219 {
220   if(mBuffer)
221   {
222     free(mBuffer);
223   }
224 }
225
226 void PixelBuffer::AllocateFixedSize(uint32_t size)
227 {
228   ReleaseBuffer();
229   mBuffer     = reinterpret_cast<unsigned char*>(malloc(size));
230   mBufferSize = size;
231 }
232
233 bool PixelBuffer::Rotate(Degree angle)
234 {
235   // Check first if Rotate() can perform the operation in the current pixel buffer.
236
237   bool validPixelFormat = false;
238   switch(mPixelFormat)
239   {
240     case Pixel::A8:
241     case Pixel::L8:
242     case Pixel::LA88:
243     case Pixel::RGB888:
244     case Pixel::RGB8888:
245     case Pixel::BGR8888:
246     case Pixel::RGBA8888:
247     case Pixel::BGRA8888: // FALL THROUGH
248     {
249       validPixelFormat = true;
250       break;
251     }
252     default:
253     {
254       // This pixel format is not supported for this operation.
255       validPixelFormat = false;
256       break;
257     }
258   }
259
260   if(!validPixelFormat)
261   {
262     // Can't rotate the pixel buffer with the current pixel format.
263     DALI_LOG_ERROR("Can't rotate the pixel buffer with the current pixel format\n");
264     return false;
265   }
266
267   float radians = Radian(angle).radian;
268
269   // Transform the input angle into the range [0..2PI]
270   radians = fmod(radians, TWO_PI);
271   radians += (radians < 0.f) ? TWO_PI : 0.f;
272
273   if(radians < Dali::Math::MACHINE_EPSILON_10)
274   {
275     // Nothing to do if the angle is zero.
276     return true;
277   }
278
279   const unsigned int pixelSize = Pixel::GetBytesPerPixel(mPixelFormat);
280
281   uint8_t* pixelsOut = nullptr;
282   Platform::RotateByShear(mBuffer,
283                           mWidth,
284                           mHeight,
285                           mStride,
286                           pixelSize,
287                           radians,
288                           pixelsOut,
289                           mWidth,
290                           mHeight);
291
292   // Check whether the rotation succedded and set the new pixel buffer data.
293   const bool success = nullptr != pixelsOut;
294
295   if(success)
296   {
297     // Release the memory of the current pixel buffer.
298     ReleaseBuffer();
299
300     // Set the new pixel buffer.
301     mBuffer     = pixelsOut;
302     pixelsOut   = nullptr;
303     mBufferSize = mWidth * mHeight * pixelSize;
304     mStride     = mWidth; // The buffer is tightly packed.
305   }
306
307   return success;
308 }
309
310 void PixelBuffer::ScaleAndCrop(float scaleFactor, ImageDimensions cropDimensions)
311 {
312   ImageDimensions outDimensions(float(mWidth) * scaleFactor,
313                                 float(mHeight) * scaleFactor);
314
315   if(outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight)
316   {
317     Resize(outDimensions);
318   }
319
320   ImageDimensions postCropDimensions(
321     std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
322     std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
323
324   if(postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
325      postCropDimensions.GetHeight() < outDimensions.GetHeight())
326   {
327     uint16_t x = (outDimensions.GetWidth() - postCropDimensions.GetWidth()) / 2;
328     uint16_t y = (outDimensions.GetHeight() - postCropDimensions.GetHeight()) / 2;
329     Crop(x, y, postCropDimensions);
330   }
331 }
332
333 void PixelBuffer::Crop(uint16_t x, uint16_t y, ImageDimensions cropDimensions)
334 {
335   PixelBufferPtr outBuffer = NewCrop(*this, x, y, cropDimensions);
336   TakeOwnershipOfBuffer(*outBuffer);
337 }
338
339 PixelBufferPtr PixelBuffer::NewCrop(const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions)
340 {
341   PixelBufferPtr outBuffer     = PixelBuffer::New(cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat());
342   int            bytesPerPixel = Pixel::GetBytesPerPixel(inBuffer.mPixelFormat);
343   int            srcStride     = inBuffer.mStride * bytesPerPixel;
344   int            destStride    = cropDimensions.GetWidth() * bytesPerPixel; // The destination buffer is tightly packed
345
346   // Clamp crop to right edge
347   if(x + cropDimensions.GetWidth() > inBuffer.mWidth)
348   {
349     destStride = (inBuffer.mWidth - x) * bytesPerPixel;
350   }
351
352   int            srcOffset  = x * bytesPerPixel + y * srcStride;
353   int            destOffset = 0;
354   unsigned char* destBuffer = outBuffer->mBuffer;
355
356   // Clamp crop to last row
357   unsigned int endRow = y + cropDimensions.GetHeight();
358   if(endRow > inBuffer.mHeight)
359   {
360     endRow = inBuffer.mHeight - 1;
361   }
362   for(uint16_t row = y; row < endRow; ++row)
363   {
364     memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride);
365     srcOffset += srcStride;
366     destOffset += destStride;
367   }
368   return outBuffer;
369 }
370
371 void PixelBuffer::SetMetadata(const Property::Map& map)
372 {
373   mMetadata.reset(new Property::Map(map));
374 }
375
376 bool PixelBuffer::GetMetadata(Property::Map& outMetadata) const
377 {
378   if(!mMetadata)
379   {
380     return false;
381   }
382   outMetadata = *mMetadata;
383   return true;
384 }
385
386 void PixelBuffer::SetMetadata(std::unique_ptr<Property::Map> metadata)
387 {
388   mMetadata = std::move(metadata);
389 }
390
391 void PixelBuffer::Resize(ImageDimensions outDimensions)
392 {
393   if(mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight())
394   {
395     PixelBufferPtr outBuffer = NewResize(*this, outDimensions);
396     TakeOwnershipOfBuffer(*outBuffer);
397   }
398 }
399
400 PixelBufferPtr PixelBuffer::NewResize(const PixelBuffer& inBuffer, ImageDimensions outDimensions)
401 {
402   PixelBufferPtr  outBuffer = PixelBuffer::New(outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat());
403   ImageDimensions inDimensions(inBuffer.mWidth, inBuffer.mHeight);
404
405   bool hasAlpha      = Pixel::HasAlpha(inBuffer.mPixelFormat);
406   int  bytesPerPixel = Pixel::GetBytesPerPixel(inBuffer.mPixelFormat);
407
408   Resampler::Filter filterType = Resampler::LANCZOS4;
409   if(inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight())
410   {
411     filterType = Resampler::MITCHELL;
412   }
413
414   // This method only really works for 8 bit wide channels.
415   // (But could be expanded to work)
416   if(inBuffer.mPixelFormat == Pixel::A8 ||
417      inBuffer.mPixelFormat == Pixel::L8 ||
418      inBuffer.mPixelFormat == Pixel::LA88 ||
419      inBuffer.mPixelFormat == Pixel::RGB888 ||
420      inBuffer.mPixelFormat == Pixel::RGB8888 ||
421      inBuffer.mPixelFormat == Pixel::BGR8888 ||
422      inBuffer.mPixelFormat == Pixel::RGBA8888 ||
423      inBuffer.mPixelFormat == Pixel::BGRA8888)
424   {
425     Dali::Internal::Platform::Resample(inBuffer.mBuffer, inDimensions, inBuffer.mStride, outBuffer->GetBuffer(), outDimensions, filterType, bytesPerPixel, hasAlpha);
426   }
427   else
428   {
429     DALI_LOG_ERROR("Trying to resize an image with too narrow a channel width");
430   }
431
432   return outBuffer;
433 }
434
435 void PixelBuffer::ApplyGaussianBlur(const float blurRadius)
436 {
437   // This method only works for pixel buffer in RGBA format.
438   if(mWidth > 0 && mHeight > 0 && mPixelFormat == Pixel::RGBA8888)
439   {
440     if(blurRadius > Math::MACHINE_EPSILON_1)
441     {
442       PerformGaussianBlurRGBA(*this, blurRadius);
443     }
444   }
445   else
446   {
447     DALI_LOG_ERROR("Trying to apply gaussian blur to an empty pixel buffer or a pixel buffer not in RGBA format");
448   }
449 }
450
451 void PixelBuffer::MultiplyColorByAlpha()
452 {
453   auto bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
454
455   // Compressed textures have unknown size of the pixel. Alpha premultiplication
456   // must be skipped in such case
457   if(Pixel::GetBytesPerPixel(mPixelFormat) && Pixel::HasAlpha(mPixelFormat))
458   {
459     unsigned char*     pixel       = mBuffer;
460     const unsigned int strideBytes = mStride * bytesPerPixel;
461     const unsigned int widthBytes  = mWidth * bytesPerPixel;
462
463     // Collect all valid channel list before lookup whole buffer
464     std::vector<Channel> validChannelList;
465     for(const Channel& channel : {Adaptor::RED, Adaptor::GREEN, Adaptor::BLUE, Adaptor::LUMINANCE})
466     {
467       if(HasChannel(mPixelFormat, channel))
468       {
469         validChannelList.emplace_back(channel);
470       }
471     }
472
473     if(DALI_LIKELY(!validChannelList.empty()))
474     {
475       for(unsigned int y = 0; y < mHeight; y++)
476       {
477         for(unsigned int x = 0; x < widthBytes; x += bytesPerPixel)
478         {
479           unsigned int alpha = ReadChannel(&pixel[x], mPixelFormat, Adaptor::ALPHA);
480           if(alpha < 255)
481           {
482             // If alpha is 255, we don't need to change color. Skip current pixel
483             // But if alpha is not 255, we should change color.
484             if(alpha > 0)
485             {
486               for(const Channel& channel : validChannelList)
487               {
488                 auto color = ReadChannel(&pixel[x], mPixelFormat, channel);
489                 WriteChannel(&pixel[x], mPixelFormat, channel, color * alpha / 255);
490               }
491             }
492             else
493             {
494               // If alpha is 0, just set all pixel as zero.
495               memset(&pixel[x], 0, bytesPerPixel);
496             }
497           }
498         }
499         pixel += strideBytes;
500       }
501     }
502     mPreMultiplied = true;
503   }
504 }
505
506 bool PixelBuffer::IsAlphaPreMultiplied() const
507 {
508   return mPreMultiplied;
509 }
510
511 uint32_t PixelBuffer::GetBrightness() const
512 {
513   uint32_t brightness    = 0;
514   uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
515
516   if(bytesPerPixel && mWidth && mHeight)
517   {
518     unsigned char* pixel       = mBuffer;
519     const uint32_t strideBytes = mStride * bytesPerPixel;
520     const uint32_t widthBytes  = mWidth * bytesPerPixel;
521     const uint32_t bufferSize  = mWidth * mHeight;
522
523     uint64_t red   = 0;
524     uint64_t green = 0;
525     uint64_t blue  = 0;
526
527     for(unsigned int y = 0; y < mHeight; y++)
528     {
529       for(unsigned int x = 0; x < widthBytes; x += bytesPerPixel)
530       {
531         red += ReadChannel(&pixel[x], mPixelFormat, Adaptor::RED);
532         green += ReadChannel(&pixel[x], mPixelFormat, Adaptor::GREEN);
533         blue += ReadChannel(&pixel[x], mPixelFormat, Adaptor::BLUE);
534       }
535       pixel += strideBytes;
536     }
537
538     // http://www.w3.org/TR/AERT#color-contrast
539     brightness = (red * BRIGHTNESS_CONSTANT_R + green * BRIGHTNESS_CONSTANT_G + blue * BRIGHTNESS_CONSTANT_B) / (1000uLL * bufferSize);
540   }
541
542   return brightness;
543 }
544
545 } // namespace Adaptor
546 } // namespace Internal
547 } // namespace Dali