2 * Copyright (c) 2014 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 "pixmap-image-impl.h"
23 #include <X11/Xutil.h>
24 #include <dali/integration-api/debug.h>
25 #include <render-surface.h>
28 #include <gl/egl-image-extensions.h>
29 #include <gl/egl-factory.h>
30 #include <adaptor-impl.h>
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>
45 using Dali::Integration::PixelBuffer;
47 // Pieces needed to save compressed images (temporary location while plumbing):
52 * Free an allocated XImage on destruction.
56 XImageJanitor( XImage* const pXImage ) : mXImage( pXImage )
58 DALI_ASSERT_DEBUG(pXImage != 0 && "Null pointer to XImage.");
65 if( !XDestroyImage(mXImage) )
67 DALI_ASSERT_DEBUG("XImage deallocation failure");
71 XImage* const mXImage;
73 XImageJanitor( const XImageJanitor& rhs );
74 XImageJanitor& operator = ( const XImageJanitor& rhs );
78 * Simple function to tell intended image file format from filename
80 FileFormat GetFormatFromFileName( const std::string& filename )
82 if (filename.length() < 5)
84 DALI_LOG_WARNING("Invalid (short) filename.");
86 FileFormat format(INVALID_FORMAT);
88 const std::size_t filenameSize = filename.length();
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" ) )
96 else if( !filename.compare( filenameSize - 4, 4, ".png" )
97 || !filename.compare( filenameSize - 4, 4, ".PNG" ) )
101 else if( !filename.compare( filenameSize - 4, 4, ".bmp" )
102 || !filename.compare( filenameSize - 4, 4, ".BMP" ) )
106 else if( !filename.compare( filenameSize - 4, 4, ".gif" )
107 || !filename.compare( filenameSize - 4, 4, ".GIF" ) )
111 else if( !filename.compare( filenameSize - 4, 4, ".ico" )
112 || !filename.compare( filenameSize - 4, 4, ".ICO" ) )
116 else if(filenameSize >= 5){
117 if( !filename.compare( filenameSize - 5, 5, ".jpeg" )
118 || !filename.compare( filenameSize - 5, 5, ".JPEG" ) )
128 bool EncodeToFormat( const PixelBuffer* pixelBuffer, std::vector< unsigned char >& encodedPixels, FileFormat formatEncoding, std::size_t width, std::size_t height, Pixel::Format pixelFormat )
130 switch( formatEncoding )
134 return SlpPlatform::EncodeToJpeg( pixelBuffer, encodedPixels, width, height, pixelFormat );
139 return SlpPlatform::EncodeToPng( pixelBuffer, encodedPixels, width, height, pixelFormat );
144 DALI_LOG_ERROR("Format not supported for image encoding (supported formats are PNG and JPEG)");
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)
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 );
159 DALI_LOG_ERROR("Encoding pixels failed");
162 return SlpPlatform::ResourceLoader::SaveFile( filename, pixbufEncoded );
166 PixmapImage* PixmapImage::New(unsigned int width, unsigned int height, Dali::PixmapImage::ColorDepth depth, Dali::Adaptor& adaptor, Any pixmap )
168 PixmapImage* image = new PixmapImage( width, height, depth, adaptor, pixmap );
169 DALI_ASSERT_DEBUG( image && "PixmapImage allocation failed." );
171 // 2nd phase construction
172 if(image) //< Defensive in case we ever compile without exceptions.
180 PixmapImage::PixmapImage(unsigned int width, unsigned int height, Dali::PixmapImage::ColorDepth depth, Dali::Adaptor& adaptor, Any pixmap)
186 mPixelFormat(Pixel::RGB888),
188 mAdaptor(Internal::Adaptor::Adaptor::GetImplementation(adaptor)),
192 mPixmap = GetPixmapFromAny(pixmap);
195 void PixmapImage::Initialize()
197 // Get render-surface being used by Dali
198 Dali::RenderSurface& surface = mAdaptor.GetSurface();
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();
205 // the dali display pointer is needed
206 mDisplay = AnyCast<Ecore_X_Display*>(display);
208 // if pixmap has been created outside of X11 Image we can return
211 // we don't own the pixmap
214 // find out the pixmap width / height and color depth
219 // get the pixel depth
220 int depth = GetPixelDepth(mColorDepth);
222 // set the pixel format
223 SetPixelFormat(depth);
225 // Get the X-Renderable for which the pixmap is created on
226 Any renderableSurface = surface.GetSurface();
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);
232 mPixmap = ecore_x_pixmap_new(daliWindow, mWidth, mHeight, depth);
236 PixmapImage::~PixmapImage()
238 // Lost the opportunity to call GlExtensionDestroy() if Adaptor is destroyed first
239 if( Adaptor::IsAvailable() )
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" );
247 if (mOwnPixmap && mPixmap)
249 ecore_x_pixmap_free(mPixmap);
253 Any PixmapImage::GetPixmap(Dali::PixmapImage::PixmapAPI api) const
255 if (api == Dali::PixmapImage::ECORE_X11)
257 // return ecore x11 type
262 // return x11 type after casting it
263 Pixmap pixmap= static_cast<Pixmap>(mPixmap);
268 Any PixmapImage::GetDisplay() const
270 // return ecore x11 type
271 return Any(mDisplay);
274 bool PixmapImage::GetPixels(std::vector<unsigned char>& pixbuf, unsigned& width, unsigned& height, Pixel::Format& pixelFormat) const
276 DALI_ASSERT_DEBUG(sizeof(unsigned) == 4);
277 bool success = false;
281 XImageJanitor xImageJanitor( XGetImage(static_cast<Display*>(mDisplay),
283 0, 0, // x,y of subregion to extract.
284 width, height, // of suregion to extract.
287 XImage* const pXImage = xImageJanitor.mXImage;
288 DALI_ASSERT_DEBUG(pXImage && "XImage (from pixmap) could not be retrieved from the server");
291 DALI_LOG_ERROR("Could not retrieve Ximage.");
295 switch(pXImage->depth)
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
304 pixelFormat = Pixel::RGB888;
305 pixbuf.resize(width*height*3);
306 unsigned char* bufPtr = &pixbuf[0];
308 for(unsigned y = height-1; y < height; --y)
310 for(unsigned x = 0; x < width; ++x, bufPtr+=3)
312 const unsigned pixel = XGetPixel(pXImage,x,y);
315 const unsigned blue = pixel & 0xFFU;
316 const unsigned green = (pixel >> 8) & 0xFFU;
317 const unsigned red = (pixel >> 16) & 0xFFU;
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;
339 for(unsigned y = height-1; y < height; --y, bufPtr += width)
341 const char * const in = pXImage->data + xDataLineSkip * y;
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 );
354 DALI_LOG_ERROR("XImage has null data pointer.");
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:
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.
367 DALI_LOG_WARNING("Pixmap has unsupported bit-depth for getting pixels: %u", pXImage->depth);
373 DALI_LOG_ERROR("Failed to get pixels from PixmapImage.");
381 bool PixmapImage::EncodeToFile(const std::string& filename) const
383 std::vector< unsigned char > pixbuf;
384 unsigned int width(0), height(0);
385 Pixel::Format pixelFormat;
387 if(GetPixels(pixbuf, width, height, pixelFormat))
389 return Dali::Internal::Adaptor::EncodeToFile(&pixbuf[0], filename, pixelFormat, width, height);
394 bool PixmapImage::GlExtensionCreate()
396 // if the image existed previously delete it.
397 if (mEglImageKHR != NULL)
399 GlExtensionDestroy();
402 EglImageExtensions* eglImageExtensions = GetEglImageExtensions();
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);
408 mEglImageKHR = eglImageExtensions->CreateImageKHR( eglBuffer );
410 return mEglImageKHR != NULL;
413 void PixmapImage::GlExtensionDestroy()
415 EglImageExtensions* eglImageExtensions = GetEglImageExtensions();
417 eglImageExtensions->DestroyImageKHR(mEglImageKHR);
422 unsigned int PixmapImage::TargetTexture()
424 EglImageExtensions* eglImageExtensions = GetEglImageExtensions();
426 eglImageExtensions->TargetTextureKHR(mEglImageKHR);
431 int PixmapImage::GetPixelDepth(Dali::PixmapImage::ColorDepth depth) const
435 case Dali::PixmapImage::COLOR_DEPTH_DEFAULT:
437 // Get the default screen depth
438 return ecore_x_default_depth_get(ecore_x_display_get(), ecore_x_default_screen_get());
440 case Dali::PixmapImage::COLOR_DEPTH_8:
444 case Dali::PixmapImage::COLOR_DEPTH_16:
448 case Dali::PixmapImage::COLOR_DEPTH_24:
452 case Dali::PixmapImage::COLOR_DEPTH_32:
458 DALI_ASSERT_DEBUG(0 && "unknown color enum");
464 void PixmapImage::SetPixelFormat(int depth)
466 // store the pixel format based on the depth
471 mPixelFormat = Pixel::A8;
476 mPixelFormat = Pixel::RGB565;
481 mPixelFormat = Pixel::RGBA8888;
487 mPixelFormat = Pixel::RGB888;
493 Ecore_X_Pixmap PixmapImage::GetPixmapFromAny(Any pixmap) const
500 // see if it is of type x11 pixmap
501 if (pixmap.GetType() == typeid (Pixmap))
503 // get the x pixmap type
504 Pixmap xpixmap = AnyCast<Pixmap>(pixmap);
506 // cast it to a ecore pixmap type
507 return static_cast<Ecore_X_Pixmap>(xpixmap);
511 return AnyCast<Ecore_X_Pixmap>(pixmap);
515 void PixmapImage::GetPixmapDetails()
519 // get the width, height and depth
520 ecore_x_pixmap_geometry_get(mPixmap, &x, &y, (int*)&mWidth, (int*)&mHeight);
522 // set the pixel format
523 SetPixelFormat(ecore_x_pixmap_depth_get(mPixmap));
526 EglImageExtensions* PixmapImage::GetEglImageExtensions() const
528 EglFactory& factory = mAdaptor.GetEGLFactory();
529 EglImageExtensions* egl = factory.GetImageExtensions();
530 DALI_ASSERT_DEBUG( egl && "EGL Image Extensions not initialized" );
534 } // namespace Adaptor
536 } // namespace internal