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