Let we make adaptor invalidate if adaptor stop
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / ubuntu-x11 / native-image-source-impl-x.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/ubuntu-x11/native-image-source-impl-x.h>
20
21 // EXTERNAL INCLUDES
22 #include <X11/Xlib.h>
23 #include <X11/Xutil.h>
24 #include <dali/devel-api/common/stage.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/internal/system/linux/dali-ecore-x.h>
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 using Dali::Integration::PixelBuffer;
41
42 // Pieces needed to save compressed images (temporary location while plumbing):
43 namespace
44 {
45 /**
46    * Free an allocated XImage on destruction.
47    */
48 struct XImageJanitor
49 {
50   XImageJanitor(XImage* const pXImage)
51   : mXImage(pXImage)
52   {
53     DALI_ASSERT_DEBUG(pXImage != 0 && "Null pointer to XImage.");
54   }
55
56   ~XImageJanitor()
57   {
58     if(mXImage)
59     {
60       if(!XDestroyImage(mXImage))
61       {
62         DALI_LOG_ERROR("XImage deallocation failure");
63       }
64     }
65   }
66   XImage* const mXImage;
67
68 private:
69   XImageJanitor(const XImageJanitor& rhs);
70   XImageJanitor& operator=(const XImageJanitor& rhs);
71 };
72 } // namespace
73
74 NativeImageSourceX* NativeImageSourceX::New(uint32_t width, uint32_t height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource)
75 {
76   NativeImageSourceX* image = new NativeImageSourceX(width, height, depth, nativeImageSource);
77   DALI_ASSERT_DEBUG(image && "NativeImageSource allocation failed.");
78
79   // 2nd phase construction
80   if(image) //< Defensive in case we ever compile without exceptions.
81   {
82     image->Initialize();
83   }
84
85   return image;
86 }
87
88 NativeImageSourceX::NativeImageSourceX(uint32_t width, uint32_t height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource)
89 : mWidth(width),
90   mHeight(height),
91   mOwnPixmap(true),
92   mPixmap(0),
93   mBlendingRequired(false),
94   mColorDepth(depth),
95   mEglImageKHR(NULL),
96   mEglGraphics(NULL),
97   mEglImageExtensions(NULL),
98   mResourceDestructionCallback()
99 {
100   DALI_ASSERT_ALWAYS(Dali::Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
101
102   GraphicsInterface* graphics = &(Adaptor::GetImplementation(Adaptor::Get()).GetGraphicsInterface());
103   mEglGraphics                = static_cast<EglGraphics*>(graphics);
104
105   // assign the pixmap
106   mPixmap = GetPixmapFromAny(nativeImageSource);
107 }
108
109 void NativeImageSourceX::Initialize()
110 {
111   // if pixmap has been created outside of X11 Image we can return
112   if(mPixmap)
113   {
114     // we don't own the pixmap
115     mOwnPixmap = false;
116
117     // find out the pixmap width / height and color depth
118     GetPixmapDetails();
119     return;
120   }
121
122   // get the pixel depth
123   int depth = GetPixelDepth(mColorDepth);
124
125   // set whether blending is required according to pixel format based on the depth
126   /* default pixel format is RGB888
127      If depth = 8, Pixel::A8;
128      If depth = 16, Pixel::RGB565;
129      If depth = 32, Pixel::RGBA8888 */
130   mBlendingRequired = (depth == 32 || depth == 8);
131
132   mPixmap = ecore_x_pixmap_new(0, mWidth, mHeight, depth);
133   ecore_x_sync();
134 }
135
136 NativeImageSourceX::~NativeImageSourceX()
137 {
138   if(mOwnPixmap && mPixmap)
139   {
140     // Temporarily disable this as this causes a crash with EFL Version 1.24.0
141     //ecore_x_pixmap_free(mPixmap);
142   }
143 }
144
145 Any NativeImageSourceX::GetNativeImageSource() const
146 {
147   // return ecore x11 type
148   return Any(mPixmap);
149 }
150
151 bool NativeImageSourceX::GetPixels(std::vector<uint8_t>& pixbuf, uint32_t& width, uint32_t& height, Pixel::Format& pixelFormat) const
152 {
153   DALI_ASSERT_DEBUG(sizeof(uint32_t) == 4);
154   bool success = false;
155   width        = mWidth;
156   height       = mHeight;
157
158   // Open a display connection
159   Display* displayConnection = XOpenDisplay(0);
160
161   XImageJanitor xImageJanitor(XGetImage(displayConnection,
162                                         mPixmap,
163                                         0,
164                                         0, // x,y of subregion to extract.
165                                         width,
166                                         height, // of subregion to extract.
167                                         0xFFFFFFFF,
168                                         ZPixmap));
169   XImage* const pXImage = xImageJanitor.mXImage;
170   DALI_ASSERT_DEBUG(pXImage && "XImage (from pixmap) could not be retrieved from the server");
171   if(!pXImage)
172   {
173     DALI_LOG_ERROR("Could not retrieve Ximage.\n");
174   }
175   else
176   {
177     switch(pXImage->depth)
178     {
179       // Note, depth is a logical value. On target the framebuffer is still 32bpp
180       // (see pXImage->bits_per_pixel) so we go through XGetPixel() and swizzle.
181       // Note, this could be the default, fallback case for all depths if *pXImage
182       // didn't have blank RGB masks (X bug), but we have to hardcode the masks and
183       // shifts instead.
184       case 24:
185       {
186         pixelFormat = Pixel::RGB888;
187         pixbuf.resize(width * height * 3);
188         uint8_t* bufPtr = &pixbuf[0];
189
190         for(uint32_t y = 0; y < height; ++y)
191         {
192           for(uint32_t x = 0; x < width; ++x, bufPtr += 3)
193           {
194             const uint32_t pixel = XGetPixel(pXImage, x, y);
195
196             // store as RGB
197             const uint32_t blue  = pixel & 0xFFU;
198             const uint32_t green = (pixel >> 8) & 0xFFU;
199             const uint32_t red   = (pixel >> 16) & 0xFFU;
200
201             *bufPtr       = red;
202             *(bufPtr + 1) = green;
203             *(bufPtr + 2) = blue;
204           }
205         }
206         success = true;
207         break;
208       }
209       case 32:
210       {
211         if(pXImage->data)
212         {
213           // Sweep through the image, doing a vertical flip, but handling each scanline as
214           // an inlined intrinsic/builtin memcpy (should be fast):
215           pixbuf.resize(width * height * 4);
216           uint32_t*      bufPtr        = reinterpret_cast<uint32_t*>(&pixbuf[0]);
217           const uint32_t xDataLineSkip = pXImage->bytes_per_line;
218           const size_t   copy_count    = static_cast<size_t>(width) * 4;
219           pixelFormat                  = Pixel::BGRA8888;
220
221           for(uint32_t y = 0; y < height; ++y, bufPtr += width)
222           {
223             const char* const in = pXImage->data + xDataLineSkip * y;
224
225             // Copy a whole scanline at a time:
226             DALI_ASSERT_DEBUG(size_t(bufPtr) >= size_t(&pixbuf[0]));
227             DALI_ASSERT_DEBUG(reinterpret_cast<size_t>(bufPtr) + copy_count <= reinterpret_cast<size_t>(&pixbuf[pixbuf.size()]));
228             DALI_ASSERT_DEBUG(in >= pXImage->data);
229             DALI_ASSERT_DEBUG(in + copy_count <= pXImage->data + xDataLineSkip * height);
230             __builtin_memcpy(bufPtr, in, copy_count);
231           }
232           success = true;
233         }
234         else
235         {
236           DALI_LOG_ERROR("XImage has null data pointer.\n");
237         }
238         break;
239       }
240       // Make a case for 16 bit modes especially to remember that the only reason we don't support them is a bug in X:
241       case 16:
242       {
243         DALI_ASSERT_DEBUG(pXImage->red_mask && pXImage->green_mask && pXImage->blue_mask && "No image masks mean 16 bit modes are not possible.");
244         ///! If the above assert doesn't fail in a debug build, the X bug may have been fixed, so revisit this function.
245         ///! No break, fall through to the general unsupported format warning below.
246       }
247       default:
248       {
249         DALI_LOG_WARNING("Pixmap has unsupported bit-depth for getting pixels: %u\n", pXImage->depth);
250       }
251     }
252   }
253   if(!success)
254   {
255     DALI_LOG_ERROR("Failed to get pixels from NativeImageSource.\n");
256     pixbuf.resize(0);
257     width  = 0;
258     height = 0;
259   }
260
261   // Close the display connection
262   XCloseDisplay(displayConnection);
263
264   return success;
265 }
266
267 void NativeImageSourceX::SetSource(Any source)
268 {
269   mPixmap = GetPixmapFromAny(source);
270
271   if(mPixmap)
272   {
273     // we don't own the pixmap
274     mOwnPixmap = false;
275
276     // find out the pixmap width / height and color depth
277     GetPixmapDetails();
278   }
279 }
280
281 bool NativeImageSourceX::IsColorDepthSupported(Dali::NativeImageSource::ColorDepth colorDepth)
282 {
283   return true;
284 }
285
286 bool NativeImageSourceX::CreateResource()
287 {
288   mEglImageExtensions = mEglGraphics->GetImageExtensions();
289   DALI_ASSERT_DEBUG(mEglImageExtensions);
290
291   // if the image existed previously delete it.
292   if(mEglImageKHR != NULL)
293   {
294     DestroyResource();
295   }
296
297   // casting from an unsigned int to a void *, which should then be cast back
298   // to an unsigned int in the driver.
299   EGLClientBuffer eglBuffer = reinterpret_cast<EGLClientBuffer>(mPixmap);
300
301   mEglImageKHR = mEglImageExtensions->CreateImageKHR(eglBuffer);
302
303   return mEglImageKHR != NULL;
304 }
305
306 void NativeImageSourceX::DestroyResource()
307 {
308   mEglImageExtensions->DestroyImageKHR(mEglImageKHR);
309
310   mEglImageKHR = NULL;
311
312   if(mResourceDestructionCallback)
313   {
314     mResourceDestructionCallback->Trigger();
315   }
316 }
317
318 uint32_t NativeImageSourceX::TargetTexture()
319 {
320   mEglImageExtensions->TargetTextureKHR(mEglImageKHR);
321
322   return 0;
323 }
324
325 void NativeImageSourceX::PrepareTexture()
326 {
327 }
328
329 int NativeImageSourceX::GetPixelDepth(Dali::NativeImageSource::ColorDepth depth) const
330 {
331   switch(depth)
332   {
333     case Dali::NativeImageSource::COLOR_DEPTH_DEFAULT:
334     {
335       // Get the default screen depth
336       return ecore_x_default_depth_get(ecore_x_display_get(), ecore_x_default_screen_get());
337     }
338     case Dali::NativeImageSource::COLOR_DEPTH_8:
339     {
340       return 8;
341     }
342     case Dali::NativeImageSource::COLOR_DEPTH_16:
343     {
344       return 16;
345     }
346     case Dali::NativeImageSource::COLOR_DEPTH_24:
347     {
348       return 24;
349     }
350     case Dali::NativeImageSource::COLOR_DEPTH_32:
351     {
352       return 32;
353     }
354     default:
355     {
356       DALI_ASSERT_DEBUG(0 && "unknown color enum");
357       return 0;
358     }
359   }
360 }
361
362 int NativeImageSourceX::GetTextureTarget() const
363 {
364   return GL_TEXTURE_2D;
365 }
366
367 bool NativeImageSourceX::ApplyNativeFragmentShader(std::string& shader)
368 {
369   return false;
370 }
371
372 const char* NativeImageSourceX::GetCustomSamplerTypename() const
373 {
374   return nullptr;
375 }
376
377 Any NativeImageSourceX::GetNativeImageHandle() const
378 {
379   return Any(mPixmap);
380 }
381
382 bool NativeImageSourceX::SourceChanged() const
383 {
384   return true;
385 }
386
387 Ecore_X_Pixmap NativeImageSourceX::GetPixmapFromAny(Any pixmap) const
388 {
389   if(pixmap.Empty())
390   {
391     return 0;
392   }
393
394   // see if it is of type x11 pixmap
395   if(pixmap.GetType() == typeid(Pixmap))
396   {
397     // get the x pixmap type
398     Pixmap xpixmap = AnyCast<Pixmap>(pixmap);
399
400     // cast it to a ecore pixmap type
401     return static_cast<Ecore_X_Pixmap>(xpixmap);
402   }
403   else
404   {
405     return AnyCast<Ecore_X_Pixmap>(pixmap);
406   }
407 }
408
409 void NativeImageSourceX::GetPixmapDetails()
410 {
411   int x, y;
412
413   // get the width, height and depth
414   ecore_x_pixmap_geometry_get(mPixmap, &x, &y, reinterpret_cast<int*>(&mWidth), reinterpret_cast<int*>(&mHeight));
415
416   // set whether blending is required according to pixel format based on the depth
417   /* default pixel format is RGB888
418      If depth = 8, Pixel::A8;
419      If depth = 16, Pixel::RGB565;
420      If depth = 32, Pixel::RGBA8888 */
421   int depth         = ecore_x_pixmap_depth_get(mPixmap);
422   mBlendingRequired = (depth == 32 || depth == 8);
423 }
424
425 uint8_t* NativeImageSourceX::AcquireBuffer(uint32_t& width, uint32_t& height, uint32_t& stride)
426 {
427   return NULL;
428 }
429
430 bool NativeImageSourceX::ReleaseBuffer(const Rect<uint32_t>& updatedArea)
431 {
432   return false;
433 }
434
435 void NativeImageSourceX::SetResourceDestructionCallback(EventThreadCallback* callback)
436 {
437   mResourceDestructionCallback = std::unique_ptr<EventThreadCallback>(callback);
438 }
439
440 void NativeImageSourceX::EnableBackBuffer(bool enable)
441 {
442 }
443
444 } // namespace Adaptor
445
446 } // namespace Internal
447
448 } // namespace Dali