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