2 * Copyright (c) 2020 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>
22 #include <dali/internal/system/linux/dali-ecore-x.h>
23 #include <X11/Xutil.h>
25 #include <dali/integration-api/debug.h>
28 #include <dali/internal/graphics/common/egl-image-extensions.h>
29 #include <dali/internal/graphics/gles/egl-graphics.h>
30 #include <dali/internal/adaptor/common/adaptor-impl.h>
31 #include <dali/devel-api/adaptor-framework/bitmap-saver.h>
32 #include <dali/integration-api/adaptor-framework/render-surface-interface.h>
42 using Dali::Integration::PixelBuffer;
44 // Pieces needed to save compressed images (temporary location while plumbing):
49 * Free an allocated XImage on destruction.
53 XImageJanitor( XImage* const pXImage ) : mXImage( pXImage )
55 DALI_ASSERT_DEBUG(pXImage != 0 && "Null pointer to XImage.");
62 if( !XDestroyImage(mXImage) )
64 DALI_LOG_ERROR("XImage deallocation failure");
68 XImage* const mXImage;
70 XImageJanitor( const XImageJanitor& rhs );
71 XImageJanitor& operator = ( const XImageJanitor& rhs );
75 NativeImageSourceX* NativeImageSourceX::New( uint32_t width, uint32_t height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource )
77 NativeImageSourceX* image = new NativeImageSourceX( width, height, depth, nativeImageSource );
78 DALI_ASSERT_DEBUG( image && "NativeImageSource allocation failed." );
80 // 2nd phase construction
81 if(image) //< Defensive in case we ever compile without exceptions.
89 NativeImageSourceX::NativeImageSourceX( uint32_t width, uint32_t height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource )
94 mBlendingRequired( false ),
97 mEglImageExtensions( NULL )
99 DALI_ASSERT_ALWAYS( Adaptor::IsAvailable() );
101 GraphicsInterface* graphics = &( Adaptor::GetImplementation( Adaptor::Get() ).GetGraphicsInterface() );
102 auto eglGraphics = static_cast<EglGraphics *>(graphics);
104 mEglImageExtensions = eglGraphics->GetImageExtensions();
106 DALI_ASSERT_DEBUG( mEglImageExtensions );
109 mPixmap = GetPixmapFromAny(nativeImageSource);
112 void NativeImageSourceX::Initialize()
114 // if pixmap has been created outside of X11 Image we can return
117 // we don't own the pixmap
120 // find out the pixmap width / height and color depth
125 // get the pixel depth
126 int depth = GetPixelDepth(mColorDepth);
128 // set whether blending is required according to pixel format based on the depth
129 /* default pixel format is RGB888
130 If depth = 8, Pixel::A8;
131 If depth = 16, Pixel::RGB565;
132 If depth = 32, Pixel::RGBA8888 */
133 mBlendingRequired = ( depth == 32 || depth == 8 );
135 mPixmap = ecore_x_pixmap_new( 0, mWidth, mHeight, depth );
139 NativeImageSourceX::~NativeImageSourceX()
141 if (mOwnPixmap && mPixmap)
143 ecore_x_pixmap_free(mPixmap);
147 Any NativeImageSourceX::GetNativeImageSource() const
149 // return ecore x11 type
153 bool NativeImageSourceX::GetPixels(std::vector<unsigned char>& pixbuf, unsigned& width, unsigned& height, Pixel::Format& pixelFormat) const
155 DALI_ASSERT_DEBUG(sizeof(unsigned) == 4);
156 bool success = false;
160 // Open a display connection
161 Display* displayConnection = XOpenDisplay( 0 );
163 XImageJanitor xImageJanitor( XGetImage( displayConnection,
165 0, 0, // x,y of subregion to extract.
166 width, 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 unsigned char* bufPtr = &pixbuf[0];
190 for(unsigned y = height-1; y < height; --y)
192 for(unsigned x = 0; x < width; ++x, bufPtr+=3)
194 const unsigned pixel = XGetPixel(pXImage,x,y);
197 const unsigned blue = pixel & 0xFFU;
198 const unsigned green = (pixel >> 8) & 0xFFU;
199 const unsigned red = (pixel >> 16) & 0xFFU;
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 unsigned * bufPtr = reinterpret_cast<unsigned *>(&pixbuf[0]);
217 const unsigned xDataLineSkip = pXImage->bytes_per_line;
218 const size_t copy_count = static_cast< size_t >( width ) * 4;
219 pixelFormat = Pixel::BGRA8888;
221 for(unsigned y = height-1; 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 bool NativeImageSourceX::EncodeToFile(const std::string& filename) const
269 std::vector< unsigned char > pixbuf;
270 unsigned int width(0), height(0);
271 Pixel::Format pixelFormat;
273 if(GetPixels(pixbuf, width, height, pixelFormat))
275 return Dali::EncodeToFile(&pixbuf[0], filename, pixelFormat, width, height);
280 void NativeImageSourceX::SetSource( Any source )
282 mPixmap = GetPixmapFromAny( source );
286 // we don't own the pixmap
289 // find out the pixmap width / height and color depth
294 bool NativeImageSourceX::IsColorDepthSupported( Dali::NativeImageSource::ColorDepth colorDepth )
299 bool NativeImageSourceX::GlExtensionCreate()
301 // if the image existed previously delete it.
302 if (mEglImageKHR != NULL)
304 GlExtensionDestroy();
307 // casting from an unsigned int to a void *, which should then be cast back
308 // to an unsigned int in the driver.
309 EGLClientBuffer eglBuffer = reinterpret_cast< EGLClientBuffer > (mPixmap);
311 mEglImageKHR = mEglImageExtensions->CreateImageKHR( eglBuffer );
313 return mEglImageKHR != NULL;
316 void NativeImageSourceX::GlExtensionDestroy()
318 mEglImageExtensions->DestroyImageKHR(mEglImageKHR);
323 uint32_t NativeImageSourceX::TargetTexture()
325 mEglImageExtensions->TargetTextureKHR(mEglImageKHR);
330 void NativeImageSourceX::PrepareTexture()
334 int NativeImageSourceX::GetPixelDepth(Dali::NativeImageSource::ColorDepth depth) const
338 case Dali::NativeImageSource::COLOR_DEPTH_DEFAULT:
340 // Get the default screen depth
341 return ecore_x_default_depth_get(ecore_x_display_get(), ecore_x_default_screen_get());
343 case Dali::NativeImageSource::COLOR_DEPTH_8:
347 case Dali::NativeImageSource::COLOR_DEPTH_16:
351 case Dali::NativeImageSource::COLOR_DEPTH_24:
355 case Dali::NativeImageSource::COLOR_DEPTH_32:
361 DALI_ASSERT_DEBUG(0 && "unknown color enum");
367 Ecore_X_Pixmap NativeImageSourceX::GetPixmapFromAny(Any pixmap) const
374 // see if it is of type x11 pixmap
375 if (pixmap.GetType() == typeid (Pixmap))
377 // get the x pixmap type
378 Pixmap xpixmap = AnyCast<Pixmap>(pixmap);
380 // cast it to a ecore pixmap type
381 return static_cast<Ecore_X_Pixmap>(xpixmap);
385 return AnyCast<Ecore_X_Pixmap>(pixmap);
389 void NativeImageSourceX::GetPixmapDetails()
393 // get the width, height and depth
394 ecore_x_pixmap_geometry_get( mPixmap, &x, &y, reinterpret_cast< int* >( &mWidth ), reinterpret_cast< int* >( &mHeight ) );
396 // set whether blending is required according to pixel format based on the depth
397 /* default pixel format is RGB888
398 If depth = 8, Pixel::A8;
399 If depth = 16, Pixel::RGB565;
400 If depth = 32, Pixel::RGBA8888 */
401 int depth = ecore_x_pixmap_depth_get(mPixmap);
402 mBlendingRequired = ( depth == 32 || depth == 8 );
405 uint8_t* NativeImageSourceX::AcquireBuffer( uint16_t& width, uint16_t& height, uint16_t& stride )
411 bool NativeImageSourceX::ReleaseBuffer()
416 } // namespace Adaptor
418 } // namespace internal