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