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