2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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 "image-loader.h"
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>
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"
35 using namespace Dali::Integration;
39 namespace TizenPlatform
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 );
47 #if defined(DEBUG_ENABLED)
48 Integration::Log::Filter* gLogFilter = Debug::Filter::New( Debug::Concise, false, "LOG_IMAGE_LOADING" );
52 * Stores the magic bytes, and the loader and header functions used for each image loader.
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).
65 * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
69 // Unknown file format
72 // formats that use magic bytes
80 FORMAT_MAGIC_BYTE_COUNT,
82 // formats after this one do not use magic bytes
83 FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
88 * A lookup table containing all the bitmap loaders with the appropriate information.
89 * Has to be in sync with enum FileFormats
91 const BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
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 },
103 const unsigned int MAGIC_LENGTH = 2;
106 * This code tries to predict the file format from the filename to help with format picking.
108 struct FormatExtension
110 const std::string extension;
114 const FormatExtension FORMAT_EXTENSIONS[] =
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 }
126 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
128 FileFormats GetFormatHint( const std::string& filename )
130 FileFormats format = FORMAT_UNKNOWN;
132 for ( unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i )
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 ) ) )
138 format = FORMAT_EXTENSIONS[i].format;
147 * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
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
156 bool GetBitmapLoaderFunctions( FILE *fp,
158 LoadBitmapFunction& loader,
159 LoadBitmapHeaderFunction& header,
160 Bitmap::Profile& profile )
162 unsigned char magic[MAGIC_LENGTH];
163 size_t read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
165 // Reset to the start of the file.
166 if( fseek(fp, 0, SEEK_SET) )
168 DALI_LOG_ERROR("Error seeking to start of file\n");
171 if (read != MAGIC_LENGTH)
176 bool loaderFound = false;
177 const BitmapLoader *lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
178 ImageLoader::Input defaultInput( fp );
180 // try hinted format first
181 if ( format != FORMAT_UNKNOWN )
183 lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
184 if ( format >= FORMAT_MAGIC_BYTE_COUNT ||
185 ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] ) )
187 unsigned int width = 0;
188 unsigned int height = 0;
189 loaderFound = lookupPtr->header( fp, width, height );
193 // then try to get a match with formats that have magic bytes
194 if ( false == loaderFound )
196 for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
197 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
200 if ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] )
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);
214 // finally try formats that do not use magic bytes
215 if ( false == loaderFound )
217 for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
218 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
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);
232 // if a loader was found set the outputs
235 loader = lookupPtr->loader;
236 header = lookupPtr->header;
237 profile = lookupPtr->profile;
240 // Reset to the start of the file.
241 if( fseek(fp, 0, SEEK_SET) )
243 DALI_LOG_ERROR("Error seeking to start of file\n");
249 } // anonymous namespace
252 namespace ImageLoader
255 bool ConvertStreamToBitmap( const ResourceType& resourceType, std::string path, FILE * const fp, const ResourceLoadingClient& client, BitmapPtr& ptr )
257 DALI_LOG_TRACE_METHOD( gLogFilter );
258 DALI_ASSERT_DEBUG( ResourceBitmap == resourceType.id );
261 BitmapPtr bitmap = 0;
265 LoadBitmapFunction function;
266 LoadBitmapHeaderFunction header;
267 Bitmap::Profile profile;
269 if ( GetBitmapLoaderFunctions( fp,
270 GetFormatHint( path ),
275 bitmap = Bitmap::New( profile, ResourcePolicy::OWNED_DISCARD );
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 );
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
286 // Run the image type decoder:
287 result = function( client, input, *bitmap );
291 DALI_LOG_WARNING( "Unable to convert %s\n", path.c_str() );
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 );
301 DALI_LOG_WARNING( "Image Decoder for %s unavailable\n", path.c_str() );
305 ptr.Reset( bitmap.Get() );
309 ResourcePointer LoadResourceSynchronously( const Integration::ResourceType& resourceType, const std::string& resourcePath )
311 ResourcePointer resource;
312 BitmapPtr bitmap = 0;
314 Internal::Platform::FileCloser fc( resourcePath.c_str(), "rb");
315 FILE * const fp = fc.GetFile();
318 bool result = ConvertStreamToBitmap( resourceType, resourcePath, fp, StubbedResourceLoadingClient(), bitmap );
319 if( result && bitmap )
321 resource.Reset(bitmap.Get());
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 )
334 unsigned int width = 0;
335 unsigned int height = 0;
337 Internal::Platform::FileCloser fc(filename.c_str(), "rb");
338 FILE *fp = fc.GetFile();
341 LoadBitmapFunction loaderFunction;
342 LoadBitmapHeaderFunction headerFunction;
343 Bitmap::Profile profile;
345 if ( GetBitmapLoaderFunctions( fp,
346 GetFormatHint(filename),
351 const ImageLoader::Input input( fp, ScalingParameters( size, fittingMode, samplingMode ), orientationCorrection );
353 const bool read_res = headerFunction( input, width, height );
356 DALI_LOG_WARNING("Image Decoder failed to read header for %s\n", filename.c_str());
361 DALI_LOG_WARNING("Image Decoder for %s unavailable\n", filename.c_str());
364 return ImageDimensions( width, height );
369 ImageDimensions GetClosestImageSize( Integration::ResourcePointer resourceBuffer,
370 ImageDimensions size,
371 FittingMode::Type fittingMode,
372 SamplingMode::Type samplingMode,
373 bool orientationCorrection )
375 unsigned int width = 0;
376 unsigned int height = 0;
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() );
382 if( encodedBlob != 0 )
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 );
389 if( blobBytes != 0 && blobSize > 0U )
391 // Open a file handle on the memory buffer:
392 Internal::Platform::FileCloser fc( blobBytes, blobSize, "rb" );
393 FILE *fp = fc.GetFile();
396 LoadBitmapFunction loaderFunction;
397 LoadBitmapHeaderFunction headerFunction;
398 Bitmap::Profile profile;
400 if ( GetBitmapLoaderFunctions( fp,
406 const ImageLoader::Input input( fp, ScalingParameters( size, fittingMode, samplingMode ), orientationCorrection );
407 const bool read_res = headerFunction( input, width, height );
410 DALI_LOG_WARNING( "Image Decoder failed to read header for resourceBuffer\n" );
416 return ImageDimensions( width, height );