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/image-data.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*, ImageAttributes&, ImageDataPtr&);
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
60 * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
64 // Unknown file format
67 // formats that use magic bytes
74 FORMAT_MAGIC_BYTE_COUNT,
76 // formats after this one do not use magic bytes
77 FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
82 * A lookup table containing all the bitmap loaders with the appropriate information.
83 * Has to be in sync with enum FileFormats
85 const BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
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 },
96 const unsigned int MAGIC_LENGTH = 2;
99 * This code tries to predict the file format from the filename to help with format picking.
101 struct FormatExtension
103 const std::string extension;
107 const FormatExtension FORMAT_EXTENSIONS[] =
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 }
118 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
120 FileFormats GetFormatHint( const std::string& filename )
122 FileFormats format = FORMAT_UNKNOWN;
124 for ( unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i )
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 ) ) )
130 format = FORMAT_EXTENSIONS[i].format;
139 * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
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
147 bool GetBitmapLoaderFunctions( FILE *fp,
149 LoadBitmapFunction& loader,
150 LoadBitmapHeaderFunction& header )
152 unsigned char magic[MAGIC_LENGTH];
153 size_t read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
155 // Reset to the start of the file.
156 if( fseek(fp, 0, SEEK_SET) )
158 DALI_LOG_ERROR("Error seeking to start of file\n");
161 if (read != MAGIC_LENGTH)
166 bool loaderFound = false;
167 const BitmapLoader *lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
168 ImageAttributes attrs;
170 // try hinted format first
171 if ( format != FORMAT_UNKNOWN )
173 lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
174 if ( format >= FORMAT_MAGIC_BYTE_COUNT ||
175 ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] ) )
177 unsigned int width = 0;
178 unsigned int height = 0;
179 loaderFound = lookupPtr->header(fp, attrs, width, height );
183 // then try to get a match with formats that have magic bytes
184 if ( false == loaderFound )
186 for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
187 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
190 if ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] )
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);
204 // finally try formats that do not use magic bytes
205 if ( false == loaderFound )
207 for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
208 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
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);
222 // if a loader was found set the outputs
225 loader = lookupPtr->loader;
226 header = lookupPtr->header;
229 // Reset to the start of the file.
230 if( fseek(fp, 0, SEEK_SET) )
232 DALI_LOG_ERROR("Error seeking to start of file\n");
240 ResourceThreadImage::ResourceThreadImage(ResourceLoader& resourceLoader)
241 : ResourceThreadBase(resourceLoader)
245 ResourceThreadImage::~ResourceThreadImage()
249 ResourcePointer ResourceThreadImage::LoadResourceSynchronously( const Integration::ResourceType& resourceType, const std::string& resourcePath )
251 ResourcePointer resource;
252 ImageDataPtr bitmap = 0;
254 FILE * const fp = fopen( resourcePath.c_str(), "rb" );
257 bool result = ConvertStreamToBitmap( resourceType, resourcePath, fp, bitmap );
258 if( result && bitmap )
260 resource.Reset(bitmap.Get());
267 void ResourceThreadImage::GetClosestImageSize( const std::string& filename,
268 const ImageAttributes& attributes,
269 Vector2 &closestSize )
271 FILE *fp = fopen(filename.c_str(), "rb");
274 LoadBitmapFunction loaderFunction;
275 LoadBitmapHeaderFunction headerFunction;
277 if ( GetBitmapLoaderFunctions( fp,
278 GetFormatHint(filename),
285 const bool read_res = headerFunction(fp, attributes, width, height);
288 DALI_LOG_WARNING("Image Decoder failed to read header for %s\n", filename.c_str());
291 closestSize.width = (float)width;
292 closestSize.height = (float)height;
296 DALI_LOG_WARNING("Image Decoder for %s unavailable\n", filename.c_str());
303 void ResourceThreadImage::GetClosestImageSize( ResourcePointer resourceBuffer,
304 const ImageAttributes& attributes,
305 Vector2 &closestSize )
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() );
311 if( encodedBlob != 0 )
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 );
318 if( blobBytes != 0 && blobSize > 0U )
320 // Open a file handle on the memory buffer:
321 FILE * const fp = fmemopen(blobBytes, blobSize, "rb");
324 LoadBitmapFunction loaderFunction;
325 LoadBitmapHeaderFunction headerFunction;
327 if ( GetBitmapLoaderFunctions( fp,
334 const bool read_res = headerFunction(fp, attributes, width, height);
337 DALI_LOG_WARNING("Image Decoder failed to read header for resourceBuffer\n");
340 closestSize.width = (float) width;
341 closestSize.height = (float) height;
350 //----------------- Called from separate thread (mThread) -----------------
352 void ResourceThreadImage::Load(const ResourceRequest& request)
354 DALI_LOG_TRACE_METHOD( mLogFilter );
355 DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str() );
357 bool file_not_found = false;
361 FILE * const fp = fopen( request.GetPath().c_str(), "rb" );
364 result = ConvertStreamToBitmap( *request.GetType(), request.GetPath(), fp, bitmap );
365 HandleConversionResult( request, result, bitmap, request.GetPath().c_str() );
369 DALI_LOG_WARNING( "Failed to open file to load \"%s\"\n", request.GetPath().c_str() );
370 file_not_found = true;
377 FailedResource resource(request.GetId(), FailureFileNotFound );
378 mResourceLoader.AddFailedLoad(resource);
382 FailedResource resource(request.GetId(), FailureUnknown);
383 mResourceLoader.AddFailedLoad(resource);
388 void ResourceThreadImage::Decode(const ResourceRequest& request)
390 DALI_LOG_TRACE_METHOD( mLogFilter );
391 DALI_LOG_INFO(mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str());
393 ImageDataPtr bitmap = 0;
395 // Get the blob of binary data that we need to decode:
396 DALI_ASSERT_DEBUG( request.GetResource() );
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() );
401 if( encodedBlob != 0 )
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 );
408 if( blobBytes != 0 && blobSize > 0U )
410 // Open a file handle on the memory buffer:
411 FILE * const fp = fmemopen(blobBytes, blobSize, "rb");
414 bool result = ConvertStreamToBitmap( *request.GetType(), request.GetPath(), fp, bitmap );
415 HandleConversionResult( request, result, bitmap, "(image data supplied as in-memory encoded buffer)" );
422 FailedResource resource(request.GetId(), FailureUnknown);
423 mResourceLoader.AddFailedLoad(resource);
427 void ResourceThreadImage::Save(const Integration::ResourceRequest& request)
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." );
435 bool ResourceThreadImage::ConvertStreamToBitmap(const ResourceType& resourceType, std::string path, FILE * const fp, ImageDataPtr& ptr)
437 DALI_LOG_TRACE_METHOD(mLogFilter);
438 DALI_ASSERT_DEBUG( ResourceImageData == resourceType.id );
441 ImageDataPtr bitmap = 0;
445 LoadBitmapFunction function;
446 LoadBitmapHeaderFunction header;
448 if ( GetBitmapLoaderFunctions( fp,
449 GetFormatHint( path ),
453 const ImageResourceType& resType = static_cast<const ImageResourceType&>(resourceType);
454 ImageAttributes attributes = resType.imageAttributes;
456 result = function(fp, attributes, bitmap);
457 DALI_LOG_SET_OBJECT_STRING(bitmap, path);
461 DALI_LOG_WARNING("Unable to convert %s\n", path.c_str());
467 DALI_LOG_WARNING("Image Decoder for %s unavailable\n", path.c_str());
469 fclose(fp); ///! Not exception safe, but an exception on a resource thread will bring the process down anyway.
472 ptr.Reset( bitmap.Get() );
478 bool IsAlphaChannelUsed(const uint8_t * const pixelBuffer, const unsigned width, const unsigned height, const Pixel::Format pixelFormat)
480 bool alphaChannelUsed = false;
482 if(pixelBuffer != NULL)
484 const uint8_t* row = pixelBuffer;
487 Pixel::GetAlphaOffsetAndMask(pixelFormat, byte, bits);
488 const unsigned bytesPerPixel = Pixel::GetBytesPerPixel( pixelFormat );
489 const unsigned stride = width * bytesPerPixel;
491 for(size_t j=0; j < height; j++)
493 const uint8_t* pixels = row;
494 for(unsigned i=0; i < width; ++i)
496 if((pixels[byte] & bits) != bits)
498 alphaChannelUsed = true;
499 j = height; // break out of outer loop
502 pixels += bytesPerPixel;
507 return alphaChannelUsed;
511 void ResourceThreadImage::HandleConversionResult( const Integration::ResourceRequest& request, bool result, Integration::ImageDataPtr imageData, const char * const msg )
513 if( result && imageData )
515 // Scan the pixels of the ImageData to see if its alpha channel is used:
516 if( Pixel::HasAlpha( imageData->pixelFormat ) )
518 const uint8_t* const pixelBuffer = imageData->GetBuffer();
521 const bool alphaUsed = IsAlphaChannelUsed( pixelBuffer, imageData->imageWidth, imageData->imageHeight, imageData->pixelFormat );
522 imageData->SetAlphaUsed( alphaUsed );
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 );
533 DALI_LOG_WARNING( "Unable to decode: %s\n", msg);
537 } // namespace SlpPlatform