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