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