Revert "[3.0] Version downgrade (1.2.0 to 1.1.45)"
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / tizen / image-loaders / image-loader.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 #include "image-loader.h"
18
19 #include <dali/devel-api/common/ref-counted-dali-vector.h>
20 #include <dali/integration-api/bitmap.h>
21 #include <dali/integration-api/debug.h>
22
23 #include "loader-astc.h"
24 #include "loader-bmp.h"
25 #include "loader-gif.h"
26 #include "loader-ico.h"
27 #include "loader-jpeg.h"
28 #include "loader-ktx.h"
29 #include "loader-png.h"
30 #include "loader-wbmp.h"
31 #include "image-operations.h"
32 #include "image-loader-input.h"
33 #include "portable/file-closer.h"
34
35 using namespace Dali::Integration;
36
37 namespace Dali
38 {
39 namespace TizenPlatform
40 {
41
42 namespace
43 {
44 typedef bool (*LoadBitmapFunction)( const ResourceLoadingClient& client, const ImageLoader::Input& input, Integration::Bitmap& bitmap );
45 typedef bool (*LoadBitmapHeaderFunction)( const ImageLoader::Input& input, unsigned int& width, unsigned int& height );
46
47 #if defined(DEBUG_ENABLED)
48 Integration::Log::Filter* gLogFilter = Debug::Filter::New( Debug::Concise, false, "LOG_IMAGE_LOADING" );
49 #endif
50
51 /**
52  * Stores the magic bytes, and the loader and header functions used for each image loader.
53  */
54 struct BitmapLoader
55 {
56   unsigned char magicByte1;        ///< The first byte in the file should be this
57   unsigned char magicByte2;        ///< The second byte in the file should be this
58   LoadBitmapFunction loader;       ///< The function which decodes the file
59   LoadBitmapHeaderFunction header; ///< The function which decodes the header of the file
60   Bitmap::Profile profile;         ///< The kind of bitmap to be created
61                                    ///  (addressable packed pixels or an opaque compressed blob).
62 };
63
64 /**
65  * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
66  */
67 enum FileFormats
68 {
69   // Unknown file format
70   FORMAT_UNKNOWN = -1,
71
72   // formats that use magic bytes
73   FORMAT_PNG = 0,
74   FORMAT_JPEG,
75   FORMAT_BMP,
76   FORMAT_GIF,
77   FORMAT_KTX,
78   FORMAT_ASTC,
79   FORMAT_ICO,
80   FORMAT_MAGIC_BYTE_COUNT,
81
82   // formats after this one do not use magic bytes
83   FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
84   FORMAT_TOTAL_COUNT
85 };
86
87 /**
88  * A lookup table containing all the bitmap loaders with the appropriate information.
89  * Has to be in sync with enum FileFormats
90  */
91 const BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
92 {
93   { Png::MAGIC_BYTE_1,  Png::MAGIC_BYTE_2,  LoadBitmapFromPng,  LoadPngHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
94   { Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
95   { Bmp::MAGIC_BYTE_1,  Bmp::MAGIC_BYTE_2,  LoadBitmapFromBmp,  LoadBmpHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
96   { Gif::MAGIC_BYTE_1,  Gif::MAGIC_BYTE_2,  LoadBitmapFromGif,  LoadGifHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
97   { Ktx::MAGIC_BYTE_1,  Ktx::MAGIC_BYTE_2,  LoadBitmapFromKtx,  LoadKtxHeader,  Bitmap::BITMAP_COMPRESSED       },
98   { Astc::MAGIC_BYTE_1, Astc::MAGIC_BYTE_2, LoadBitmapFromAstc, LoadAstcHeader, Bitmap::BITMAP_COMPRESSED       },
99   { Ico::MAGIC_BYTE_1,  Ico::MAGIC_BYTE_2,  LoadBitmapFromIco,  LoadIcoHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
100   { 0x0,                0x0,                LoadBitmapFromWbmp, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
101 };
102
103 const unsigned int MAGIC_LENGTH = 2;
104
105 /**
106  * This code tries to predict the file format from the filename to help with format picking.
107  */
108 struct FormatExtension
109 {
110   const std::string extension;
111   FileFormats format;
112 };
113
114 const FormatExtension FORMAT_EXTENSIONS[] =
115 {
116  { ".png",  FORMAT_PNG  },
117  { ".jpg",  FORMAT_JPEG },
118  { ".bmp",  FORMAT_BMP  },
119  { ".gif",  FORMAT_GIF  },
120  { ".ktx",  FORMAT_KTX  },
121  { ".astc", FORMAT_ASTC },
122  { ".ico",  FORMAT_ICO  },
123  { ".wbmp", FORMAT_WBMP }
124 };
125
126 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
127
128 FileFormats GetFormatHint( const std::string& filename )
129 {
130   FileFormats format = FORMAT_UNKNOWN;
131
132   for ( unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i )
133   {
134     unsigned int length = FORMAT_EXTENSIONS[i].extension.size();
135     if ( ( filename.size() > length ) &&
136          ( 0 == filename.compare( filename.size() - length, length, FORMAT_EXTENSIONS[i].extension ) ) )
137     {
138       format = FORMAT_EXTENSIONS[i].format;
139       break;
140     }
141   }
142
143   return format;
144 }
145
146 /**
147  * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
148  * bitmap.
149  * @param[in]   fp      The file to decode
150  * @param[in]   format  Hint about what format to try first
151  * @param[out]  loader  Set with the function to use to decode the image
152  * @param[out]  header  Set with the function to use to decode the header
153  * @param[out]  profile The kind of bitmap to hold the bits loaded for the bitmap.
154  * @return true, if we can decode the image, false otherwise
155  */
156 bool GetBitmapLoaderFunctions( FILE *fp,
157                                FileFormats format,
158                                LoadBitmapFunction& loader,
159                                LoadBitmapHeaderFunction& header,
160                                Bitmap::Profile& profile )
161 {
162   unsigned char magic[MAGIC_LENGTH];
163   size_t read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
164
165   // Reset to the start of the file.
166   if( fseek(fp, 0, SEEK_SET) )
167   {
168     DALI_LOG_ERROR("Error seeking to start of file\n");
169   }
170
171   if (read != MAGIC_LENGTH)
172   {
173     return false;
174   }
175
176   bool loaderFound = false;
177   const BitmapLoader *lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
178   ImageLoader::Input defaultInput( fp );
179
180   // try hinted format first
181   if ( format != FORMAT_UNKNOWN )
182   {
183     lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
184     if ( format >= FORMAT_MAGIC_BYTE_COUNT ||
185          ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] ) )
186     {
187       unsigned int width = 0;
188       unsigned int height = 0;
189       loaderFound = lookupPtr->header( fp, width, height );
190     }
191   }
192
193   // then try to get a match with formats that have magic bytes
194   if ( false == loaderFound )
195   {
196     for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
197           lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
198           ++lookupPtr )
199     {
200       if ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] )
201       {
202         // to seperate ico file format and wbmp file format
203         unsigned int width = 0;
204         unsigned int height = 0;
205         loaderFound = lookupPtr->header(fp, width, height);
206       }
207       if (loaderFound)
208       {
209         break;
210       }
211     }
212   }
213
214   // finally try formats that do not use magic bytes
215   if ( false == loaderFound )
216   {
217     for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
218           lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
219           ++lookupPtr )
220     {
221       // to seperate ico file format and wbmp file format
222       unsigned int width = 0;
223       unsigned int height = 0;
224       loaderFound = lookupPtr->header(fp, width, height);
225       if (loaderFound)
226       {
227         break;
228       }
229     }
230   }
231
232   // if a loader was found set the outputs
233   if ( loaderFound )
234   {
235     loader  = lookupPtr->loader;
236     header  = lookupPtr->header;
237     profile = lookupPtr->profile;
238   }
239
240   // Reset to the start of the file.
241   if( fseek(fp, 0, SEEK_SET) )
242   {
243     DALI_LOG_ERROR("Error seeking to start of file\n");
244   }
245
246   return loaderFound;
247 }
248
249 } // anonymous namespace
250
251
252 namespace ImageLoader
253 {
254
255 bool ConvertStreamToBitmap( const ResourceType& resourceType, std::string path, FILE * const fp, const ResourceLoadingClient& client, BitmapPtr& ptr )
256 {
257   DALI_LOG_TRACE_METHOD( gLogFilter );
258   DALI_ASSERT_DEBUG( ResourceBitmap == resourceType.id );
259
260   bool result = false;
261   BitmapPtr bitmap = 0;
262
263   if (fp != NULL)
264   {
265     LoadBitmapFunction function;
266     LoadBitmapHeaderFunction header;
267     Bitmap::Profile profile;
268
269     if ( GetBitmapLoaderFunctions( fp,
270                                    GetFormatHint( path ),
271                                    function,
272                                    header,
273                                    profile ) )
274     {
275       bitmap = Bitmap::New( profile, ResourcePolicy::OWNED_DISCARD );
276
277       DALI_LOG_SET_OBJECT_STRING( bitmap, path );
278       const BitmapResourceType& resType = static_cast<const BitmapResourceType&>( resourceType );
279       const ScalingParameters scalingParameters( resType.size, resType.scalingMode, resType.samplingMode );
280       const ImageLoader::Input input( fp, scalingParameters, resType.orientationCorrection );
281
282       // Check for cancellation now we have hit the filesystem, done some allocation, and burned some cycles:
283       // This won't do anything from synchronous API, it's only useful when called from another thread.
284       client.InterruptionPoint(); // Note: By design, this can throw an exception
285
286       // Run the image type decoder:
287       result = function( client, input, *bitmap );
288
289       if (!result)
290       {
291         DALI_LOG_WARNING( "Unable to convert %s\n", path.c_str() );
292         bitmap = 0;
293       }
294
295       // Apply the requested image attributes if not interrupted:
296       client.InterruptionPoint(); // Note: By design, this can throw an exception
297       bitmap = Internal::Platform::ApplyAttributesToBitmap( bitmap, resType.size, resType.scalingMode, resType.samplingMode );
298     }
299     else
300     {
301       DALI_LOG_WARNING( "Image Decoder for %s unavailable\n", path.c_str() );
302     }
303   }
304
305   ptr.Reset( bitmap.Get() );
306   return result;
307 }
308
309 ResourcePointer LoadResourceSynchronously( const Integration::ResourceType& resourceType, const std::string& resourcePath )
310 {
311   ResourcePointer resource;
312   BitmapPtr bitmap = 0;
313
314   Internal::Platform::FileCloser fc( resourcePath.c_str(), "rb");
315   FILE * const fp = fc.GetFile();
316   if( fp != NULL )
317   {
318     bool result = ConvertStreamToBitmap( resourceType, resourcePath, fp, StubbedResourceLoadingClient(), bitmap );
319     if( result && bitmap )
320     {
321       resource.Reset(bitmap.Get());
322     }
323   }
324   return resource;
325 }
326
327 ///@ToDo: Rename GetClosestImageSize() functions. Make them use the orientation correction and scaling information. Requires jpeg loader to tell us about reorientation. [Is there still a requirement for this functionality at all?]
328 ImageDimensions  GetClosestImageSize( const std::string& filename,
329                                       ImageDimensions size,
330                                       FittingMode::Type fittingMode,
331                                       SamplingMode::Type samplingMode,
332                                       bool orientationCorrection )
333 {
334   unsigned int width = 0;
335   unsigned int height = 0;
336
337   Internal::Platform::FileCloser fc(filename.c_str(), "rb");
338   FILE *fp = fc.GetFile();
339   if (fp != NULL)
340   {
341     LoadBitmapFunction loaderFunction;
342     LoadBitmapHeaderFunction headerFunction;
343     Bitmap::Profile profile;
344
345     if ( GetBitmapLoaderFunctions( fp,
346                                    GetFormatHint(filename),
347                                    loaderFunction,
348                                    headerFunction,
349                                    profile ) )
350     {
351       const ImageLoader::Input input( fp, ScalingParameters( size, fittingMode, samplingMode ), orientationCorrection );
352
353       const bool read_res = headerFunction( input, width, height );
354       if(!read_res)
355       {
356         DALI_LOG_WARNING("Image Decoder failed to read header for %s\n", filename.c_str());
357       }
358     }
359     else
360     {
361       DALI_LOG_WARNING("Image Decoder for %s unavailable\n", filename.c_str());
362     }
363   }
364   return ImageDimensions( width, height );
365 }
366
367
368
369 ImageDimensions GetClosestImageSize( Integration::ResourcePointer resourceBuffer,
370                                      ImageDimensions size,
371                                      FittingMode::Type fittingMode,
372                                      SamplingMode::Type samplingMode,
373                                      bool orientationCorrection )
374 {
375   unsigned int width = 0;
376   unsigned int height = 0;
377
378   // Get the blob of binary data that we need to decode:
379   DALI_ASSERT_DEBUG( resourceBuffer );
380   Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( resourceBuffer.Get() );
381
382   if( encodedBlob != 0 )
383   {
384     const size_t blobSize     = encodedBlob->GetVector().Size();
385     uint8_t * const blobBytes = &(encodedBlob->GetVector()[0]);
386     DALI_ASSERT_DEBUG( blobSize > 0U );
387     DALI_ASSERT_DEBUG( blobBytes != 0U );
388
389     if( blobBytes != 0 && blobSize > 0U )
390     {
391       // Open a file handle on the memory buffer:
392       Internal::Platform::FileCloser fc( blobBytes, blobSize, "rb" );
393       FILE *fp = fc.GetFile();
394       if ( fp != NULL )
395       {
396         LoadBitmapFunction loaderFunction;
397         LoadBitmapHeaderFunction headerFunction;
398         Bitmap::Profile profile;
399
400         if ( GetBitmapLoaderFunctions( fp,
401                                        FORMAT_UNKNOWN,
402                                        loaderFunction,
403                                        headerFunction,
404                                        profile ) )
405         {
406           const ImageLoader::Input input( fp, ScalingParameters( size, fittingMode, samplingMode ), orientationCorrection );
407           const bool read_res = headerFunction( input, width, height );
408           if( !read_res )
409           {
410             DALI_LOG_WARNING( "Image Decoder failed to read header for resourceBuffer\n" );
411           }
412         }
413       }
414     }
415   }
416   return ImageDimensions( width, height );
417 }
418
419 } // ImageLoader
420 } // TizenPlatform
421 } // Dali