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