[dali_2.3.25] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / tizen / native-image-source-impl-tizen.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/tizen/native-image-source-impl-tizen.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/stage.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/gl-defines.h>
25 #include <tbm_surface_internal.h>
26 #include <cstring>
27
28 // INTERNAL INCLUDES
29 #include <dali/integration-api/adaptor-framework/render-surface-interface.h>
30 #include <dali/internal/adaptor/common/adaptor-impl.h>
31 #include <dali/internal/graphics/common/egl-image-extensions.h>
32 #include <dali/internal/graphics/gles/egl-graphics.h>
33
34 namespace Dali
35 {
36 namespace Internal
37 {
38 namespace Adaptor
39 {
40 namespace
41 {
42 const char* SAMPLER_TYPE = "samplerExternalOES";
43
44 // clang-format off
45 tbm_format FORMATS_BLENDING_REQUIRED[] = {
46   TBM_FORMAT_ARGB4444, TBM_FORMAT_ABGR4444,
47   TBM_FORMAT_RGBA4444, TBM_FORMAT_BGRA4444,
48   TBM_FORMAT_RGBX5551, TBM_FORMAT_BGRX5551,
49   TBM_FORMAT_ARGB1555, TBM_FORMAT_ABGR1555,
50   TBM_FORMAT_RGBA5551, TBM_FORMAT_BGRA5551,
51   TBM_FORMAT_ARGB8888, TBM_FORMAT_ABGR8888,
52   TBM_FORMAT_RGBA8888, TBM_FORMAT_BGRA8888,
53   TBM_FORMAT_ARGB2101010, TBM_FORMAT_ABGR2101010,
54   TBM_FORMAT_RGBA1010102, TBM_FORMAT_BGRA1010102
55 };
56 // clang-format on
57
58 const int NUM_FORMATS_BLENDING_REQUIRED = 18;
59
60 } // namespace
61
62 using Dali::Integration::PixelBuffer;
63
64 NativeImageSourceTizen* NativeImageSourceTizen::New(uint32_t width, uint32_t height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource)
65 {
66   NativeImageSourceTizen* image = new NativeImageSourceTizen(width, height, depth, nativeImageSource);
67   DALI_ASSERT_DEBUG(image && "NativeImageSource allocation failed.");
68
69   if(image)
70   {
71     image->Initialize();
72   }
73
74   return image;
75 }
76
77 NativeImageSourceTizen::NativeImageSourceTizen(uint32_t width, uint32_t height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource)
78 : mWidth(width),
79   mHeight(height),
80   mTbmSurface(NULL),
81   mTbmBackSurface(NULL),
82   mTbmFormat(0),
83   mColorDepth(depth),
84   mMutex(),
85   mEglImageKHR(NULL),
86   mEglGraphics(NULL),
87   mEglImageExtensions(NULL),
88   mResourceDestructionCallback(),
89   mOwnTbmSurface(false),
90   mBlendingRequired(false),
91   mSetSource(false),
92   mIsBufferAcquired(false),
93   mBackBufferEnabled(false)
94 {
95   DALI_ASSERT_ALWAYS(Dali::Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
96
97   GraphicsInterface* graphics = &(Adaptor::GetImplementation(Adaptor::Get()).GetGraphicsInterface());
98   mEglGraphics                = static_cast<EglGraphics*>(graphics);
99
100   mTbmSurface = GetSurfaceFromAny(nativeImageSource);
101
102   if(mTbmSurface != NULL)
103   {
104     tbm_surface_internal_ref(mTbmSurface);
105     mBlendingRequired = CheckBlending(tbm_surface_get_format(mTbmSurface));
106     mWidth            = tbm_surface_get_width(mTbmSurface);
107     mHeight           = tbm_surface_get_height(mTbmSurface);
108   }
109 }
110
111 void NativeImageSourceTizen::Initialize()
112 {
113   if(mTbmSurface != NULL || mWidth == 0 || mHeight == 0)
114   {
115     return;
116   }
117
118   tbm_format format = TBM_FORMAT_RGB888;
119   int        depth  = 0;
120
121   switch(mColorDepth)
122   {
123     case Dali::NativeImageSource::COLOR_DEPTH_DEFAULT:
124     {
125       format = TBM_FORMAT_ARGB8888;
126       depth  = 32;
127       break;
128     }
129     case Dali::NativeImageSource::COLOR_DEPTH_8:
130     {
131       format = TBM_FORMAT_C8;
132       depth  = 8;
133       break;
134     }
135     case Dali::NativeImageSource::COLOR_DEPTH_16:
136     {
137       format = TBM_FORMAT_RGB565;
138       depth  = 16;
139       break;
140     }
141     case Dali::NativeImageSource::COLOR_DEPTH_24:
142     {
143       format = TBM_FORMAT_RGB888;
144       depth  = 24;
145       break;
146     }
147     case Dali::NativeImageSource::COLOR_DEPTH_32:
148     {
149       format = TBM_FORMAT_ARGB8888;
150       depth  = 32;
151       break;
152     }
153     default:
154     {
155       DALI_LOG_WARNING("Wrong color depth.\n");
156       return;
157     }
158   }
159
160   // set whether blending is required according to pixel format based on the depth
161   /* default pixel format is RGB888
162      If depth = 8, Pixel::A8;
163      If depth = 16, Pixel::RGB565;
164      If depth = 32, Pixel::RGBA8888 */
165   mBlendingRequired = (depth == 32 || depth == 8);
166
167   mTbmSurface    = tbm_surface_create(mWidth, mHeight, format);
168   mOwnTbmSurface = true;
169 }
170
171 tbm_surface_h NativeImageSourceTizen::GetSurfaceFromAny(Any source) const
172 {
173   if(source.Empty())
174   {
175     return NULL;
176   }
177
178   if(source.GetType() == typeid(tbm_surface_h))
179   {
180     return AnyCast<tbm_surface_h>(source);
181   }
182   else
183   {
184     return NULL;
185   }
186 }
187
188 void NativeImageSourceTizen::DestroySurface()
189 {
190   if(mTbmSurface)
191   {
192     if(mIsBufferAcquired)
193     {
194       Rect<uint32_t> emptyRect{};
195       ReleaseBuffer(emptyRect);
196     }
197     if(mOwnTbmSurface)
198     {
199       if(tbm_surface_destroy(mTbmSurface) != TBM_SURFACE_ERROR_NONE)
200       {
201         DALI_LOG_ERROR("Failed to destroy tbm_surface\n");
202       }
203     }
204     else
205     {
206       tbm_surface_internal_unref(mTbmSurface);
207     }
208     mTbmSurface = NULL;
209
210     DestroyBackBuffer();
211   }
212 }
213
214 NativeImageSourceTizen::~NativeImageSourceTizen()
215 {
216   DestroySurface();
217 }
218
219 Any NativeImageSourceTizen::GetNativeImageSource() const
220 {
221   return Any(mTbmSurface);
222 }
223
224 bool NativeImageSourceTizen::GetPixels(std::vector<uint8_t>& pixbuf, uint32_t& width, uint32_t& height, Pixel::Format& pixelFormat) const
225 {
226   std::scoped_lock lock(mMutex);
227   if(mTbmSurface != NULL)
228   {
229     tbm_surface_info_s surface_info;
230
231     if(tbm_surface_map(mTbmSurface, TBM_SURF_OPTION_READ, &surface_info) != TBM_SURFACE_ERROR_NONE)
232     {
233       DALI_LOG_ERROR("Fail to map tbm_surface\n");
234
235       width  = 0;
236       height = 0;
237
238       return false;
239     }
240
241     tbm_format format = surface_info.format;
242     uint32_t   stride = surface_info.planes[0].stride;
243     uint8_t*   ptr    = surface_info.planes[0].ptr;
244
245     width  = mWidth;
246     height = mHeight;
247     size_t lineSize;
248     size_t offset;
249     size_t cOffset;
250
251     switch(format)
252     {
253       case TBM_FORMAT_RGB888:
254       {
255         lineSize    = width * 3;
256         pixelFormat = Pixel::RGB888;
257         pixbuf.resize(lineSize * height);
258         uint8_t* bufptr = &pixbuf[0];
259
260         for(uint32_t r = 0; r < height; ++r, bufptr += lineSize)
261         {
262           for(uint32_t c = 0; c < width; ++c)
263           {
264             cOffset                 = c * 3;
265             offset                  = cOffset + r * stride;
266             *(bufptr + cOffset)     = ptr[offset + 2];
267             *(bufptr + cOffset + 1) = ptr[offset + 1];
268             *(bufptr + cOffset + 2) = ptr[offset];
269           }
270         }
271         break;
272       }
273       case TBM_FORMAT_RGBA8888:
274       {
275         lineSize    = width * 4;
276         pixelFormat = Pixel::RGBA8888;
277         pixbuf.resize(lineSize * height);
278         uint8_t* bufptr = &pixbuf[0];
279
280         for(uint32_t r = 0; r < height; ++r, bufptr += lineSize)
281         {
282           for(uint32_t c = 0; c < width; ++c)
283           {
284             cOffset                 = c * 4;
285             offset                  = cOffset + r * stride;
286             *(bufptr + cOffset)     = ptr[offset + 3];
287             *(bufptr + cOffset + 1) = ptr[offset + 2];
288             *(bufptr + cOffset + 2) = ptr[offset + 1];
289             *(bufptr + cOffset + 3) = ptr[offset];
290           }
291         }
292         break;
293       }
294       case TBM_FORMAT_ARGB8888:
295       {
296         lineSize    = width * 4;
297         pixelFormat = Pixel::RGBA8888;
298         pixbuf.resize(lineSize * height);
299         uint8_t* bufptr = &pixbuf[0];
300
301         for(uint32_t r = 0; r < height; ++r, bufptr += lineSize)
302         {
303           for(uint32_t c = 0; c < width; ++c)
304           {
305             cOffset                 = c * 4;
306             offset                  = cOffset + r * stride;
307             *(bufptr + cOffset)     = ptr[offset + 2];
308             *(bufptr + cOffset + 1) = ptr[offset + 1];
309             *(bufptr + cOffset + 2) = ptr[offset];
310             *(bufptr + cOffset + 3) = ptr[offset + 3];
311           }
312         }
313         break;
314       }
315       default:
316       {
317         DALI_ASSERT_ALWAYS(0 && "Tbm surface has unsupported pixel format.\n");
318
319         return false;
320       }
321     }
322
323     if(tbm_surface_unmap(mTbmSurface) != TBM_SURFACE_ERROR_NONE)
324     {
325       DALI_LOG_ERROR("Fail to unmap tbm_surface\n");
326     }
327
328     return true;
329   }
330
331   DALI_LOG_WARNING("TBM surface does not exist.\n");
332
333   width  = 0;
334   height = 0;
335
336   return false;
337 }
338
339 void NativeImageSourceTizen::SetSource(Any source)
340 {
341   std::scoped_lock lock(mMutex);
342
343   DestroySurface();
344
345   mOwnTbmSurface = false;
346   mTbmSurface    = GetSurfaceFromAny(source);
347
348   if(mTbmSurface != NULL)
349   {
350     mSetSource = true;
351     tbm_surface_internal_ref(mTbmSurface);
352     mBlendingRequired = CheckBlending(tbm_surface_get_format(mTbmSurface));
353     mWidth            = tbm_surface_get_width(mTbmSurface);
354     mHeight           = tbm_surface_get_height(mTbmSurface);
355
356     if(mBackBufferEnabled)
357     {
358       DestroyBackBuffer();
359       CreateBackBuffer();
360     }
361   }
362 }
363
364 bool NativeImageSourceTizen::IsColorDepthSupported(Dali::NativeImageSource::ColorDepth colorDepth)
365 {
366   uint32_t*  formats;
367   uint32_t   formatNum;
368   tbm_format format = TBM_FORMAT_RGB888;
369
370   switch(colorDepth)
371   {
372     case Dali::NativeImageSource::COLOR_DEPTH_DEFAULT:
373     {
374       format = TBM_FORMAT_ARGB8888;
375       break;
376     }
377     case Dali::NativeImageSource::COLOR_DEPTH_8:
378     {
379       format = TBM_FORMAT_C8;
380       break;
381     }
382     case Dali::NativeImageSource::COLOR_DEPTH_16:
383     {
384       format = TBM_FORMAT_RGB565;
385       break;
386     }
387     case Dali::NativeImageSource::COLOR_DEPTH_24:
388     {
389       format = TBM_FORMAT_RGB888;
390       break;
391     }
392     case Dali::NativeImageSource::COLOR_DEPTH_32:
393     {
394       format = TBM_FORMAT_ARGB8888;
395       break;
396     }
397   }
398
399   if(tbm_surface_query_formats(&formats, &formatNum))
400   {
401     for(uint32_t i = 0; i < formatNum; i++)
402     {
403       if(formats[i] == format)
404       {
405         free(formats);
406         return true;
407       }
408     }
409   }
410
411   free(formats);
412   return false;
413 }
414
415 bool NativeImageSourceTizen::CreateResource()
416 {
417   // If an EGL image exists, use it as it is without creating it.
418   if(mEglImageKHR != NULL)
419   {
420     return true;
421   }
422
423   // casting from an unsigned int to a void *, which should then be cast back
424   // to an unsigned int in the driver.
425   EGLClientBuffer eglBuffer = mTbmBackSurface ? reinterpret_cast<EGLClientBuffer>(mTbmBackSurface) : reinterpret_cast<EGLClientBuffer>(mTbmSurface);
426   if(!eglBuffer || !tbm_surface_internal_is_valid(mTbmSurface))
427   {
428     DALI_LOG_ERROR("Invalid surface\n");
429     return false;
430   }
431
432   mEglImageExtensions = mEglGraphics->GetImageExtensions();
433   DALI_ASSERT_DEBUG(mEglImageExtensions);
434
435   mEglImageKHR = mEglImageExtensions->CreateImageKHR(eglBuffer);
436   if(!mEglImageKHR)
437   {
438     DALI_LOG_ERROR("Fail to CreateImageKHR\n");
439   }
440
441   return mEglImageKHR != NULL;
442 }
443
444 void NativeImageSourceTizen::DestroyResource()
445 {
446   std::scoped_lock lock(mMutex);
447   if(mEglImageKHR)
448   {
449     mEglImageExtensions->DestroyImageKHR(mEglImageKHR);
450
451     mEglImageKHR = NULL;
452   }
453
454   if(mResourceDestructionCallback)
455   {
456     mResourceDestructionCallback->Trigger();
457   }
458 }
459
460 uint32_t NativeImageSourceTizen::TargetTexture()
461 {
462   mEglImageExtensions->TargetTextureKHR(mEglImageKHR);
463
464   return 0;
465 }
466
467 void NativeImageSourceTizen::PrepareTexture()
468 {
469   std::scoped_lock lock(mMutex);
470   if(mSetSource)
471   {
472     // Destroy previous eglImage because use for new one.
473     // if mEglImageKHR is not to be NULL here, it will not be updated with a new eglImage.
474     mEglImageExtensions->DestroyImageKHR(mEglImageKHR);
475     mEglImageKHR = NULL;
476
477     if(CreateResource())
478     {
479       TargetTexture();
480     }
481
482     mSetSource = false;
483   }
484 }
485
486 bool NativeImageSourceTizen::ApplyNativeFragmentShader(std::string& shader)
487 {
488   return mEglGraphics->ApplyNativeFragmentShader(shader, SAMPLER_TYPE);
489 }
490
491 const char* NativeImageSourceTizen::GetCustomSamplerTypename() const
492 {
493   return SAMPLER_TYPE;
494 }
495
496 int NativeImageSourceTizen::GetTextureTarget() const
497 {
498   return GL_TEXTURE_EXTERNAL_OES;
499 }
500
501 Any NativeImageSourceTizen::GetNativeImageHandle() const
502 {
503   return GetNativeImageSource();
504 }
505
506 bool NativeImageSourceTizen::SourceChanged() const
507 {
508   if(mTbmBackSurface)
509   {
510     return mUpdatedArea.IsEmpty() ? false : true;
511   }
512   return true;
513 }
514
515 Rect<uint32_t> NativeImageSourceTizen::GetUpdatedArea()
516 {
517   std::scoped_lock lock(mMutex);
518   Rect<uint32_t>   updatedArea{0, 0, mWidth, mHeight};
519   if(!mUpdatedArea.IsEmpty() && mTbmSurface != NULL && mTbmBackSurface != NULL)
520   {
521     updatedArea = mUpdatedArea;
522
523     tbm_surface_info_s info;
524     if(tbm_surface_map(mTbmSurface, TBM_SURF_OPTION_READ | TBM_SURF_OPTION_WRITE, &info) != TBM_SURFACE_ERROR_NONE)
525     {
526       DALI_LOG_ERROR("Fail to map tbm_surface\n");
527       return updatedArea;
528     }
529
530     tbm_surface_info_s backBufferInfo;
531     if(tbm_surface_map(mTbmBackSurface, TBM_SURF_OPTION_READ | TBM_SURF_OPTION_WRITE, &backBufferInfo) != TBM_SURFACE_ERROR_NONE)
532     {
533       DALI_LOG_ERROR("Fail to map tbm_surface - backbuffer\n");
534       tbm_surface_unmap(mTbmSurface);
535       return updatedArea;
536     }
537
538     uint8_t* srcBuffer = info.planes[0].ptr;
539     uint8_t* dstBuffer = backBufferInfo.planes[0].ptr;
540
541     uint32_t stride        = info.planes[0].stride;
542     uint32_t bytesPerPixel = info.bpp >> 3;
543
544     srcBuffer += updatedArea.y * stride + updatedArea.x * bytesPerPixel;
545     dstBuffer += updatedArea.y * stride + updatedArea.x * bytesPerPixel;
546
547     // Copy to back buffer
548     for(uint32_t y = 0; y < updatedArea.height; y++)
549     {
550       memcpy(dstBuffer, srcBuffer, updatedArea.width * bytesPerPixel);
551       srcBuffer += stride;
552       dstBuffer += stride;
553     }
554
555     tbm_surface_unmap(mTbmSurface);
556     tbm_surface_unmap(mTbmBackSurface);
557
558     // Reset the updated area
559     mUpdatedArea.Set(0u, 0u, 0u, 0u);
560   }
561   return updatedArea;
562 }
563
564 bool NativeImageSourceTizen::CheckBlending(tbm_format format)
565 {
566   if(mTbmFormat != format)
567   {
568     for(int i = 0; i < NUM_FORMATS_BLENDING_REQUIRED; ++i)
569     {
570       if(format == FORMATS_BLENDING_REQUIRED[i])
571       {
572         mBlendingRequired = true;
573         break;
574       }
575     }
576     mTbmFormat = format;
577   }
578
579   return mBlendingRequired;
580 }
581
582 uint8_t* NativeImageSourceTizen::AcquireBuffer(uint32_t& width, uint32_t& height, uint32_t& stride)
583 {
584   mMutex.lock(); // We don't use std::scoped_lock here
585   if(mTbmSurface != NULL)
586   {
587     tbm_surface_info_s info;
588
589     if(tbm_surface_map(mTbmSurface, TBM_SURF_OPTION_READ | TBM_SURF_OPTION_WRITE, &info) != TBM_SURFACE_ERROR_NONE)
590     {
591       DALI_LOG_ERROR("Fail to map tbm_surface\n");
592
593       width  = 0;
594       height = 0;
595
596       mMutex.unlock();
597       return NULL;
598     }
599     tbm_surface_internal_ref(mTbmSurface);
600     mIsBufferAcquired = true;
601
602     stride = info.planes[0].stride;
603     width  = mWidth;
604     height = mHeight;
605
606     // The lock is held until ReleaseBuffer is called
607     return info.planes[0].ptr;
608   }
609   mMutex.unlock();
610   return NULL;
611 }
612
613 bool NativeImageSourceTizen::ReleaseBuffer(const Rect<uint32_t>& updatedArea)
614 {
615   bool ret = false;
616   if(mTbmSurface != NULL)
617   {
618     if(mTbmBackSurface)
619     {
620       if(updatedArea.IsEmpty())
621       {
622         mUpdatedArea.Set(0, 0, mWidth, mHeight);
623       }
624       else
625       {
626         if(mUpdatedArea.IsEmpty())
627         {
628           mUpdatedArea = updatedArea;
629         }
630         else
631         {
632           mUpdatedArea.Merge(updatedArea);
633         }
634       }
635     }
636
637     ret = (tbm_surface_unmap(mTbmSurface) == TBM_SURFACE_ERROR_NONE);
638     if(!ret)
639     {
640       DALI_LOG_ERROR("Fail to unmap tbm_surface\n");
641     }
642     tbm_surface_internal_unref(mTbmSurface);
643     mIsBufferAcquired = false;
644   }
645   // Unlock the mutex locked by AcquireBuffer.
646   mMutex.unlock();
647   return ret;
648 }
649
650 void NativeImageSourceTizen::SetResourceDestructionCallback(EventThreadCallback* callback)
651 {
652   std::scoped_lock lock(mMutex);
653   mResourceDestructionCallback = std::unique_ptr<EventThreadCallback>(callback);
654 }
655
656 void NativeImageSourceTizen::EnableBackBuffer(bool enable)
657 {
658   if(enable != mBackBufferEnabled)
659   {
660     mBackBufferEnabled = enable;
661
662     if(mBackBufferEnabled)
663     {
664       CreateBackBuffer();
665     }
666     else
667     {
668       DestroyBackBuffer();
669     }
670   }
671 }
672
673 void NativeImageSourceTizen::CreateBackBuffer()
674 {
675   if(!mTbmBackSurface && mTbmSurface)
676   {
677     mTbmBackSurface = tbm_surface_create(mWidth, mHeight, tbm_surface_get_format(mTbmSurface));
678   }
679 }
680
681 void NativeImageSourceTizen::DestroyBackBuffer()
682 {
683   if(mTbmBackSurface)
684   {
685     if(tbm_surface_destroy(mTbmBackSurface) != TBM_SURFACE_ERROR_NONE)
686     {
687       DALI_LOG_ERROR("Failed to destroy tbm_surface\n");
688     }
689     mTbmBackSurface = NULL;
690   }
691 }
692
693 } // namespace Adaptor
694
695 } // namespace Internal
696
697 } // namespace Dali