[dali_1.0.7] Merge branch 'tizen'
[platform/core/uifw/dali-adaptor.git] / adaptors / x11 / pixmap-image-impl-x.cpp
1 /*
2  * Copyright (c) 2014 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 "pixmap-image-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <Ecore_X.h>
23 #include <X11/Xutil.h>
24 #include <dali/integration-api/debug.h>
25 #include <render-surface.h>
26
27 // INTERNAL INCLUDES
28 #include <gl/egl-image-extensions.h>
29 #include <gl/egl-factory.h>
30 #include <adaptor-impl.h>
31 #include <bitmap-saver.h>
32
33 namespace Dali
34 {
35
36 namespace Internal
37 {
38
39 namespace Adaptor
40 {
41 using Dali::Integration::PixelBuffer;
42
43 // Pieces needed to save compressed images (temporary location while plumbing):
44 namespace
45 {
46
47   /**
48    * Free an allocated XImage on destruction.
49    */
50   struct XImageJanitor
51   {
52     XImageJanitor( XImage* const  pXImage ) : mXImage( pXImage )
53     {
54       DALI_ASSERT_DEBUG(pXImage != 0 && "Null pointer to XImage.");
55     }
56
57     ~XImageJanitor()
58     {
59       if( mXImage )
60       {
61         if( !XDestroyImage(mXImage) )
62         {
63           DALI_ASSERT_DEBUG("XImage deallocation failure");
64         }
65       }
66     }
67     XImage* const  mXImage;
68   private:
69     XImageJanitor( const XImageJanitor& rhs );
70     XImageJanitor& operator = ( const XImageJanitor& rhs );
71   };
72 }
73
74 PixmapImage* PixmapImage::New(unsigned int width, unsigned int height, Dali::PixmapImage::ColorDepth depth, Dali::Adaptor& adaptor,  Any pixmap )
75 {
76   PixmapImage* image = new PixmapImage( width, height, depth, adaptor, pixmap );
77   DALI_ASSERT_DEBUG( image && "PixmapImage 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 PixmapImage::PixmapImage(unsigned int width, unsigned int height, Dali::PixmapImage::ColorDepth depth, Dali::Adaptor& adaptor, Any pixmap)
89 : mWidth(width),
90   mHeight(height),
91   mOwnPixmap(true),
92   mPixmap(0),
93   mDisplay(NULL),
94   mPixelFormat(Pixel::RGB888),
95   mColorDepth(depth),
96   mAdaptor(Internal::Adaptor::Adaptor::GetImplementation(adaptor)),
97   mEglImageKHR(NULL)
98 {
99   // assign the pixmap
100   mPixmap = GetPixmapFromAny(pixmap);
101 }
102
103 void PixmapImage::Initialize()
104 {
105   // Get render-surface being used by Dali
106   Dali::RenderSurface& surface = mAdaptor.GetSurface();
107
108   // get the X11 display pointer and store it
109   // it is used by eglCreateImageKHR, and XFreePixmap
110   // Any other display (x-connection) will fail in eglCreateImageKHR
111   Any display = surface.GetDisplay();
112
113   // the dali display pointer is needed
114   mDisplay = AnyCast<Ecore_X_Display*>(display);
115
116   // if pixmap has been created outside of X11 Image we can return
117   if (mPixmap)
118   {
119     // we don't own the pixmap
120     mOwnPixmap = false;
121
122     // find out the pixmap width / height and color depth
123     GetPixmapDetails();
124     return;
125   }
126
127   // get the pixel depth
128   int depth = GetPixelDepth(mColorDepth);
129
130   // set the pixel format
131   SetPixelFormat(depth);
132
133   // Get the X-Renderable for which the pixmap is created on
134   Any renderableSurface =  surface.GetSurface();
135
136   // if Dali is using a Pixmap or Window to render to it doesn't matter because they have the same
137   // underlying type of unsigned long
138   Ecore_X_Window daliWindow = AnyCast< Ecore_X_Window >(renderableSurface);
139
140   mPixmap = ecore_x_pixmap_new(daliWindow, mWidth, mHeight, depth);
141   ecore_x_sync();
142 }
143
144 PixmapImage::~PixmapImage()
145 {
146   // Lost the opportunity to call GlExtensionDestroy() if Adaptor is destroyed first
147   if( Adaptor::IsAvailable() )
148   {
149     // GlExtensionDestroy() called from GLCleanup on the render thread. Checking this is done here.
150     // (mEglImageKHR is now read/written from different threads although ref counted destruction
151     //  should mean this isnt concurrent)
152     DALI_ASSERT_ALWAYS( NULL == mEglImageKHR && "NativeImage GL resources have not been properly cleaned up" );
153   }
154
155   if (mOwnPixmap && mPixmap)
156   {
157     ecore_x_pixmap_free(mPixmap);
158   }
159 }
160
161 Any PixmapImage::GetPixmap(Dali::PixmapImage::PixmapAPI api) const
162 {
163   if (api == Dali::PixmapImage::ECORE_X11)
164   {
165     // return ecore x11 type
166     return Any(mPixmap);
167   }
168   else
169   {
170     // return x11 type after casting it
171     Pixmap pixmap=  static_cast<Pixmap>(mPixmap);
172     return Any(pixmap);
173   }
174 }
175
176 Any PixmapImage::GetDisplay() const
177 {
178   // return ecore x11 type
179   return Any(mDisplay);
180 }
181
182 bool PixmapImage::GetPixels(std::vector<unsigned char>& pixbuf, unsigned& width, unsigned& height, Pixel::Format& pixelFormat) const
183 {
184   DALI_ASSERT_DEBUG(sizeof(unsigned) == 4);
185   bool success = false;
186   width  = mWidth;
187   height = mHeight;
188
189   XImageJanitor xImageJanitor( XGetImage(static_cast<Display*>(mDisplay),
190                                mPixmap,
191                                0, 0, // x,y of subregion to extract.
192                                width, height, // of suregion to extract.
193                                0xFFFFFFFF,
194                                ZPixmap));
195   XImage* const  pXImage = xImageJanitor.mXImage;
196   DALI_ASSERT_DEBUG(pXImage && "XImage (from pixmap) could not be retrieved from the server");
197   if(!pXImage)
198   {
199     DALI_LOG_ERROR("Could not retrieve Ximage.");
200   }
201   else
202   {
203     switch(pXImage->depth)
204     {
205       // Note, depth is a logical value. On target the framebuffer is still 32bpp
206       // (see pXImage->bits_per_pixel) so we go through XGetPixel() and swizzle.
207       // Note, this could be the default, fallback case for all depths if *pXImage
208       // didn't have blank RGB masks (X bug), but we have to hardcode the masks and
209       // shifts instead.
210       case 24:
211       {
212         pixelFormat = Pixel::RGB888;
213         pixbuf.resize(width*height*3);
214         unsigned char* bufPtr = &pixbuf[0];
215
216         for(unsigned y = height-1; y < height; --y)
217         {
218           for(unsigned x = 0; x < width; ++x, bufPtr+=3)
219           {
220             const unsigned pixel = XGetPixel(pXImage,x,y);
221
222             // store as RGB
223             const unsigned blue  =  pixel & 0xFFU;
224             const unsigned green = (pixel >> 8)  & 0xFFU;
225             const unsigned red   = (pixel >> 16) & 0xFFU;
226
227             *bufPtr = red;
228             *(bufPtr+1) = green;
229             *(bufPtr+2) = blue;
230           }
231         }
232         success = true;
233         break;
234       }
235       case 32:
236       {
237         if(pXImage->data)
238         {
239           // Sweep through the image, doing a vertical flip, but handling each scanline as
240           // an inlined intrinsic/builtin memcpy (should be fast):
241           pixbuf.resize(width*height*4);
242           unsigned * bufPtr = reinterpret_cast<unsigned *>(&pixbuf[0]);
243           const unsigned xDataLineSkip = pXImage->bytes_per_line;
244           const size_t copy_count = width * 4;
245           pixelFormat = Pixel::BGRA8888;
246
247           for(unsigned y = height-1; y < height; --y, bufPtr += width)
248           {
249             const char * const in = pXImage->data + xDataLineSkip * y;
250
251             // Copy a whole scanline at a time:
252             DALI_ASSERT_DEBUG( size_t( bufPtr ) >= size_t( &pixbuf[0] ));
253             DALI_ASSERT_DEBUG( reinterpret_cast<size_t>( bufPtr ) + copy_count <= reinterpret_cast<size_t>( &pixbuf[pixbuf.size()] ) );
254             DALI_ASSERT_DEBUG( in >= pXImage->data );
255             DALI_ASSERT_DEBUG( in + copy_count <= pXImage->data + xDataLineSkip * height );
256             __builtin_memcpy( bufPtr, in, copy_count );
257           }
258           success = true;
259         }
260         else
261         {
262           DALI_LOG_ERROR("XImage has null data pointer.");
263         }
264         break;
265       }
266       // Make a case for 16 bit modes especially to remember that the only reason we don't support them is a bug in X:
267       case 16:
268       {
269         DALI_ASSERT_DEBUG(pXImage->red_mask && pXImage->green_mask && pXImage->blue_mask && "No image masks mean 16 bit modes are not possible.");
270         ///! If the above assert doesn't fail in a debug build, the X bug may have been fixed, so revisit this function.
271         ///! No break, fall through to the general unsupported format warning below.
272       }
273       default:
274       {
275         DALI_LOG_WARNING("Pixmap has unsupported bit-depth for getting pixels: %u", pXImage->depth);
276       }
277     }
278   }
279   if(!success)
280   {
281     DALI_LOG_ERROR("Failed to get pixels from PixmapImage.");
282     pixbuf.resize(0);
283     width = 0;
284     height = 0;
285   }
286   return success;
287 }
288
289 bool PixmapImage::EncodeToFile(const std::string& filename) const
290 {
291   std::vector< unsigned char > pixbuf;
292   unsigned int width(0), height(0);
293   Pixel::Format pixelFormat;
294
295   if(GetPixels(pixbuf, width, height, pixelFormat))
296   {
297     return Dali::EncodeToFile(&pixbuf[0], filename, pixelFormat, width, height);
298   }
299   return false;
300 }
301
302 bool PixmapImage::GlExtensionCreate()
303 {
304   // if the image existed previously delete it.
305   if (mEglImageKHR != NULL)
306   {
307     GlExtensionDestroy();
308   }
309
310   EglImageExtensions* eglImageExtensions = GetEglImageExtensions();
311
312   // casting from an unsigned int to a void *, which should then be cast back
313   // to an unsigned int in the driver.
314   EGLClientBuffer eglBuffer = reinterpret_cast< EGLClientBuffer > (mPixmap);
315
316   mEglImageKHR = eglImageExtensions->CreateImageKHR( eglBuffer );
317
318   return mEglImageKHR != NULL;
319 }
320
321 void PixmapImage::GlExtensionDestroy()
322 {
323   EglImageExtensions* eglImageExtensions = GetEglImageExtensions();
324
325   eglImageExtensions->DestroyImageKHR(mEglImageKHR);
326
327   mEglImageKHR = NULL;
328 }
329
330 unsigned int PixmapImage::TargetTexture()
331 {
332   EglImageExtensions* eglImageExtensions = GetEglImageExtensions();
333
334   eglImageExtensions->TargetTextureKHR(mEglImageKHR);
335
336   return 0;
337 }
338
339 int PixmapImage::GetPixelDepth(Dali::PixmapImage::ColorDepth depth) const
340 {
341   switch (depth)
342   {
343     case Dali::PixmapImage::COLOR_DEPTH_DEFAULT:
344     {
345       // Get the default screen depth
346       return ecore_x_default_depth_get(ecore_x_display_get(), ecore_x_default_screen_get());
347     }
348     case Dali::PixmapImage::COLOR_DEPTH_8:
349     {
350       return 8;
351     }
352     case Dali::PixmapImage::COLOR_DEPTH_16:
353     {
354       return 16;
355     }
356     case Dali::PixmapImage::COLOR_DEPTH_24:
357     {
358       return 24;
359     }
360     case Dali::PixmapImage::COLOR_DEPTH_32:
361     {
362       return 32;
363     }
364     default:
365     {
366       DALI_ASSERT_DEBUG(0 && "unknown color enum");
367       return 0;
368     }
369   }
370 }
371
372 void PixmapImage::SetPixelFormat(int depth)
373 {
374   // store the pixel format based on the depth
375   switch (depth)
376   {
377     case 8:
378     {
379       mPixelFormat = Pixel::A8;
380       break;
381     }
382     case 16:
383     {
384       mPixelFormat = Pixel::RGB565;
385       break;
386     }
387     case 32:
388     {
389       mPixelFormat = Pixel::RGBA8888;
390       break;
391     }
392     case 24:
393     default:
394     {
395       mPixelFormat = Pixel::RGB888;
396       break;
397     }
398   }
399 }
400
401 Ecore_X_Pixmap PixmapImage::GetPixmapFromAny(Any pixmap) const
402 {
403   if (pixmap.Empty())
404   {
405     return 0;
406   }
407
408   // see if it is of type x11 pixmap
409   if (pixmap.GetType() == typeid (Pixmap))
410   {
411     // get the x pixmap type
412     Pixmap xpixmap = AnyCast<Pixmap>(pixmap);
413
414     // cast it to a ecore pixmap type
415     return static_cast<Ecore_X_Pixmap>(xpixmap);
416   }
417   else
418   {
419     return AnyCast<Ecore_X_Pixmap>(pixmap);
420   }
421 }
422
423 void PixmapImage::GetPixmapDetails()
424 {
425   int x, y;
426
427   // get the width, height and depth
428   ecore_x_pixmap_geometry_get(mPixmap, &x, &y, (int*)&mWidth, (int*)&mHeight);
429
430   // set the pixel format
431   SetPixelFormat(ecore_x_pixmap_depth_get(mPixmap));
432 }
433
434 EglImageExtensions* PixmapImage::GetEglImageExtensions() const
435 {
436   EglFactory& factory = mAdaptor.GetEGLFactory();
437   EglImageExtensions* egl = factory.GetImageExtensions();
438   DALI_ASSERT_DEBUG( egl && "EGL Image Extensions not initialized" );
439   return egl;
440 }
441
442 } // namespace Adaptor
443
444 } // namespace internal
445
446 } // namespace Dali