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