2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 // http://floralicense.org/license/
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.
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>
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"
32 using namespace Dali::Integration;
34 using boost::unique_lock;
45 typedef bool (*LoadBitmapFunction)(FILE*, Bitmap&, ImageAttributes&);
46 typedef bool (*LoadBitmapHeaderFunction)(FILE*, const ImageAttributes& attrs, unsigned int& width, unsigned int& height );
49 * Stores the magic bytes, and the loader and header functions used for each image loader.
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).
62 * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
66 // Unknown file format
69 // formats that use magic bytes
76 FORMAT_MAGIC_BYTE_COUNT,
78 // formats after this one do not use magic bytes
79 FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
84 * A lookup table containing all the bitmap loaders with the appropriate information.
85 * Has to be in sync with enum FileFormats
87 const BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
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 },
98 const unsigned int MAGIC_LENGTH = 2;
101 * This code tries to predict the file format from the filename to help with format picking.
103 struct FormatExtension
105 const std::string extension;
109 const FormatExtension FORMAT_EXTENSIONS[] =
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 }
120 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
122 FileFormats GetFormatHint( const std::string& filename )
124 FileFormats format = FORMAT_UNKNOWN;
126 for ( unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i )
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 ) ) )
132 format = FORMAT_EXTENSIONS[i].format;
141 * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
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
150 bool GetBitmapLoaderFunctions( FILE *fp,
152 LoadBitmapFunction& loader,
153 LoadBitmapHeaderFunction& header,
154 Bitmap::Profile& profile )
156 unsigned char magic[MAGIC_LENGTH];
157 size_t read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
159 // Reset to the start of the file.
160 if( fseek(fp, 0, SEEK_SET) )
162 DALI_LOG_ERROR("Error seeking to start of file\n");
165 if (read != MAGIC_LENGTH)
170 bool loaderFound = false;
171 const BitmapLoader *lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
172 ImageAttributes attrs;
174 // try hinted format first
175 if ( format != FORMAT_UNKNOWN )
177 lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
178 if ( format >= FORMAT_MAGIC_BYTE_COUNT ||
179 ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] ) )
181 unsigned int width = 0;
182 unsigned int height = 0;
183 loaderFound = lookupPtr->header(fp, attrs, width, height );
187 // then try to get a match with formats that have magic bytes
188 if ( false == loaderFound )
190 for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
191 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
194 if ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] )
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);
208 // finally try formats that do not use magic bytes
209 if ( false == loaderFound )
211 for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
212 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
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);
226 // if a loader was found set the outputs
229 loader = lookupPtr->loader;
230 header = lookupPtr->header;
231 profile = lookupPtr->profile;
234 // Reset to the start of the file.
235 if( fseek(fp, 0, SEEK_SET) )
237 DALI_LOG_ERROR("Error seeking to start of file\n");
245 ResourceThreadImage::ResourceThreadImage(ResourceLoader& resourceLoader)
246 : ResourceThreadBase(resourceLoader)
250 ResourceThreadImage::~ResourceThreadImage()
254 void ResourceThreadImage::GetClosestImageSize( const std::string& filename,
255 const ImageAttributes& attributes,
256 Vector2 &closestSize )
258 FILE *fp = fopen(filename.c_str(), "rb");
261 LoadBitmapFunction loaderFunction;
262 LoadBitmapHeaderFunction headerFunction;
263 Bitmap::Profile profile;
265 if ( GetBitmapLoaderFunctions( fp,
266 GetFormatHint(filename),
274 const bool read_res = headerFunction(fp, attributes, width, height);
277 DALI_LOG_WARNING("Image Decoder failed to read header for %s\n", filename.c_str());
280 closestSize.width = (float)width;
281 closestSize.height = (float)height;
285 DALI_LOG_WARNING("Image Decoder for %s unavailable\n", filename.c_str());
292 void ResourceThreadImage::GetClosestImageSize( ResourcePointer resourceBuffer,
293 const ImageAttributes& attributes,
294 Vector2 &closestSize )
296 BitmapPtr bitmap = 0;
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() );
302 if( encodedBlob != 0 )
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 );
309 if( blobBytes != 0 && blobSize > 0U )
311 // Open a file handle on the memory buffer:
312 FILE * const fp = fmemopen(blobBytes, blobSize, "rb");
315 LoadBitmapFunction loaderFunction;
316 LoadBitmapHeaderFunction headerFunction;
317 Bitmap::Profile profile;
319 if ( GetBitmapLoaderFunctions( fp,
327 const bool read_res = headerFunction(fp, attributes, width, height);
330 DALI_LOG_WARNING("Image Decoder failed to read header for resourceBuffer\n");
333 closestSize.width = (float) width;
334 closestSize.height = (float) height;
343 //----------------- Called from separate thread (mThread) -----------------
345 void ResourceThreadImage::Load(const ResourceRequest& request)
347 DALI_LOG_TRACE_METHOD( mLogFilter );
348 DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str() );
350 bool file_not_found = false;
351 BitmapPtr bitmap = 0;
353 FILE * const fp = fopen( request.GetPath().c_str(), "rb" );
356 bitmap = ConvertStreamToBitmap( request, fp );
359 DALI_LOG_WARNING( "Unable to decode %s\n", request.GetPath().c_str() );
364 DALI_LOG_WARNING( "Failed to open file to load \"%s\"\n", request.GetPath().c_str() );
365 file_not_found = true;
372 FailedResource resource(request.GetId(), FailureFileNotFound );
373 mResourceLoader.AddFailedLoad(resource);
377 FailedResource resource(request.GetId(), FailureUnknown);
378 mResourceLoader.AddFailedLoad(resource);
383 void ResourceThreadImage::Decode(const ResourceRequest& request)
385 DALI_LOG_TRACE_METHOD( mLogFilter );
386 DALI_LOG_INFO(mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str());
388 BitmapPtr bitmap = 0;
390 // Get the blob of binary data that we need to decode:
391 DALI_ASSERT_DEBUG( request.GetResource() );
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() );
396 if( encodedBlob != 0 )
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 );
403 if( blobBytes != 0 && blobSize > 0U )
405 // Open a file handle on the memory buffer:
406 FILE * const fp = fmemopen(blobBytes, blobSize, "rb");
409 bitmap = ConvertStreamToBitmap(request, fp);
413 DALI_LOG_WARNING( "Unable to decode bitmap supplied as in-memory blob.\n" );
420 FailedResource resource(request.GetId(), FailureUnknown);
421 mResourceLoader.AddFailedLoad(resource);
425 void ResourceThreadImage::Save(const Integration::ResourceRequest& request)
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." );
433 BitmapPtr ResourceThreadImage::ConvertStreamToBitmap(const ResourceRequest& request, FILE * const fp)
435 DALI_LOG_TRACE_METHOD(mLogFilter);
436 DALI_ASSERT_DEBUG( request.GetType() && ResourceBitmap == request.GetType()->id );
439 BitmapPtr bitmap = 0;
440 std::string path = request.GetPath();
444 // Only png, jpg, bmp, gif, and compressed-data-containing ktx files are supported.
445 LoadBitmapFunction function;
446 LoadBitmapHeaderFunction header;
447 Bitmap::Profile profile;
449 if ( GetBitmapLoaderFunctions( fp,
450 GetFormatHint( request.GetPath() ),
455 bitmap = Bitmap::New(profile, true);
457 DALI_LOG_SET_OBJECT_STRING(bitmap, request.GetPath());
458 BitmapResourceType& resType = static_cast<BitmapResourceType&>(*(request.GetType()));
459 ImageAttributes& attributes = resType.imageAttributes;
461 result = function(fp, *bitmap, attributes);
465 DALI_LOG_WARNING("Unable to decode %s\n", path.c_str());
471 DALI_LOG_WARNING("Image Decoder for %s unavailable\n", path.c_str());
473 fclose(fp); ///! Not exception safe, but an exception on a resource thread will bring the process down anyway.
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 );
487 } // namespace SlpPlatform