Fix native image capture code and DALi image saving
[platform/core/uifw/dali-adaptor.git] / adaptors / tizen / native-image-source-impl-tizen.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 "native-image-source-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/gl-defines.h>
24 #include <cstring>
25 #include <tbm_surface_internal.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 <render-surface.h>
32
33 // Allow this to be encoded and saved:
34 #include <platform-abstractions/tizen/resource-loader/resource-loader.h>
35 #include <bitmap-saver.h>
36
37 namespace Dali
38 {
39
40 namespace Internal
41 {
42
43 namespace Adaptor
44 {
45
46 namespace
47 {
48 const char* FRAGMENT_PREFIX = "#extension GL_OES_EGL_image_external:require\n";
49 const char* SAMPLER_TYPE = "samplerExternalOES";
50
51 tbm_format FORMATS_BLENDING_REQUIRED[] = {
52   TBM_FORMAT_ARGB4444, TBM_FORMAT_ABGR4444,
53   TBM_FORMAT_RGBA4444, TBM_FORMAT_BGRA4444,
54   TBM_FORMAT_RGBX5551, TBM_FORMAT_BGRX5551,
55   TBM_FORMAT_ARGB1555, TBM_FORMAT_ABGR1555,
56   TBM_FORMAT_RGBA5551, TBM_FORMAT_BGRA5551,
57   TBM_FORMAT_ARGB8888, TBM_FORMAT_ABGR8888,
58   TBM_FORMAT_RGBA8888, TBM_FORMAT_BGRA8888,
59   TBM_FORMAT_ARGB2101010, TBM_FORMAT_ABGR2101010,
60   TBM_FORMAT_RGBA1010102, TBM_FORMAT_BGRA1010102
61 };
62
63 const int NUM_FORMATS_BLENDING_REQUIRED = 18;
64
65 }
66
67 using Dali::Integration::PixelBuffer;
68
69 NativeImageSource* NativeImageSource::New(unsigned int width, unsigned int height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource )
70 {
71   NativeImageSource* image = new NativeImageSource( width, height, depth, nativeImageSource );
72   DALI_ASSERT_DEBUG( image && "NativeImageSource allocation failed." );
73
74   if( image )
75   {
76     image->Initialize();
77   }
78
79   return image;
80 }
81
82 NativeImageSource::NativeImageSource( unsigned int width, unsigned int height, Dali::NativeImageSource::ColorDepth depth, Any nativeImageSource )
83 : mWidth( width ),
84   mHeight( height ),
85   mOwnTbmSurface( false ),
86   mTbmSurface( NULL ),
87   mTbmFormat( 0 ),
88   mBlendingRequired( false ),
89   mColorDepth( depth ),
90   mEglImageKHR( NULL ),
91   mEglImageExtensions( NULL ),
92   mSetSource( false )
93 {
94   DALI_ASSERT_ALWAYS( Adaptor::IsAvailable() );
95   EglFactory& eglFactory = Adaptor::GetImplementation( Adaptor::Get() ).GetEGLFactory();
96   mEglImageExtensions = eglFactory.GetImageExtensions();
97   DALI_ASSERT_DEBUG( mEglImageExtensions );
98
99   mTbmSurface = GetSurfaceFromAny( nativeImageSource );
100
101   if( mTbmSurface != NULL )
102   {
103     tbm_surface_internal_ref( mTbmSurface );
104     mBlendingRequired = CheckBlending( tbm_surface_get_format( mTbmSurface ) );
105     mWidth = tbm_surface_get_width( mTbmSurface );
106     mHeight = tbm_surface_get_height( mTbmSurface );
107   }
108 }
109
110 void NativeImageSource::Initialize()
111 {
112   if( mTbmSurface != NULL || mWidth == 0 || mHeight == 0 )
113   {
114     return;
115   }
116
117   tbm_format format = TBM_FORMAT_RGB888;
118   int depth = 0;
119
120   switch( mColorDepth )
121   {
122     case Dali::NativeImageSource::COLOR_DEPTH_DEFAULT:
123     {
124       format = TBM_FORMAT_RGBA8888;
125       depth = 32;
126       break;
127     }
128     case Dali::NativeImageSource::COLOR_DEPTH_8:
129     {
130       format = TBM_FORMAT_C8;
131       depth = 8;
132       break;
133     }
134     case Dali::NativeImageSource::COLOR_DEPTH_16:
135     {
136       format = TBM_FORMAT_RGB565;
137       depth = 16;
138       break;
139     }
140     case Dali::NativeImageSource::COLOR_DEPTH_24:
141     {
142       format = TBM_FORMAT_RGB888;
143       depth = 24;
144       break;
145     }
146     case Dali::NativeImageSource::COLOR_DEPTH_32:
147     {
148       format = TBM_FORMAT_RGBA8888;
149       depth = 32;
150       break;
151     }
152     default:
153     {
154       DALI_LOG_WARNING( "Wrong color depth.\n" );
155       return;
156     }
157   }
158
159   // set whether blending is required according to pixel format based on the depth
160   /* default pixel format is RGB888
161      If depth = 8, Pixel::A8;
162      If depth = 16, Pixel::RGB565;
163      If depth = 32, Pixel::RGBA8888 */
164   mBlendingRequired = ( depth == 32 || depth == 8 );
165
166   mTbmSurface = tbm_surface_create( mWidth, mHeight, format );
167   mOwnTbmSurface = true;
168 }
169
170 tbm_surface_h NativeImageSource::GetSurfaceFromAny( Any source ) const
171 {
172   if( source.Empty() )
173   {
174     return NULL;
175   }
176
177   if( source.GetType() == typeid( tbm_surface_h ) )
178   {
179     return AnyCast< tbm_surface_h >( source );
180   }
181   else
182   {
183     return NULL;
184   }
185 }
186
187 NativeImageSource::~NativeImageSource()
188 {
189   if( mOwnTbmSurface )
190   {
191     if( mTbmSurface != NULL && tbm_surface_destroy( mTbmSurface ) != TBM_SURFACE_ERROR_NONE )
192     {
193       DALI_LOG_ERROR( "Failed to destroy tbm_surface\n" );
194     }
195   }
196   else
197   {
198     if( mTbmSurface != NULL )
199     {
200       tbm_surface_internal_unref( mTbmSurface );
201     }
202   }
203 }
204
205 Any NativeImageSource::GetNativeImageSource() const
206 {
207   return Any( mTbmSurface );
208 }
209
210 bool NativeImageSource::GetPixels(std::vector<unsigned char>& pixbuf, unsigned& width, unsigned& height, Pixel::Format& pixelFormat) const
211 {
212   if( mTbmSurface != NULL )
213   {
214     tbm_surface_info_s surface_info;
215
216     if( tbm_surface_map( mTbmSurface, TBM_SURF_OPTION_READ, &surface_info) != TBM_SURFACE_ERROR_NONE )
217     {
218       DALI_LOG_ERROR( "Fail to map tbm_surface\n" );
219
220       width = 0;
221       height = 0;
222
223       return false;
224     }
225
226     tbm_format format = surface_info.format;
227     uint32_t stride = surface_info.planes[0].stride;
228     unsigned char* ptr = surface_info.planes[0].ptr;
229
230     width = mWidth;
231     height = mHeight;
232     size_t lineSize;
233     size_t offset;
234     size_t cOffset;
235
236     switch( format )
237     {
238       case TBM_FORMAT_RGB888:
239       {
240         lineSize = width*3;
241         pixelFormat = Pixel::RGB888;
242         pixbuf.resize( lineSize*height );
243         unsigned char* bufptr = &pixbuf[0];
244
245         for( unsigned int r = 0; r < height; ++r, bufptr += lineSize )
246         {
247           for( unsigned int c = 0; c < width; ++c )
248           {
249             cOffset = c*3;
250             offset = cOffset + r*stride;
251             *(bufptr+cOffset) = ptr[offset+2];
252             *(bufptr+cOffset+1) = ptr[offset+1];
253             *(bufptr+cOffset+2) = ptr[offset];
254           }
255         }
256         break;
257       }
258       case TBM_FORMAT_RGBA8888:
259       {
260         lineSize = width*4;
261         pixelFormat = Pixel::RGBA8888;
262         pixbuf.resize( lineSize*height );
263         unsigned char* bufptr = &pixbuf[0];
264
265         for( unsigned int r = 0; r < height; ++r, bufptr += lineSize )
266         {
267           for( unsigned int c = 0; c < width; ++c )
268           {
269             cOffset = c*4;
270             offset = cOffset + r*stride;
271             *(bufptr+cOffset) = ptr[offset+3];
272             *(bufptr+cOffset+1) = ptr[offset+2];
273             *(bufptr+cOffset+2) = ptr[offset+1];
274             *(bufptr+cOffset+3) = ptr[offset];
275           }
276         }
277         break;
278       }
279       default:
280       {
281         DALI_ASSERT_ALWAYS( 0 && "Tbm surface has unsupported pixel format.\n" );
282
283         return false;
284       }
285     }
286
287     if( tbm_surface_unmap( mTbmSurface ) != TBM_SURFACE_ERROR_NONE )
288     {
289       DALI_LOG_ERROR( "Fail to unmap tbm_surface\n" );
290     }
291
292     return true;
293   }
294
295   DALI_LOG_WARNING( "TBM surface does not exist.\n" );
296
297   width = 0;
298   height = 0;
299
300   return false;
301 }
302
303 bool NativeImageSource::EncodeToFile(const std::string& filename) const
304 {
305   std::vector< unsigned char > pixbuf;
306   unsigned int width(0), height(0);
307   Pixel::Format pixelFormat;
308
309   if(GetPixels(pixbuf, width, height, pixelFormat))
310   {
311     return Dali::EncodeToFile(&pixbuf[0], filename, pixelFormat, width, height);
312   }
313   return false;
314 }
315
316 void NativeImageSource::SetSource( Any source )
317 {
318   if( mOwnTbmSurface )
319   {
320     if( mTbmSurface != NULL && tbm_surface_destroy( mTbmSurface ) != TBM_SURFACE_ERROR_NONE )
321     {
322       DALI_LOG_ERROR( "Failed to destroy tbm_surface\n" );
323     }
324
325     mTbmSurface = NULL;
326     mOwnTbmSurface = false;
327   }
328   else
329   {
330     if( mTbmSurface != NULL )
331     {
332       tbm_surface_internal_unref( mTbmSurface );
333       mTbmSurface = NULL;
334     }
335   }
336
337   mTbmSurface = GetSurfaceFromAny( source );
338
339   if( mTbmSurface != NULL )
340   {
341     mSetSource = true;
342     tbm_surface_internal_ref( mTbmSurface );
343     mBlendingRequired = CheckBlending( tbm_surface_get_format( mTbmSurface ) );
344     mWidth = tbm_surface_get_width( mTbmSurface );
345     mHeight = tbm_surface_get_height( mTbmSurface );
346   }
347 }
348
349 bool NativeImageSource::IsColorDepthSupported( Dali::NativeImageSource::ColorDepth colorDepth )
350 {
351   uint32_t* formats;
352   uint32_t formatNum;
353   tbm_format format = TBM_FORMAT_RGB888;
354
355   switch( colorDepth )
356   {
357     case Dali::NativeImageSource::COLOR_DEPTH_DEFAULT:
358     {
359       format = TBM_FORMAT_RGBA8888;
360       break;
361     }
362     case Dali::NativeImageSource::COLOR_DEPTH_8:
363     {
364       format = TBM_FORMAT_C8;
365       break;
366     }
367     case Dali::NativeImageSource::COLOR_DEPTH_16:
368     {
369       format = TBM_FORMAT_RGB565;
370       break;
371     }
372     case Dali::NativeImageSource::COLOR_DEPTH_24:
373     {
374       format = TBM_FORMAT_RGB888;
375       break;
376     }
377     case Dali::NativeImageSource::COLOR_DEPTH_32:
378     {
379       format = TBM_FORMAT_RGBA8888;
380       break;
381     }
382   }
383
384   if( tbm_surface_query_formats( &formats, &formatNum ) )
385   {
386     for( unsigned int i = 0; i < formatNum; i++ )
387     {
388       if( formats[i] == format )
389       {
390         free( formats );
391         return true;
392       }
393     }
394   }
395
396   free( formats );
397   return false;
398 }
399
400 bool NativeImageSource::GlExtensionCreate()
401 {
402   // casting from an unsigned int to a void *, which should then be cast back
403   // to an unsigned int in the driver.
404   EGLClientBuffer eglBuffer = reinterpret_cast< EGLClientBuffer >(mTbmSurface);
405   if( !eglBuffer )
406   {
407     return false;
408   }
409
410   mEglImageKHR = mEglImageExtensions->CreateImageKHR( eglBuffer );
411
412   return mEglImageKHR != NULL;
413 }
414
415 void NativeImageSource::GlExtensionDestroy()
416 {
417   if( mEglImageKHR )
418   {
419     mEglImageExtensions->DestroyImageKHR(mEglImageKHR);
420
421     mEglImageKHR = NULL;
422   }
423 }
424
425 unsigned int NativeImageSource::TargetTexture()
426 {
427   mEglImageExtensions->TargetTextureKHR(mEglImageKHR);
428
429   return 0;
430 }
431
432 void NativeImageSource::PrepareTexture()
433 {
434   if( mSetSource )
435   {
436     void* eglImage = mEglImageKHR;
437
438     if( GlExtensionCreate() )
439     {
440       TargetTexture();
441     }
442
443     mEglImageExtensions->DestroyImageKHR( eglImage );
444
445     mSetSource = false;
446   }
447 }
448
449 int NativeImageSource::GetPixelDepth(Dali::NativeImageSource::ColorDepth depth) const
450 {
451   switch (depth)
452   {
453     case Dali::NativeImageSource::COLOR_DEPTH_DEFAULT:
454     {
455       // ToDo: Get the default screen depth
456       return 32;
457     }
458     case Dali::NativeImageSource::COLOR_DEPTH_8:
459     {
460       return 8;
461     }
462     case Dali::NativeImageSource::COLOR_DEPTH_16:
463     {
464       return 16;
465     }
466     case Dali::NativeImageSource::COLOR_DEPTH_24:
467     {
468       return 24;
469     }
470     case Dali::NativeImageSource::COLOR_DEPTH_32:
471     {
472       return 32;
473     }
474     default:
475     {
476       DALI_ASSERT_DEBUG(0 && "unknown color enum");
477       return 0;
478     }
479   }
480 }
481
482 const char* NativeImageSource::GetCustomFragmentPreFix()
483 {
484   return FRAGMENT_PREFIX;
485 }
486
487 const char* NativeImageSource::GetCustomSamplerTypename()
488 {
489   return SAMPLER_TYPE;
490 }
491
492 int NativeImageSource::GetEglImageTextureTarget()
493 {
494   return GL_TEXTURE_EXTERNAL_OES;
495 }
496
497 bool NativeImageSource::CheckBlending( tbm_format format )
498 {
499   if( mTbmFormat != format )
500   {
501     for(int i = 0; i < NUM_FORMATS_BLENDING_REQUIRED; ++i)
502     {
503       if( format == FORMATS_BLENDING_REQUIRED[i] )
504       {
505         mBlendingRequired = true;
506         break;
507       }
508     }
509     mTbmFormat = format;
510   }
511
512   return mBlendingRequired;
513 }
514
515 } // namespace Adaptor
516
517 } // namespace internal
518
519 } // namespace Dali