Removed legacy resource tracking / logging
[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/bitmap.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*, Bitmap&, ImageAttributes&);
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   Bitmap::Profile profile;         ///< The kind of bitmap to be created
58                                    ///  (addressable packed pixels or an opaque compressed blob).
59 };
60
61 /**
62  * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
63  */
64 enum FileFormats
65 {
66   // Unknown file format
67   FORMAT_UNKNOWN = -1,
68
69   // formats that use magic bytes
70   FORMAT_PNG = 0,
71   FORMAT_JPEG,
72   FORMAT_BMP,
73   FORMAT_GIF,
74   FORMAT_KTX,
75   FORMAT_ICO,
76   FORMAT_MAGIC_BYTE_COUNT,
77
78   // formats after this one do not use magic bytes
79   FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
80   FORMAT_TOTAL_COUNT
81 };
82
83 /**
84  * A lookup table containing all the bitmap loaders with the appropriate information.
85  * Has to be in sync with enum FileFormats
86  */
87 const BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
88 {
89   { Png::MAGIC_BYTE_1,  Png::MAGIC_BYTE_2,  LoadBitmapFromPng,  LoadPngHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
90   { Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
91   { Bmp::MAGIC_BYTE_1,  Bmp::MAGIC_BYTE_2,  LoadBitmapFromBmp,  LoadBmpHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
92   { Gif::MAGIC_BYTE_1,  Gif::MAGIC_BYTE_2,  LoadBitmapFromGif,  LoadGifHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
93   { Ktx::MAGIC_BYTE_1,  Ktx::MAGIC_BYTE_2,  LoadBitmapFromKtx,  LoadKtxHeader,  Bitmap::BITMAP_COMPRESSED       },
94   { Ico::MAGIC_BYTE_1,  Ico::MAGIC_BYTE_2,  LoadBitmapFromIco,  LoadIcoHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
95   { 0x0,                0x0,                LoadBitmapFromWbmp, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
96 };
97
98 const unsigned int MAGIC_LENGTH = 2;
99
100 /**
101  * This code tries to predict the file format from the filename to help with format picking.
102  */
103 struct FormatExtension
104 {
105   const std::string extension;
106   FileFormats format;
107 };
108
109 const FormatExtension FORMAT_EXTENSIONS[] =
110 {
111  { ".png",  FORMAT_PNG  },
112  { ".jpg",  FORMAT_JPEG },
113  { ".bmp",  FORMAT_BMP  },
114  { ".gif",  FORMAT_GIF  },
115  { ".ktx",  FORMAT_KTX  },
116  { ".ico",  FORMAT_ICO  },
117  { ".wbmp", FORMAT_WBMP }
118 };
119
120 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
121
122 FileFormats GetFormatHint( const std::string& filename )
123 {
124   FileFormats format = FORMAT_UNKNOWN;
125
126   for ( unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i )
127   {
128     unsigned int length = FORMAT_EXTENSIONS[i].extension.size();
129     if ( ( filename.size() > length ) &&
130          ( 0 == filename.compare( filename.size() - length, length, FORMAT_EXTENSIONS[i].extension ) ) )
131     {
132       format = FORMAT_EXTENSIONS[i].format;
133       break;
134     }
135   }
136
137   return format;
138 }
139
140 /**
141  * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
142  * bitmap.
143  * @param[in]   fp      The file to decode
144  * @param[in]   format  Hint about what format to try first
145  * @param[out]  loader  Set with the function to use to decode the image
146  * @param[out]  header  Set with the function to use to decode the header
147  * @param[out]  profile The kind of bitmap to hold the bits loaded for the bitmap.
148  * @return true, if we can decode the image, false otherwise
149  */
150 bool GetBitmapLoaderFunctions( FILE *fp,
151                                FileFormats format,
152                                LoadBitmapFunction& loader,
153                                LoadBitmapHeaderFunction& header,
154                                Bitmap::Profile& profile )
155 {
156   unsigned char magic[MAGIC_LENGTH];
157   size_t read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
158
159   // Reset to the start of the file.
160   if( fseek(fp, 0, SEEK_SET) )
161   {
162     DALI_LOG_ERROR("Error seeking to start of file\n");
163   }
164
165   if (read != MAGIC_LENGTH)
166   {
167     return false;
168   }
169
170   bool loaderFound = false;
171   const BitmapLoader *lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
172   ImageAttributes attrs;
173
174   // try hinted format first
175   if ( format != FORMAT_UNKNOWN )
176   {
177     lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
178     if ( format >= FORMAT_MAGIC_BYTE_COUNT ||
179          ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] ) )
180     {
181       unsigned int width = 0;
182       unsigned int height = 0;
183       loaderFound = lookupPtr->header(fp, attrs, width, height );
184     }
185   }
186
187   // then try to get a match with formats that have magic bytes
188   if ( false == loaderFound )
189   {
190     for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
191           lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
192           ++lookupPtr )
193     {
194       if ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] )
195       {
196         // to seperate ico file format and wbmp file format
197         unsigned int width = 0;
198         unsigned int height = 0;
199         loaderFound = lookupPtr->header(fp, attrs, width, height);
200       }
201       if (loaderFound)
202       {
203         break;
204       }
205     }
206   }
207
208   // finally try formats that do not use magic bytes
209   if ( false == loaderFound )
210   {
211     for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
212           lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
213           ++lookupPtr )
214     {
215       // to seperate ico file format and wbmp file format
216       unsigned int width = 0;
217       unsigned int height = 0;
218       loaderFound = lookupPtr->header(fp, attrs, width, height);
219       if (loaderFound)
220       {
221         break;
222       }
223     }
224   }
225
226   // if a loader was found set the outputs
227   if ( loaderFound )
228   {
229     loader  = lookupPtr->loader;
230     header  = lookupPtr->header;
231     profile = lookupPtr->profile;
232   }
233
234   // Reset to the start of the file.
235   if( fseek(fp, 0, SEEK_SET) )
236   {
237     DALI_LOG_ERROR("Error seeking to start of file\n");
238   }
239
240   return loaderFound;
241 }
242
243 }
244
245 ResourceThreadImage::ResourceThreadImage(ResourceLoader& resourceLoader)
246 : ResourceThreadBase(resourceLoader)
247 {
248 }
249
250 ResourceThreadImage::~ResourceThreadImage()
251 {
252 }
253
254 void ResourceThreadImage::GetClosestImageSize( const std::string& filename,
255                                                const ImageAttributes& attributes,
256                                                Vector2 &closestSize )
257 {
258   FILE *fp = fopen(filename.c_str(), "rb");
259   if (fp != NULL)
260   {
261     LoadBitmapFunction loaderFunction;
262     LoadBitmapHeaderFunction headerFunction;
263     Bitmap::Profile profile;
264
265     if ( GetBitmapLoaderFunctions( fp,
266                                    GetFormatHint(filename),
267                                    loaderFunction,
268                                    headerFunction,
269                                    profile ) )
270     {
271       unsigned int width;
272       unsigned int height;
273
274       const bool read_res = headerFunction(fp, attributes, width, height);
275       if(!read_res)
276       {
277         DALI_LOG_WARNING("Image Decoder failed to read header for %s\n", filename.c_str());
278       }
279
280       closestSize.width = (float)width;
281       closestSize.height = (float)height;
282     }
283     else
284     {
285       DALI_LOG_WARNING("Image Decoder for %s unavailable\n", filename.c_str());
286     }
287     fclose(fp);
288   }
289 }
290
291
292 void ResourceThreadImage::GetClosestImageSize( ResourcePointer resourceBuffer,
293                                                const ImageAttributes& attributes,
294                                                Vector2 &closestSize )
295 {
296   BitmapPtr bitmap = 0;
297
298   // Get the blob of binary data that we need to decode:
299   DALI_ASSERT_DEBUG( resourceBuffer );
300   Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( resourceBuffer.Get() );
301
302   if( encodedBlob != 0 )
303   {
304     const size_t blobSize     = encodedBlob->GetVector().Size();
305     uint8_t * const blobBytes = &(encodedBlob->GetVector()[0]);
306     DALI_ASSERT_DEBUG( blobSize > 0U );
307     DALI_ASSERT_DEBUG( blobBytes != 0U );
308
309     if( blobBytes != 0 && blobSize > 0U )
310     {
311       // Open a file handle on the memory buffer:
312       FILE * const fp = fmemopen(blobBytes, blobSize, "rb");
313       if ( fp != NULL )
314       {
315         LoadBitmapFunction loaderFunction;
316         LoadBitmapHeaderFunction headerFunction;
317         Bitmap::Profile profile;
318
319         if ( GetBitmapLoaderFunctions( fp,
320                                        FORMAT_UNKNOWN,
321                                        loaderFunction,
322                                        headerFunction,
323                                        profile ) )
324         {
325           unsigned int width;
326           unsigned int height;
327           const bool read_res = headerFunction(fp, attributes, width, height);
328           if(!read_res)
329           {
330             DALI_LOG_WARNING("Image Decoder failed to read header for resourceBuffer\n");
331           }
332
333           closestSize.width = (float) width;
334           closestSize.height = (float) height;
335         }
336         fclose(fp);
337       }
338     }
339   }
340 }
341
342
343 //----------------- Called from separate thread (mThread) -----------------
344
345 void ResourceThreadImage::Load(const ResourceRequest& request)
346 {
347   DALI_LOG_TRACE_METHOD( mLogFilter );
348   DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str() );
349
350   bool file_not_found = false;
351   BitmapPtr bitmap = 0;
352
353   FILE * const fp = fopen( request.GetPath().c_str(), "rb" );
354   if( fp != NULL )
355   {
356     bitmap = ConvertStreamToBitmap( request, fp );
357     if( !bitmap )
358     {
359       DALI_LOG_WARNING( "Unable to decode %s\n", request.GetPath().c_str() );
360     }
361   }
362   else
363   {
364     DALI_LOG_WARNING( "Failed to open file to load \"%s\"\n", request.GetPath().c_str() );
365     file_not_found = true;
366   }
367
368   if ( !bitmap )
369   {
370     if( file_not_found )
371     {
372       FailedResource resource(request.GetId(), FailureFileNotFound  );
373       mResourceLoader.AddFailedLoad(resource);
374     }
375     else
376     {
377       FailedResource resource(request.GetId(), FailureUnknown);
378       mResourceLoader.AddFailedLoad(resource);
379     }
380   }
381 }
382
383 void ResourceThreadImage::Decode(const ResourceRequest& request)
384 {
385   DALI_LOG_TRACE_METHOD( mLogFilter );
386   DALI_LOG_INFO(mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str());
387
388   BitmapPtr bitmap = 0;
389
390   // Get the blob of binary data that we need to decode:
391   DALI_ASSERT_DEBUG( request.GetResource() );
392
393   DALI_ASSERT_DEBUG( 0 != dynamic_cast<Dali::RefCountedVector<uint8_t>*>( request.GetResource().Get() ) && "Only blobs of binary data can be decoded." );
394   Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( request.GetResource().Get() );
395
396   if( encodedBlob != 0 )
397   {
398     const size_t blobSize     = encodedBlob->GetVector().Size();
399     uint8_t * const blobBytes = &(encodedBlob->GetVector()[0]);
400     DALI_ASSERT_DEBUG( blobSize > 0U );
401     DALI_ASSERT_DEBUG( blobBytes != 0U );
402
403     if( blobBytes != 0 && blobSize > 0U )
404     {
405       // Open a file handle on the memory buffer:
406       FILE * const fp = fmemopen(blobBytes, blobSize, "rb");
407       if ( fp != NULL )
408       {
409         bitmap = ConvertStreamToBitmap(request, fp);
410       }
411       if ( !bitmap )
412       {
413         DALI_LOG_WARNING( "Unable to decode bitmap supplied as in-memory blob.\n" );
414       }
415     }
416   }
417
418   if (!bitmap)
419   {
420     FailedResource resource(request.GetId(), FailureUnknown);
421     mResourceLoader.AddFailedLoad(resource);
422   }
423 }
424
425 void ResourceThreadImage::Save(const Integration::ResourceRequest& request)
426 {
427   DALI_LOG_TRACE_METHOD( mLogFilter );
428   DALI_ASSERT_DEBUG( request.GetType()->id == ResourceBitmap );
429   DALI_LOG_WARNING( "Image saving not supported on background resource threads." );
430 }
431
432
433 BitmapPtr ResourceThreadImage::ConvertStreamToBitmap(const ResourceRequest& request, FILE * const fp)
434 {
435   DALI_LOG_TRACE_METHOD(mLogFilter);
436   DALI_ASSERT_DEBUG( request.GetType() && ResourceBitmap == request.GetType()->id );
437
438   bool result = false;
439   BitmapPtr bitmap = 0;
440   std::string path = request.GetPath();
441
442   if (fp != NULL)
443   {
444     // Only png, jpg, bmp, gif, and compressed-data-containing ktx files are supported.
445     LoadBitmapFunction function;
446     LoadBitmapHeaderFunction header;
447     Bitmap::Profile profile;
448
449     if ( GetBitmapLoaderFunctions( fp,
450                                    GetFormatHint( request.GetPath() ),
451                                    function,
452                                    header,
453                                    profile ) )
454     {
455       bitmap = Bitmap::New(profile, true);
456
457       DALI_LOG_SET_OBJECT_STRING(bitmap, request.GetPath());
458       BitmapResourceType& resType = static_cast<BitmapResourceType&>(*(request.GetType()));
459       ImageAttributes& attributes  = resType.imageAttributes;
460
461       result = function(fp, *bitmap, attributes);
462
463       if (!result)
464       {
465         DALI_LOG_WARNING("Unable to decode %s\n", path.c_str());
466         bitmap = 0;
467       }
468     }
469     else
470     {
471       DALI_LOG_WARNING("Image Decoder for %s unavailable\n", path.c_str());
472     }
473     fclose(fp); ///! Not exception safe, but an exception on a resource thread will bring the process down anyway.
474   }
475
476   if ( result )
477   {
478     // Construct LoadedResource and ResourcePointer for image data
479     LoadedResource resource( request.GetId(), request.GetType()->id, ResourcePointer( bitmap.Get() ) );
480     // Queue the loaded resource
481     mResourceLoader.AddLoadedResource( resource );
482   }
483   return bitmap;
484 }
485
486
487 } // namespace SlpPlatform
488
489 } // namespace Dali