2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/imaging/ubuntu-x11/native-image-source-impl-x.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>
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>
40 using Dali::Integration::PixelBuffer;
42 // Pieces needed to save compressed images (temporary location while plumbing):
46 * Free an allocated XImage on destruction.
50 XImageJanitor(XImage* const pXImage)
53 DALI_ASSERT_DEBUG(pXImage != 0 && "Null pointer to XImage.");
60 if(!XDestroyImage(mXImage))
62 DALI_LOG_ERROR("XImage deallocation failure");
66 XImage* const mXImage;
69 XImageJanitor(const XImageJanitor& rhs);
70 XImageJanitor& operator=(const XImageJanitor& rhs);
74 NativeImageSourceX* NativeImageSourceX::New(uint32_t width, uint32_t height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource)
76 NativeImageSourceX* image = new NativeImageSourceX(width, height, depth, nativeImageSource);
77 DALI_ASSERT_DEBUG(image && "NativeImageSource allocation failed.");
79 // 2nd phase construction
80 if(image) //< Defensive in case we ever compile without exceptions.
88 NativeImageSourceX::NativeImageSourceX(uint32_t width, uint32_t height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource)
93 mBlendingRequired(false),
97 mEglImageExtensions(NULL),
98 mResourceDestructionCallback()
100 DALI_ASSERT_ALWAYS(Dali::Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
102 GraphicsInterface* graphics = &(Adaptor::GetImplementation(Adaptor::Get()).GetGraphicsInterface());
103 mEglGraphics = static_cast<EglGraphics*>(graphics);
106 mPixmap = GetPixmapFromAny(nativeImageSource);
109 void NativeImageSourceX::Initialize()
111 // if pixmap has been created outside of X11 Image we can return
114 // we don't own the pixmap
117 // find out the pixmap width / height and color depth
122 // get the pixel depth
123 int depth = GetPixelDepth(mColorDepth);
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);
132 mPixmap = ecore_x_pixmap_new(0, mWidth, mHeight, depth);
136 NativeImageSourceX::~NativeImageSourceX()
138 if(mOwnPixmap && mPixmap)
140 // Temporarily disable this as this causes a crash with EFL Version 1.24.0
141 //ecore_x_pixmap_free(mPixmap);
145 Any NativeImageSourceX::GetNativeImageSource() const
147 // return ecore x11 type
151 bool NativeImageSourceX::GetPixels(std::vector<uint8_t>& pixbuf, uint32_t& width, uint32_t& height, Pixel::Format& pixelFormat) const
153 DALI_ASSERT_DEBUG(sizeof(uint32_t) == 4);
154 bool success = false;
158 // Open a display connection
159 Display* displayConnection = XOpenDisplay(0);
161 XImageJanitor xImageJanitor(XGetImage(displayConnection,
164 0, // x,y of subregion to extract.
166 height, // of subregion to extract.
169 XImage* const pXImage = xImageJanitor.mXImage;
170 DALI_ASSERT_DEBUG(pXImage && "XImage (from pixmap) could not be retrieved from the server");
173 DALI_LOG_ERROR("Could not retrieve Ximage.\n");
177 switch(pXImage->depth)
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
186 pixelFormat = Pixel::RGB888;
187 pixbuf.resize(width * height * 3);
188 uint8_t* bufPtr = &pixbuf[0];
190 for(uint32_t y = 0; y < height; ++y)
192 for(uint32_t x = 0; x < width; ++x, bufPtr += 3)
194 const uint32_t pixel = XGetPixel(pXImage, x, y);
197 const uint32_t blue = pixel & 0xFFU;
198 const uint32_t green = (pixel >> 8) & 0xFFU;
199 const uint32_t red = (pixel >> 16) & 0xFFU;
202 *(bufPtr + 1) = green;
203 *(bufPtr + 2) = blue;
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;
221 for(uint32_t y = 0; y < height; ++y, bufPtr += width)
223 const char* const in = pXImage->data + xDataLineSkip * y;
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);
236 DALI_LOG_ERROR("XImage has null data pointer.\n");
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:
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.
249 DALI_LOG_WARNING("Pixmap has unsupported bit-depth for getting pixels: %u\n", pXImage->depth);
255 DALI_LOG_ERROR("Failed to get pixels from NativeImageSource.\n");
261 // Close the display connection
262 XCloseDisplay(displayConnection);
267 void NativeImageSourceX::SetSource(Any source)
269 mPixmap = GetPixmapFromAny(source);
273 // we don't own the pixmap
276 // find out the pixmap width / height and color depth
281 bool NativeImageSourceX::IsColorDepthSupported(Dali::NativeImageSource::ColorDepth colorDepth)
286 bool NativeImageSourceX::CreateResource()
288 mEglImageExtensions = mEglGraphics->GetImageExtensions();
289 DALI_ASSERT_DEBUG(mEglImageExtensions);
291 // if the image existed previously delete it.
292 if(mEglImageKHR != NULL)
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);
301 mEglImageKHR = mEglImageExtensions->CreateImageKHR(eglBuffer);
303 return mEglImageKHR != NULL;
306 void NativeImageSourceX::DestroyResource()
308 mEglImageExtensions->DestroyImageKHR(mEglImageKHR);
312 if(mResourceDestructionCallback)
314 mResourceDestructionCallback->Trigger();
318 uint32_t NativeImageSourceX::TargetTexture()
320 mEglImageExtensions->TargetTextureKHR(mEglImageKHR);
325 void NativeImageSourceX::PrepareTexture()
329 int NativeImageSourceX::GetPixelDepth(Dali::NativeImageSource::ColorDepth depth) const
333 case Dali::NativeImageSource::COLOR_DEPTH_DEFAULT:
335 // Get the default screen depth
336 return ecore_x_default_depth_get(ecore_x_display_get(), ecore_x_default_screen_get());
338 case Dali::NativeImageSource::COLOR_DEPTH_8:
342 case Dali::NativeImageSource::COLOR_DEPTH_16:
346 case Dali::NativeImageSource::COLOR_DEPTH_24:
350 case Dali::NativeImageSource::COLOR_DEPTH_32:
356 DALI_ASSERT_DEBUG(0 && "unknown color enum");
362 int NativeImageSourceX::GetTextureTarget() const
364 return GL_TEXTURE_2D;
367 bool NativeImageSourceX::ApplyNativeFragmentShader(std::string& shader)
372 const char* NativeImageSourceX::GetCustomSamplerTypename() const
377 Any NativeImageSourceX::GetNativeImageHandle() const
382 bool NativeImageSourceX::SourceChanged() const
387 Ecore_X_Pixmap NativeImageSourceX::GetPixmapFromAny(Any pixmap) const
394 // see if it is of type x11 pixmap
395 if(pixmap.GetType() == typeid(Pixmap))
397 // get the x pixmap type
398 Pixmap xpixmap = AnyCast<Pixmap>(pixmap);
400 // cast it to a ecore pixmap type
401 return static_cast<Ecore_X_Pixmap>(xpixmap);
405 return AnyCast<Ecore_X_Pixmap>(pixmap);
409 void NativeImageSourceX::GetPixmapDetails()
413 // get the width, height and depth
414 ecore_x_pixmap_geometry_get(mPixmap, &x, &y, reinterpret_cast<int*>(&mWidth), reinterpret_cast<int*>(&mHeight));
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);
425 uint8_t* NativeImageSourceX::AcquireBuffer(uint32_t& width, uint32_t& height, uint32_t& stride)
430 bool NativeImageSourceX::ReleaseBuffer(const Rect<uint32_t>& updatedArea)
435 void NativeImageSourceX::SetResourceDestructionCallback(EventThreadCallback* callback)
437 mResourceDestructionCallback = std::unique_ptr<EventThreadCallback>(callback);
440 void NativeImageSourceX::EnableBackBuffer(bool enable)
444 } // namespace Adaptor
446 } // namespace Internal