Bitmap adaptor patch 1 of 1 - Replace all uses of the Bitmap class with new simpler...
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / slp / resource-loader / resource-thread-image.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 #include "resource-thread-image.h"
18 #include <dali/public-api/common/ref-counted-dali-vector.h>
19 #include <dali/integration-api/image-data.h>
20 #include <dali/integration-api/debug.h>
21 #include <dali/integration-api/resource-cache.h>
22
23 #include "loader-bmp.h"
24 #include "loader-gif.h"
25 #include "loader-jpeg.h"
26 #include "loader-png.h"
27 #include "loader-ico.h"
28 #include "loader-ktx.h"
29 #include "loader-wbmp.h"
30
31 using namespace std;
32 using namespace Dali::Integration;
33 using boost::mutex;
34 using boost::unique_lock;
35
36 namespace Dali
37 {
38
39 namespace SlpPlatform
40 {
41
42 namespace
43 {
44
45 typedef bool (*LoadBitmapFunction)(FILE*, ImageAttributes&, ImageDataPtr&);
46 typedef bool (*LoadBitmapHeaderFunction)(FILE*, const ImageAttributes& attrs, unsigned int& width, unsigned int& height );
47
48 /**
49  * Stores the magic bytes, and the loader and header functions used for each image loader.
50  */
51 struct BitmapLoader
52 {
53   unsigned char magicByte1;        ///< The first byte in the file should be this
54   unsigned char magicByte2;        ///< The second byte in the file should be this
55   LoadBitmapFunction loader;       ///< The function which decodes the file
56   LoadBitmapHeaderFunction header; ///< The function which decodes the header of the file
57 };
58
59 /**
60  * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
61  */
62 enum FileFormats
63 {
64   // Unknown file format
65   FORMAT_UNKNOWN = -1,
66
67   // formats that use magic bytes
68   FORMAT_PNG = 0,
69   FORMAT_JPEG,
70   FORMAT_BMP,
71   FORMAT_GIF,
72   FORMAT_KTX,
73   FORMAT_ICO,
74   FORMAT_MAGIC_BYTE_COUNT,
75
76   // formats after this one do not use magic bytes
77   FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
78   FORMAT_TOTAL_COUNT
79 };
80
81 /**
82  * A lookup table containing all the bitmap loaders with the appropriate information.
83  * Has to be in sync with enum FileFormats
84  */
85 const BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
86 {
87   { Png::MAGIC_BYTE_1,  Png::MAGIC_BYTE_2,  LoadBitmapFromPng,  LoadPngHeader  },
88   { Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadJpegHeader },
89   { Bmp::MAGIC_BYTE_1,  Bmp::MAGIC_BYTE_2,  LoadBitmapFromBmp,  LoadBmpHeader  },
90   { Gif::MAGIC_BYTE_1,  Gif::MAGIC_BYTE_2,  LoadBitmapFromGif,  LoadGifHeader  },
91   { Ktx::MAGIC_BYTE_1,  Ktx::MAGIC_BYTE_2,  LoadBitmapFromKtx,  LoadKtxHeader  },
92   { Ico::MAGIC_BYTE_1,  Ico::MAGIC_BYTE_2,  LoadBitmapFromIco,  LoadIcoHeader  },
93   { 0x0,                0x0,                LoadBitmapFromWbmp, LoadWbmpHeader },
94 };
95
96 const unsigned int MAGIC_LENGTH = 2;
97
98 /**
99  * This code tries to predict the file format from the filename to help with format picking.
100  */
101 struct FormatExtension
102 {
103   const std::string extension;
104   FileFormats format;
105 };
106
107 const FormatExtension FORMAT_EXTENSIONS[] =
108 {
109  { ".png",  FORMAT_PNG  },
110  { ".jpg",  FORMAT_JPEG },
111  { ".bmp",  FORMAT_BMP  },
112  { ".gif",  FORMAT_GIF  },
113  { ".ktx",  FORMAT_KTX  },
114  { ".ico",  FORMAT_ICO  },
115  { ".wbmp", FORMAT_WBMP }
116 };
117
118 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
119
120 FileFormats GetFormatHint( const std::string& filename )
121 {
122   FileFormats format = FORMAT_UNKNOWN;
123
124   for ( unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i )
125   {
126     unsigned int length = FORMAT_EXTENSIONS[i].extension.size();
127     if ( ( filename.size() > length ) &&
128          ( 0 == filename.compare( filename.size() - length, length, FORMAT_EXTENSIONS[i].extension ) ) )
129     {
130       format = FORMAT_EXTENSIONS[i].format;
131       break;
132     }
133   }
134
135   return format;
136 }
137
138 /**
139  * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
140  * bitmap.
141  * @param[in]   fp      The file to decode
142  * @param[in]   format  Hint about what format to try first
143  * @param[out]  loader  Set with the function to use to decode the image
144  * @param[out]  header  Set with the function to use to decode the header
145  * @return true, if we can decode the image, false otherwise
146  */
147 bool GetBitmapLoaderFunctions( FILE *fp,
148                                FileFormats format,
149                                LoadBitmapFunction& loader,
150                                LoadBitmapHeaderFunction& header )
151 {
152   unsigned char magic[MAGIC_LENGTH];
153   size_t read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
154
155   // Reset to the start of the file.
156   if( fseek(fp, 0, SEEK_SET) )
157   {
158     DALI_LOG_ERROR("Error seeking to start of file\n");
159   }
160
161   if (read != MAGIC_LENGTH)
162   {
163     return false;
164   }
165
166   bool loaderFound = false;
167   const BitmapLoader *lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
168   ImageAttributes attrs;
169
170   // try hinted format first
171   if ( format != FORMAT_UNKNOWN )
172   {
173     lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
174     if ( format >= FORMAT_MAGIC_BYTE_COUNT ||
175        ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] ) )
176     {
177       unsigned int width = 0;
178       unsigned int height = 0;
179       loaderFound = lookupPtr->header(fp, attrs, width, height );
180     }
181   }
182
183   // then try to get a match with formats that have magic bytes
184   if ( false == loaderFound )
185   {
186     for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
187           lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
188           ++lookupPtr )
189     {
190       if ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] )
191       {
192         // to seperate ico file format and wbmp file format
193         unsigned int width = 0;
194         unsigned int height = 0;
195         loaderFound = lookupPtr->header(fp, attrs, width, height);
196       }
197       if (loaderFound)
198       {
199         break;
200       }
201     }
202   }
203
204   // finally try formats that do not use magic bytes
205   if ( false == loaderFound )
206   {
207     for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
208           lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
209           ++lookupPtr )
210     {
211       // to seperate ico file format and wbmp file format
212       unsigned int width = 0;
213       unsigned int height = 0;
214       loaderFound = lookupPtr->header(fp, attrs, width, height);
215       if (loaderFound)
216       {
217         break;
218       }
219     }
220   }
221
222   // if a loader was found set the outputs
223   if ( loaderFound )
224   {
225     loader  = lookupPtr->loader;
226     header  = lookupPtr->header;
227   }
228
229   // Reset to the start of the file.
230   if( fseek(fp, 0, SEEK_SET) )
231   {
232     DALI_LOG_ERROR("Error seeking to start of file\n");
233   }
234
235   return loaderFound;
236 }
237
238 }
239
240 ResourceThreadImage::ResourceThreadImage(ResourceLoader& resourceLoader)
241 : ResourceThreadBase(resourceLoader)
242 {
243 }
244
245 ResourceThreadImage::~ResourceThreadImage()
246 {
247 }
248
249 ResourcePointer ResourceThreadImage::LoadResourceSynchronously( const Integration::ResourceType& resourceType, const std::string& resourcePath )
250 {
251   ResourcePointer resource;
252   ImageDataPtr bitmap = 0;
253
254   FILE * const fp = fopen( resourcePath.c_str(), "rb" );
255   if( fp != NULL )
256   {
257     bool result = ConvertStreamToBitmap( resourceType, resourcePath, fp, bitmap );
258     if( result && bitmap )
259     {
260       resource.Reset(bitmap.Get());
261     }
262   }
263   return resource;
264 }
265
266
267 void ResourceThreadImage::GetClosestImageSize( const std::string& filename,
268                                                const ImageAttributes& attributes,
269                                                Vector2 &closestSize )
270 {
271   FILE *fp = fopen(filename.c_str(), "rb");
272   if (fp != NULL)
273   {
274     LoadBitmapFunction loaderFunction;
275     LoadBitmapHeaderFunction headerFunction;
276
277     if ( GetBitmapLoaderFunctions( fp,
278                                    GetFormatHint(filename),
279                                    loaderFunction,
280                                    headerFunction ) )
281     {
282       unsigned int width;
283       unsigned int height;
284
285       const bool read_res = headerFunction(fp, attributes, width, height);
286       if(!read_res)
287       {
288         DALI_LOG_WARNING("Image Decoder failed to read header for %s\n", filename.c_str());
289       }
290
291       closestSize.width = (float)width;
292       closestSize.height = (float)height;
293     }
294     else
295     {
296       DALI_LOG_WARNING("Image Decoder for %s unavailable\n", filename.c_str());
297     }
298     fclose(fp);
299   }
300 }
301
302
303 void ResourceThreadImage::GetClosestImageSize( ResourcePointer resourceBuffer,
304                                                const ImageAttributes& attributes,
305                                                Vector2 &closestSize )
306 {
307   // Get the blob of binary data that we need to decode:
308   DALI_ASSERT_DEBUG( resourceBuffer );
309   Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( resourceBuffer.Get() );
310
311   if( encodedBlob != 0 )
312   {
313     const size_t blobSize     = encodedBlob->GetVector().Size();
314     uint8_t * const blobBytes = &(encodedBlob->GetVector()[0]);
315     DALI_ASSERT_DEBUG( blobSize > 0U );
316     DALI_ASSERT_DEBUG( blobBytes != 0U );
317
318     if( blobBytes != 0 && blobSize > 0U )
319     {
320       // Open a file handle on the memory buffer:
321       FILE * const fp = fmemopen(blobBytes, blobSize, "rb");
322       if ( fp != NULL )
323       {
324         LoadBitmapFunction loaderFunction;
325         LoadBitmapHeaderFunction headerFunction;
326
327         if ( GetBitmapLoaderFunctions( fp,
328                                        FORMAT_UNKNOWN,
329                                        loaderFunction,
330                                        headerFunction ) )
331         {
332           unsigned int width;
333           unsigned int height;
334           const bool read_res = headerFunction(fp, attributes, width, height);
335           if(!read_res)
336           {
337             DALI_LOG_WARNING("Image Decoder failed to read header for resourceBuffer\n");
338           }
339
340           closestSize.width = (float) width;
341           closestSize.height = (float) height;
342         }
343         fclose(fp);
344       }
345     }
346   }
347 }
348
349
350 //----------------- Called from separate thread (mThread) -----------------
351
352 void ResourceThreadImage::Load(const ResourceRequest& request)
353 {
354   DALI_LOG_TRACE_METHOD( mLogFilter );
355   DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str() );
356
357   bool file_not_found = false;
358   ImageDataPtr bitmap;
359   bool result = false;
360
361   FILE * const fp = fopen( request.GetPath().c_str(), "rb" );
362   if( fp != NULL )
363   {
364     result = ConvertStreamToBitmap( *request.GetType(), request.GetPath(), fp, bitmap );
365     HandleConversionResult( request, result, bitmap,  request.GetPath().c_str() );
366   }
367   else
368   {
369     DALI_LOG_WARNING( "Failed to open file to load \"%s\"\n", request.GetPath().c_str() );
370     file_not_found = true;
371   }
372
373   if ( !bitmap )
374   {
375     if( file_not_found )
376     {
377       FailedResource resource(request.GetId(), FailureFileNotFound  );
378       mResourceLoader.AddFailedLoad(resource);
379     }
380     else
381     {
382       FailedResource resource(request.GetId(), FailureUnknown);
383       mResourceLoader.AddFailedLoad(resource);
384     }
385   }
386 }
387
388 void ResourceThreadImage::Decode(const ResourceRequest& request)
389 {
390   DALI_LOG_TRACE_METHOD( mLogFilter );
391   DALI_LOG_INFO(mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str());
392
393   ImageDataPtr bitmap = 0;
394
395   // Get the blob of binary data that we need to decode:
396   DALI_ASSERT_DEBUG( request.GetResource() );
397
398   DALI_ASSERT_DEBUG( 0 != dynamic_cast<Dali::RefCountedVector<uint8_t>*>( request.GetResource().Get() ) && "Only blobs of binary data can be decoded." );
399   Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( request.GetResource().Get() );
400
401   if( encodedBlob != 0 )
402   {
403     const size_t blobSize     = encodedBlob->GetVector().Size();
404     uint8_t * const blobBytes = &(encodedBlob->GetVector()[0]);
405     DALI_ASSERT_DEBUG( blobSize > 0U );
406     DALI_ASSERT_DEBUG( blobBytes != 0U );
407
408     if( blobBytes != 0 && blobSize > 0U )
409     {
410       // Open a file handle on the memory buffer:
411       FILE * const fp = fmemopen(blobBytes, blobSize, "rb");
412       if ( fp != NULL )
413       {
414         bool result = ConvertStreamToBitmap( *request.GetType(), request.GetPath(), fp, bitmap );
415         HandleConversionResult( request, result, bitmap, "(image data supplied as in-memory encoded buffer)" );
416       }
417     }
418   }
419
420   if (!bitmap)
421   {
422     FailedResource resource(request.GetId(), FailureUnknown);
423     mResourceLoader.AddFailedLoad(resource);
424   }
425 }
426
427 void ResourceThreadImage::Save(const Integration::ResourceRequest& request)
428 {
429   DALI_LOG_TRACE_METHOD( mLogFilter );
430   DALI_ASSERT_DEBUG( request.GetType()->id == ResourceImageData );
431   DALI_LOG_WARNING( "Image saving not supported on background resource threads." );
432 }
433
434
435 bool ResourceThreadImage::ConvertStreamToBitmap(const ResourceType& resourceType, std::string path, FILE * const fp, ImageDataPtr& ptr)
436 {
437   DALI_LOG_TRACE_METHOD(mLogFilter);
438   DALI_ASSERT_DEBUG( ResourceImageData == resourceType.id );
439
440   bool result = false;
441   ImageDataPtr bitmap = 0;
442
443   if (fp != NULL)
444   {
445     LoadBitmapFunction function;
446     LoadBitmapHeaderFunction header;
447
448     if ( GetBitmapLoaderFunctions( fp,
449                                    GetFormatHint( path ),
450                                    function,
451                                    header ) )
452     {
453       const ImageResourceType& resType = static_cast<const ImageResourceType&>(resourceType);
454       ImageAttributes attributes  = resType.imageAttributes;
455
456       result = function(fp, attributes, bitmap);
457       DALI_LOG_SET_OBJECT_STRING(bitmap, path);
458
459       if (!result)
460       {
461         DALI_LOG_WARNING("Unable to convert %s\n", path.c_str());
462         bitmap = 0;
463       }
464     }
465     else
466     {
467       DALI_LOG_WARNING("Image Decoder for %s unavailable\n", path.c_str());
468     }
469     fclose(fp); ///! Not exception safe, but an exception on a resource thread will bring the process down anyway.
470   }
471
472   ptr.Reset( bitmap.Get() );
473   return result;
474 }
475
476 namespace
477 {
478 bool IsAlphaChannelUsed(const uint8_t * const pixelBuffer, const unsigned width, const unsigned height, const Pixel::Format pixelFormat)
479 {
480   bool alphaChannelUsed = false;
481
482   if(pixelBuffer != NULL)
483   {
484     const uint8_t* row = pixelBuffer;
485
486     int byte; int bits;
487     Pixel::GetAlphaOffsetAndMask(pixelFormat, byte, bits);
488     const unsigned bytesPerPixel = Pixel::GetBytesPerPixel( pixelFormat );
489     const unsigned stride       = width * bytesPerPixel;
490
491     for(size_t j=0; j < height; j++)
492     {
493       const uint8_t* pixels = row;
494       for(unsigned i=0; i < width; ++i)
495       {
496         if((pixels[byte] & bits) != bits)
497         {
498           alphaChannelUsed = true;
499           j = height; // break out of outer loop
500           break;
501         }
502         pixels += bytesPerPixel;
503       }
504       row += stride;
505     }
506   }
507   return alphaChannelUsed;
508 }
509 }
510
511 void ResourceThreadImage::HandleConversionResult( const Integration::ResourceRequest& request, bool result, Integration::ImageDataPtr imageData, const char * const msg )
512 {
513   if( result && imageData )
514   {
515     // Scan the pixels of the ImageData to see if its alpha channel is used:
516     if( Pixel::HasAlpha( imageData->pixelFormat ) )
517     {
518       const uint8_t* const pixelBuffer = imageData->GetBuffer();
519       if( pixelBuffer )
520       {
521         const bool alphaUsed = IsAlphaChannelUsed( pixelBuffer, imageData->imageWidth, imageData->imageHeight, imageData->pixelFormat );
522         imageData->SetAlphaUsed( alphaUsed );
523       }
524     }
525
526     // Construct LoadedResource and ResourcePointer for image data
527     LoadedResource resource( request.GetId(), request.GetType()->id, Integration::ResourcePointer( imageData.Get() ) );
528     // Queue the loaded resource
529     mResourceLoader.AddLoadedResource( resource );
530   }
531   else
532   {
533     DALI_LOG_WARNING( "Unable to decode: %s\n",  msg);
534   }
535 }
536
537 } // namespace SlpPlatform
538
539 } // namespace Dali