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/public-api/common/ref-counted-dali-vector.h>
20 #include <dali/public-api/images/image-attributes.h>
21 #include <dali/integration-api/bitmap.h>
22 #include <dali/integration-api/debug.h>
24 #include "loader-bmp.h"
25 #include "loader-gif.h"
26 #include "loader-jpeg.h"
27 #include "loader-png.h"
28 #include "loader-ico.h"
29 #include "loader-ktx.h"
30 #include "loader-wbmp.h"
31 #include "image-operations.h"
33 using namespace Dali::Integration;
37 namespace TizenPlatform
42 typedef bool (*LoadBitmapFunction)( FILE*, Bitmap&, ImageAttributes&, const ResourceLoadingClient& );
43 typedef bool (*LoadBitmapHeaderFunction)(FILE*, const ImageAttributes& attrs, unsigned int& width, unsigned int& height );
45 #if defined(DEBUG_ENABLED)
46 Integration::Log::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_IMAGE_LOADING");
50 * Stores the magic bytes, and the loader and header functions used for each image loader.
54 unsigned char magicByte1; ///< The first byte in the file should be this
55 unsigned char magicByte2; ///< The second byte in the file should be this
56 LoadBitmapFunction loader; ///< The function which decodes the file
57 LoadBitmapHeaderFunction header; ///< The function which decodes the header of the file
58 Bitmap::Profile profile; ///< The kind of bitmap to be created
59 /// (addressable packed pixels or an opaque compressed blob).
63 * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
67 // Unknown file format
70 // formats that use magic bytes
77 FORMAT_MAGIC_BYTE_COUNT,
79 // formats after this one do not use magic bytes
80 FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
85 * A lookup table containing all the bitmap loaders with the appropriate information.
86 * Has to be in sync with enum FileFormats
88 const BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
90 { Png::MAGIC_BYTE_1, Png::MAGIC_BYTE_2, LoadBitmapFromPng, LoadPngHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
91 { Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
92 { Bmp::MAGIC_BYTE_1, Bmp::MAGIC_BYTE_2, LoadBitmapFromBmp, LoadBmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
93 { Gif::MAGIC_BYTE_1, Gif::MAGIC_BYTE_2, LoadBitmapFromGif, LoadGifHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
94 { Ktx::MAGIC_BYTE_1, Ktx::MAGIC_BYTE_2, LoadBitmapFromKtx, LoadKtxHeader, Bitmap::BITMAP_COMPRESSED },
95 { Ico::MAGIC_BYTE_1, Ico::MAGIC_BYTE_2, LoadBitmapFromIco, LoadIcoHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
96 { 0x0, 0x0, LoadBitmapFromWbmp, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
99 const unsigned int MAGIC_LENGTH = 2;
102 * This code tries to predict the file format from the filename to help with format picking.
104 struct FormatExtension
106 const std::string extension;
110 const FormatExtension FORMAT_EXTENSIONS[] =
112 { ".png", FORMAT_PNG },
113 { ".jpg", FORMAT_JPEG },
114 { ".bmp", FORMAT_BMP },
115 { ".gif", FORMAT_GIF },
116 { ".ktx", FORMAT_KTX },
117 { ".ico", FORMAT_ICO },
118 { ".wbmp", FORMAT_WBMP }
121 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
123 FileFormats GetFormatHint( const std::string& filename )
125 FileFormats format = FORMAT_UNKNOWN;
127 for ( unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i )
129 unsigned int length = FORMAT_EXTENSIONS[i].extension.size();
130 if ( ( filename.size() > length ) &&
131 ( 0 == filename.compare( filename.size() - length, length, FORMAT_EXTENSIONS[i].extension ) ) )
133 format = FORMAT_EXTENSIONS[i].format;
142 * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
144 * @param[in] fp The file to decode
145 * @param[in] format Hint about what format to try first
146 * @param[out] loader Set with the function to use to decode the image
147 * @param[out] header Set with the function to use to decode the header
148 * @param[out] profile The kind of bitmap to hold the bits loaded for the bitmap.
149 * @return true, if we can decode the image, false otherwise
151 bool GetBitmapLoaderFunctions( FILE *fp,
153 LoadBitmapFunction& loader,
154 LoadBitmapHeaderFunction& header,
155 Bitmap::Profile& profile )
157 unsigned char magic[MAGIC_LENGTH];
158 size_t read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
160 // Reset to the start of the file.
161 if( fseek(fp, 0, SEEK_SET) )
163 DALI_LOG_ERROR("Error seeking to start of file\n");
166 if (read != MAGIC_LENGTH)
171 bool loaderFound = false;
172 const BitmapLoader *lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
173 ImageAttributes attrs;
175 // try hinted format first
176 if ( format != FORMAT_UNKNOWN )
178 lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
179 if ( format >= FORMAT_MAGIC_BYTE_COUNT ||
180 ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] ) )
182 unsigned int width = 0;
183 unsigned int height = 0;
184 loaderFound = lookupPtr->header(fp, attrs, width, height );
188 // then try to get a match with formats that have magic bytes
189 if ( false == loaderFound )
191 for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
192 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
195 if ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] )
197 // to seperate ico file format and wbmp file format
198 unsigned int width = 0;
199 unsigned int height = 0;
200 loaderFound = lookupPtr->header(fp, attrs, width, height);
209 // finally try formats that do not use magic bytes
210 if ( false == loaderFound )
212 for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
213 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
216 // to seperate ico file format and wbmp file format
217 unsigned int width = 0;
218 unsigned int height = 0;
219 loaderFound = lookupPtr->header(fp, attrs, width, height);
227 // if a loader was found set the outputs
230 loader = lookupPtr->loader;
231 header = lookupPtr->header;
232 profile = lookupPtr->profile;
235 // Reset to the start of the file.
236 if( fseek(fp, 0, SEEK_SET) )
238 DALI_LOG_ERROR("Error seeking to start of file\n");
244 } // anonymous namespace
247 namespace ImageLoader
250 bool ConvertStreamToBitmap(const ResourceType& resourceType, std::string path, FILE * const fp, const ResourceLoadingClient& client, BitmapPtr& ptr)
252 DALI_LOG_TRACE_METHOD(gLogFilter);
253 DALI_ASSERT_DEBUG( ResourceBitmap == resourceType.id );
256 BitmapPtr bitmap = 0;
260 LoadBitmapFunction function;
261 LoadBitmapHeaderFunction header;
262 Bitmap::Profile profile;
264 if ( GetBitmapLoaderFunctions( fp,
265 GetFormatHint( path ),
270 bitmap = Bitmap::New(profile, ResourcePolicy::DISCARD );
272 DALI_LOG_SET_OBJECT_STRING(bitmap, path);
273 const BitmapResourceType& resType = static_cast<const BitmapResourceType&>(resourceType);
274 const ImageAttributes& requestedAttributes = resType.imageAttributes; //< Original attributes.
275 ImageAttributes attributes = resType.imageAttributes; //< r/w copy of the attributes.
277 // Check for cancellation now we have hit the filesystem, done some allocation, and burned some cycles:
278 // This won't do anything from synchronous API, it's only useful when called from another thread.
279 client.InterruptionPoint(); // Note: By design, this can throw an exception
281 // Run the image type decoder:
282 // Note, this can overwrite the attributes parameter.
283 result = function( fp, *bitmap, attributes, client );
287 DALI_LOG_WARNING( "Unable to convert %s\n", path.c_str() );
291 // Apply the requested image attributes in best-effort fashion:
292 client.InterruptionPoint(); // Note: By design, this can throw an exception
293 bitmap = Internal::Platform::ApplyAttributesToBitmap( bitmap, requestedAttributes );
297 DALI_LOG_WARNING("Image Decoder for %s unavailable\n", path.c_str());
301 ptr.Reset( bitmap.Get() );
305 ResourcePointer LoadResourceSynchronously( const Integration::ResourceType& resourceType, const std::string& resourcePath )
307 ResourcePointer resource;
308 BitmapPtr bitmap = 0;
310 FILE * const fp = fopen( resourcePath.c_str(), "rb" );
313 bool result = ConvertStreamToBitmap( resourceType, resourcePath, fp, StubbedResourceLoadingClient(), bitmap );
314 if( result && bitmap )
316 resource.Reset(bitmap.Get());
324 void GetClosestImageSize( const std::string& filename,
325 const ImageAttributes& attributes,
326 Vector2 &closestSize )
328 FILE *fp = fopen(filename.c_str(), "rb");
331 LoadBitmapFunction loaderFunction;
332 LoadBitmapHeaderFunction headerFunction;
333 Bitmap::Profile profile;
335 if ( GetBitmapLoaderFunctions( fp,
336 GetFormatHint(filename),
344 const bool read_res = headerFunction(fp, attributes, width, height);
347 DALI_LOG_WARNING("Image Decoder failed to read header for %s\n", filename.c_str());
350 closestSize.width = (float)width;
351 closestSize.height = (float)height;
355 DALI_LOG_WARNING("Image Decoder for %s unavailable\n", filename.c_str());
362 void GetClosestImageSize( ResourcePointer resourceBuffer,
363 const ImageAttributes& attributes,
364 Vector2 &closestSize )
366 BitmapPtr bitmap = 0;
368 // Get the blob of binary data that we need to decode:
369 DALI_ASSERT_DEBUG( resourceBuffer );
370 Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( resourceBuffer.Get() );
372 if( encodedBlob != 0 )
374 const size_t blobSize = encodedBlob->GetVector().Size();
375 uint8_t * const blobBytes = &(encodedBlob->GetVector()[0]);
376 DALI_ASSERT_DEBUG( blobSize > 0U );
377 DALI_ASSERT_DEBUG( blobBytes != 0U );
379 if( blobBytes != 0 && blobSize > 0U )
381 // Open a file handle on the memory buffer:
382 FILE * const fp = fmemopen(blobBytes, blobSize, "rb");
385 LoadBitmapFunction loaderFunction;
386 LoadBitmapHeaderFunction headerFunction;
387 Bitmap::Profile profile;
389 if ( GetBitmapLoaderFunctions( fp,
397 const bool read_res = headerFunction(fp, attributes, width, height);
400 DALI_LOG_WARNING("Image Decoder failed to read header for resourceBuffer\n");
403 closestSize.width = (float) width;
404 closestSize.height = (float) height;