[dali_1.0.4] 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
32 // Allow this to be encoded and saved:
33 #include <platform-abstractions/slp/resource-loader/resource-loader.h>
34 #include <platform-abstractions/slp/resource-loader/loader-jpeg.h>
35 #include <platform-abstractions/slp/resource-loader/loader-png.h>
36
37 namespace Dali
38 {
39
40 namespace Internal
41 {
42
43 namespace Adaptor
44 {
45 using Dali::Integration::PixelBuffer;
46
47 // Pieces needed to save compressed images (temporary location while plumbing):
48 namespace
49 {
50
51   /**
52    * Free an allocated XImage on destruction.
53    */
54   struct XImageJanitor
55   {
56     XImageJanitor( XImage* const  pXImage ) : mXImage( pXImage )
57     {
58       DALI_ASSERT_DEBUG(pXImage != 0 && "Null pointer to XImage.");
59     }
60
61     ~XImageJanitor()
62     {
63       if( mXImage )
64       {
65         if( !XDestroyImage(mXImage) )
66         {
67           DALI_ASSERT_DEBUG("XImage deallocation failure");
68         }
69       }
70     }
71     XImage* const  mXImage;
72   private:
73     XImageJanitor( const XImageJanitor& rhs );
74     XImageJanitor& operator = ( const XImageJanitor& rhs );
75   };
76
77   /**
78    * Simple function to tell intended image file format from filename
79    */
80   FileFormat GetFormatFromFileName( const std::string& filename )
81   {
82     if (filename.length() < 5)
83     {
84       DALI_LOG_WARNING("Invalid (short) filename.");
85     }
86     FileFormat format(INVALID_FORMAT);
87
88     const std::size_t filenameSize = filename.length();
89
90     if(filenameSize >= 4){ // Avoid throwing out_of_range or failing silently if exceptions are turned-off on the compare(). (http://www.cplusplus.com/reference/string/string/compare/)
91       if( !filename.compare( filenameSize - 4, 4, ".jpg" )
92        || !filename.compare( filenameSize - 4, 4, ".JPG" ) )
93       {
94         format = JPG_FORMAT;
95       }
96       else if( !filename.compare( filenameSize - 4, 4, ".png" )
97             || !filename.compare( filenameSize - 4, 4, ".PNG" ) )
98       {
99         format = PNG_FORMAT;
100       }
101       else if( !filename.compare( filenameSize - 4, 4, ".bmp" )
102             || !filename.compare( filenameSize - 4, 4, ".BMP" ) )
103       {
104         format = BMP_FORMAT;
105       }
106       else if( !filename.compare( filenameSize - 4, 4, ".gif" )
107             || !filename.compare( filenameSize - 4, 4, ".GIF" ) )
108       {
109         format = GIF_FORMAT;
110       }
111       else if( !filename.compare( filenameSize - 4, 4, ".ico" )
112             || !filename.compare( filenameSize - 4, 4, ".ICO" ) )
113       {
114         format = ICO_FORMAT;
115       }
116       else if(filenameSize >= 5){
117         if( !filename.compare( filenameSize - 5, 5, ".jpeg" )
118          || !filename.compare( filenameSize - 5, 5, ".JPEG" ) )
119         {
120           format = JPG_FORMAT;
121         }
122       }
123     }
124
125     return format;
126   }
127
128   bool EncodeToFormat( const PixelBuffer* pixelBuffer, std::vector< unsigned char >& encodedPixels, FileFormat formatEncoding, std::size_t width, std::size_t height, Pixel::Format pixelFormat )
129   {
130     switch( formatEncoding )
131     {
132       case JPG_FORMAT:
133       {
134         return SlpPlatform::EncodeToJpeg( pixelBuffer, encodedPixels, width, height, pixelFormat );
135         break;
136       }
137       case PNG_FORMAT:
138       {
139         return SlpPlatform::EncodeToPng( pixelBuffer, encodedPixels, width, height, pixelFormat );
140         break;
141       }
142       default:
143       {
144         DALI_LOG_ERROR("Format not supported for image encoding (supported formats are PNG and JPEG)");
145         break;
146       }
147     }
148     return false;
149   }
150
151   bool EncodeToFile(const PixelBuffer * const pixelBuffer, const std::string& filename, const Pixel::Format pixelFormat, const std::size_t width, const std::size_t height)
152   {
153     DALI_ASSERT_DEBUG(pixelBuffer != 0 && filename.size() > 4 && width > 0 && height > 0);
154     std::vector< unsigned char > pixbufEncoded;
155     const FileFormat format = GetFormatFromFileName( filename );
156     const bool encodeResult = EncodeToFormat( pixelBuffer, pixbufEncoded, format, width, height, pixelFormat );
157     if(!encodeResult)
158     {
159       DALI_LOG_ERROR("Encoding pixels failed");
160       return false;
161     }
162     return SlpPlatform::ResourceLoader::SaveFile( filename, pixbufEncoded );
163   }
164 }
165
166 PixmapImage* PixmapImage::New(unsigned int width, unsigned int height, Dali::PixmapImage::ColorDepth depth, Dali::Adaptor& adaptor,  Any pixmap )
167 {
168   PixmapImage* image = new PixmapImage( width, height, depth, adaptor, pixmap );
169   DALI_ASSERT_DEBUG( image && "PixmapImage allocation failed." );
170
171   // 2nd phase construction
172   if(image) //< Defensive in case we ever compile without exceptions.
173   {
174     image->Initialize();
175   }
176
177   return image;
178 }
179
180 PixmapImage::PixmapImage(unsigned int width, unsigned int height, Dali::PixmapImage::ColorDepth depth, Dali::Adaptor& adaptor, Any pixmap)
181 : mWidth(width),
182   mHeight(height),
183   mOwnPixmap(true),
184   mPixmap(0),
185   mDisplay(NULL),
186   mPixelFormat(Pixel::RGB888),
187   mColorDepth(depth),
188   mAdaptor(Internal::Adaptor::Adaptor::GetImplementation(adaptor)),
189   mEglImageKHR(NULL)
190 {
191   // assign the pixmap
192   mPixmap = GetPixmapFromAny(pixmap);
193 }
194
195 void PixmapImage::Initialize()
196 {
197   // Get render-surface being used by Dali
198   Dali::RenderSurface& surface = mAdaptor.GetSurface();
199
200   // get the X11 display pointer and store it
201   // it is used by eglCreateImageKHR, and XFreePixmap
202   // Any other display (x-connection) will fail in eglCreateImageKHR
203   Any display = surface.GetDisplay();
204
205   // the dali display pointer is needed
206   mDisplay = AnyCast<Ecore_X_Display*>(display);
207
208   // if pixmap has been created outside of X11 Image we can return
209   if (mPixmap)
210   {
211     // we don't own the pixmap
212     mOwnPixmap = false;
213
214     // find out the pixmap width / height and color depth
215     GetPixmapDetails();
216     return;
217   }
218
219   // get the pixel depth
220   int depth = GetPixelDepth(mColorDepth);
221
222   // set the pixel format
223   SetPixelFormat(depth);
224
225   // Get the X-Renderable for which the pixmap is created on
226   Any renderableSurface =  surface.GetSurface();
227
228   // if Dali is using a Pixmap or Window to render to it doesn't matter because they have the same
229   // underlying type of unsigned long
230   Ecore_X_Window daliWindow = AnyCast< Ecore_X_Window >(renderableSurface);
231
232   mPixmap = ecore_x_pixmap_new(daliWindow, mWidth, mHeight, depth);
233   ecore_x_sync();
234 }
235
236 PixmapImage::~PixmapImage()
237 {
238   // Lost the opportunity to call GlExtensionDestroy() if Adaptor is destroyed first
239   if( Adaptor::IsAvailable() )
240   {
241     // GlExtensionDestroy() called from GLCleanup on the render thread. Checking this is done here.
242     // (mEglImageKHR is now read/written from different threads although ref counted destruction
243     //  should mean this isnt concurrent)
244     DALI_ASSERT_ALWAYS( NULL == mEglImageKHR && "NativeImage GL resources have not been properly cleaned up" );
245   }
246
247   if (mOwnPixmap && mPixmap)
248   {
249     ecore_x_pixmap_free(mPixmap);
250   }
251 }
252
253 Any PixmapImage::GetPixmap(Dali::PixmapImage::PixmapAPI api) const
254 {
255   if (api == Dali::PixmapImage::ECORE_X11)
256   {
257     // return ecore x11 type
258     return Any(mPixmap);
259   }
260   else
261   {
262     // return x11 type after casting it
263     Pixmap pixmap=  static_cast<Pixmap>(mPixmap);
264     return Any(pixmap);
265   }
266 }
267
268 Any PixmapImage::GetDisplay() const
269 {
270   // return ecore x11 type
271   return Any(mDisplay);
272 }
273
274 bool PixmapImage::GetPixels(std::vector<unsigned char>& pixbuf, unsigned& width, unsigned& height, Pixel::Format& pixelFormat) const
275 {
276   DALI_ASSERT_DEBUG(sizeof(unsigned) == 4);
277   bool success = false;
278   width  = mWidth;
279   height = mHeight;
280
281   XImageJanitor xImageJanitor( XGetImage(static_cast<Display*>(mDisplay),
282                                mPixmap,
283                                0, 0, // x,y of subregion to extract.
284                                width, height, // of suregion to extract.
285                                0xFFFFFFFF,
286                                ZPixmap));
287   XImage* const  pXImage = xImageJanitor.mXImage;
288   DALI_ASSERT_DEBUG(pXImage && "XImage (from pixmap) could not be retrieved from the server");
289   if(!pXImage)
290   {
291     DALI_LOG_ERROR("Could not retrieve Ximage.");
292   }
293   else
294   {
295     switch(pXImage->depth)
296     {
297       // Note, depth is a logical value. On target the framebuffer is still 32bpp
298       // (see pXImage->bits_per_pixel) so we go through XGetPixel() and swizzle.
299       // Note, this could be the default, fallback case for all depths if *pXImage
300       // didn't have blank RGB masks (X bug), but we have to hardcode the masks and
301       // shifts instead.
302       case 24:
303       {
304         pixelFormat = Pixel::RGB888;
305         pixbuf.resize(width*height*3);
306         unsigned char* bufPtr = &pixbuf[0];
307
308         for(unsigned y = height-1; y < height; --y)
309         {
310           for(unsigned x = 0; x < width; ++x, bufPtr+=3)
311           {
312             const unsigned pixel = XGetPixel(pXImage,x,y);
313
314             // store as RGB
315             const unsigned blue  =  pixel & 0xFFU;
316             const unsigned green = (pixel >> 8)  & 0xFFU;
317             const unsigned red   = (pixel >> 16) & 0xFFU;
318
319             *bufPtr = red;
320             *(bufPtr+1) = green;
321             *(bufPtr+2) = blue;
322           }
323         }
324         success = true;
325         break;
326       }
327       case 32:
328       {
329         if(pXImage->data)
330         {
331           // Sweep through the image, doing a vertical flip, but handling each scanline as
332           // an inlined intrinsic/builtin memcpy (should be fast):
333           pixbuf.resize(width*height*4);
334           unsigned * bufPtr = reinterpret_cast<unsigned *>(&pixbuf[0]);
335           const unsigned xDataLineSkip = pXImage->bytes_per_line;
336           const size_t copy_count = width * 4;
337           pixelFormat = Pixel::BGRA8888;
338
339           for(unsigned y = height-1; y < height; --y, bufPtr += width)
340           {
341             const char * const in = pXImage->data + xDataLineSkip * y;
342
343             // Copy a whole scanline at a time:
344             DALI_ASSERT_DEBUG( size_t( bufPtr ) >= size_t( &pixbuf[0] ));
345             DALI_ASSERT_DEBUG( reinterpret_cast<size_t>( bufPtr ) + copy_count <= reinterpret_cast<size_t>( &pixbuf[pixbuf.size()] ) );
346             DALI_ASSERT_DEBUG( in >= pXImage->data );
347             DALI_ASSERT_DEBUG( in + copy_count <= pXImage->data + xDataLineSkip * height );
348             __builtin_memcpy( bufPtr, in, copy_count );
349           }
350           success = true;
351         }
352         else
353         {
354           DALI_LOG_ERROR("XImage has null data pointer.");
355         }
356         break;
357       }
358       // Make a case for 16 bit modes especially to remember that the only reason we don't support them is a bug in X:
359       case 16:
360       {
361         DALI_ASSERT_DEBUG(pXImage->red_mask && pXImage->green_mask && pXImage->blue_mask && "No image masks mean 16 bit modes are not possible.");
362         ///! If the above assert doesn't fail in a debug build, the X bug may have been fixed, so revisit this function.
363         ///! No break, fall through to the general unsupported format warning below.
364       }
365       default:
366       {
367         DALI_LOG_WARNING("Pixmap has unsupported bit-depth for getting pixels: %u", pXImage->depth);
368       }
369     }
370   }
371   if(!success)
372   {
373     DALI_LOG_ERROR("Failed to get pixels from PixmapImage.");
374     pixbuf.resize(0);
375     width = 0;
376     height = 0;
377   }
378   return success;
379 }
380
381 bool PixmapImage::EncodeToFile(const std::string& filename) const
382 {
383   std::vector< unsigned char > pixbuf;
384   unsigned int width(0), height(0);
385   Pixel::Format pixelFormat;
386
387   if(GetPixels(pixbuf, width, height, pixelFormat))
388   {
389     return Dali::Internal::Adaptor::EncodeToFile(&pixbuf[0], filename, pixelFormat, width, height);
390   }
391   return false;
392 }
393
394 bool PixmapImage::GlExtensionCreate()
395 {
396   // if the image existed previously delete it.
397   if (mEglImageKHR != NULL)
398   {
399     GlExtensionDestroy();
400   }
401
402   EglImageExtensions* eglImageExtensions = GetEglImageExtensions();
403
404   // casting from an unsigned int to a void *, which should then be cast back
405   // to an unsigned int in the driver.
406   EGLClientBuffer eglBuffer = reinterpret_cast< EGLClientBuffer > (mPixmap);
407
408   mEglImageKHR = eglImageExtensions->CreateImageKHR( eglBuffer );
409
410   return mEglImageKHR != NULL;
411 }
412
413 void PixmapImage::GlExtensionDestroy()
414 {
415   EglImageExtensions* eglImageExtensions = GetEglImageExtensions();
416
417   eglImageExtensions->DestroyImageKHR(mEglImageKHR);
418
419   mEglImageKHR = NULL;
420 }
421
422 unsigned int PixmapImage::TargetTexture()
423 {
424   EglImageExtensions* eglImageExtensions = GetEglImageExtensions();
425
426   eglImageExtensions->TargetTextureKHR(mEglImageKHR);
427
428   return 0;
429 }
430
431 int PixmapImage::GetPixelDepth(Dali::PixmapImage::ColorDepth depth) const
432 {
433   switch (depth)
434   {
435     case Dali::PixmapImage::COLOR_DEPTH_DEFAULT:
436     {
437       // Get the default screen depth
438       return ecore_x_default_depth_get(ecore_x_display_get(), ecore_x_default_screen_get());
439     }
440     case Dali::PixmapImage::COLOR_DEPTH_8:
441     {
442       return 8;
443     }
444     case Dali::PixmapImage::COLOR_DEPTH_16:
445     {
446       return 16;
447     }
448     case Dali::PixmapImage::COLOR_DEPTH_24:
449     {
450       return 24;
451     }
452     case Dali::PixmapImage::COLOR_DEPTH_32:
453     {
454       return 32;
455     }
456     default:
457     {
458       DALI_ASSERT_DEBUG(0 && "unknown color enum");
459       return 0;
460     }
461   }
462 }
463
464 void PixmapImage::SetPixelFormat(int depth)
465 {
466   // store the pixel format based on the depth
467   switch (depth)
468   {
469     case 8:
470     {
471       mPixelFormat = Pixel::A8;
472       break;
473     }
474     case 16:
475     {
476       mPixelFormat = Pixel::RGB565;
477       break;
478     }
479     case 32:
480     {
481       mPixelFormat = Pixel::RGBA8888;
482       break;
483     }
484     case 24:
485     default:
486     {
487       mPixelFormat = Pixel::RGB888;
488       break;
489     }
490   }
491 }
492
493 Ecore_X_Pixmap PixmapImage::GetPixmapFromAny(Any pixmap) const
494 {
495   if (pixmap.Empty())
496   {
497     return 0;
498   }
499
500   // see if it is of type x11 pixmap
501   if (pixmap.GetType() == typeid (Pixmap))
502   {
503     // get the x pixmap type
504     Pixmap xpixmap = AnyCast<Pixmap>(pixmap);
505
506     // cast it to a ecore pixmap type
507     return static_cast<Ecore_X_Pixmap>(xpixmap);
508   }
509   else
510   {
511     return AnyCast<Ecore_X_Pixmap>(pixmap);
512   }
513 }
514
515 void PixmapImage::GetPixmapDetails()
516 {
517   int x, y;
518
519   // get the width, height and depth
520   ecore_x_pixmap_geometry_get(mPixmap, &x, &y, (int*)&mWidth, (int*)&mHeight);
521
522   // set the pixel format
523   SetPixelFormat(ecore_x_pixmap_depth_get(mPixmap));
524 }
525
526 EglImageExtensions* PixmapImage::GetEglImageExtensions() const
527 {
528   EglFactory& factory = mAdaptor.GetEGLFactory();
529   EglImageExtensions* egl = factory.GetImageExtensions();
530   DALI_ASSERT_DEBUG( egl && "EGL Image Extensions not initialized" );
531   return egl;
532 }
533
534 } // namespace Adaptor
535
536 } // namespace internal
537
538 } // namespace Dali