2 * Copyright (c) 2017 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 <dali/internal/imaging/common/image-loader.h>
19 #include <dali/devel-api/common/ref-counted-dali-vector.h>
20 #include <dali/internal/imaging/common/pixel-buffer-impl.h>
22 #include <dali/internal/imaging/common/loader-astc.h>
23 #include <dali/internal/imaging/common/loader-bmp.h>
24 #include <dali/internal/imaging/common/loader-gif.h>
25 #include <dali/internal/imaging/common/loader-ico.h>
26 #include <dali/internal/imaging/common/loader-jpeg.h>
27 #include <dali/internal/imaging/common/loader-ktx.h>
28 #include <dali/internal/imaging/common/loader-png.h>
29 #include <dali/internal/imaging/common/loader-wbmp.h>
30 #include <dali/internal/imaging/common/image-operations.h>
31 #include <dali/devel-api/adaptor-framework/image-loader-input.h>
32 #include <dali/internal/imaging/common/image-loader-plugin-proxy.h>
33 #include <dali/internal/system/common/file-reader.h>
35 using namespace Dali::Integration;
36 using namespace Dali::Internal::Platform;
40 namespace TizenPlatform
45 #if defined(DEBUG_ENABLED)
46 Integration::Log::Filter* gLogFilter = Debug::Filter::New( Debug::Concise, false, "LOG_IMAGE_LOADING" );
49 static unsigned int gMaxTextureSize = 4096;
52 * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
56 // Unknown file format
59 // formats that use magic bytes
67 FORMAT_MAGIC_BYTE_COUNT,
69 // formats after this one do not use magic bytes
70 FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
75 * A lookup table containing all the bitmap loaders with the appropriate information.
76 * Has to be in sync with enum FileFormats
78 const Dali::ImageLoader::BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
80 { Png::MAGIC_BYTE_1, Png::MAGIC_BYTE_2, LoadBitmapFromPng, LoadPngHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
81 { Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
82 { Bmp::MAGIC_BYTE_1, Bmp::MAGIC_BYTE_2, LoadBitmapFromBmp, LoadBmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
83 { Gif::MAGIC_BYTE_1, Gif::MAGIC_BYTE_2, LoadBitmapFromGif, LoadGifHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
84 { Ktx::MAGIC_BYTE_1, Ktx::MAGIC_BYTE_2, LoadBitmapFromKtx, LoadKtxHeader, Bitmap::BITMAP_COMPRESSED },
85 { Astc::MAGIC_BYTE_1, Astc::MAGIC_BYTE_2, LoadBitmapFromAstc, LoadAstcHeader, Bitmap::BITMAP_COMPRESSED },
86 { Ico::MAGIC_BYTE_1, Ico::MAGIC_BYTE_2, LoadBitmapFromIco, LoadIcoHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
87 { 0x0, 0x0, LoadBitmapFromWbmp, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
90 const unsigned int MAGIC_LENGTH = 2;
93 * This code tries to predict the file format from the filename to help with format picking.
95 struct FormatExtension
97 const std::string extension;
101 const FormatExtension FORMAT_EXTENSIONS[] =
103 { ".png", FORMAT_PNG },
104 { ".jpg", FORMAT_JPEG },
105 { ".bmp", FORMAT_BMP },
106 { ".gif", FORMAT_GIF },
107 { ".ktx", FORMAT_KTX },
108 { ".astc", FORMAT_ASTC },
109 { ".ico", FORMAT_ICO },
110 { ".wbmp", FORMAT_WBMP }
113 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
115 FileFormats GetFormatHint( const std::string& filename )
117 FileFormats format = FORMAT_UNKNOWN;
119 for ( unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i )
121 unsigned int length = FORMAT_EXTENSIONS[i].extension.size();
122 if ( ( filename.size() > length ) &&
123 ( 0 == filename.compare( filename.size() - length, length, FORMAT_EXTENSIONS[i].extension ) ) )
125 format = FORMAT_EXTENSIONS[i].format;
134 * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
136 * @param[in] fp The file to decode
137 * @param[in] format Hint about what format to try first
138 * @param[out] loader Set with the function to use to decode the image
139 * @param[out] header Set with the function to use to decode the header
140 * @param[out] profile The kind of bitmap to hold the bits loaded for the bitmap.
141 * @return true, if we can decode the image, false otherwise
143 bool GetBitmapLoaderFunctions( FILE *fp,
145 Dali::ImageLoader::LoadBitmapFunction& loader,
146 Dali::ImageLoader::LoadBitmapHeaderFunction& header,
147 Bitmap::Profile& profile,
148 const std::string& filename )
150 unsigned char magic[MAGIC_LENGTH];
151 size_t read = InternalFile::fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
153 // Reset to the start of the file.
154 if( InternalFile::fseek(fp, 0, SEEK_SET) )
156 DALI_LOG_ERROR("Error seeking to start of file\n");
159 if (read != MAGIC_LENGTH)
164 bool loaderFound = false;
165 const Dali::ImageLoader::BitmapLoader *lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
166 Dali::ImageLoader::Input defaultInput( fp );
168 // try plugin image loader
169 const Dali::ImageLoader::BitmapLoader* data = Internal::Adaptor::ImageLoaderPluginProxy::BitmapLoaderLookup( filename );
173 unsigned int width = 0;
174 unsigned int height = 0;
175 loaderFound = lookupPtr->header( fp, width, height );
179 if ( false == loaderFound && format != FORMAT_UNKNOWN )
181 lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
182 if ( format >= FORMAT_MAGIC_BYTE_COUNT ||
183 ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] ) )
185 unsigned int width = 0;
186 unsigned int height = 0;
187 loaderFound = lookupPtr->header( fp, width, height );
191 // then try to get a match with formats that have magic bytes
192 if ( false == loaderFound )
194 for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
195 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
198 if ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] )
200 // to seperate ico file format and wbmp file format
201 unsigned int width = 0;
202 unsigned int height = 0;
203 loaderFound = lookupPtr->header(fp, width, height);
212 // finally try formats that do not use magic bytes
213 if ( false == loaderFound )
215 for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
216 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
219 // to seperate ico file format and wbmp file format
220 unsigned int width = 0;
221 unsigned int height = 0;
222 loaderFound = lookupPtr->header(fp, width, height);
230 // if a loader was found set the outputs
233 loader = lookupPtr->loader;
234 header = lookupPtr->header;
235 profile = lookupPtr->profile;
238 // Reset to the start of the file.
239 if( InternalFile::fseek(fp, 0, SEEK_SET) )
241 DALI_LOG_ERROR("Error seeking to start of file\n");
247 } // anonymous namespace
250 namespace ImageLoader
253 bool ConvertStreamToBitmap( const BitmapResourceType& resource, std::string path, FILE * const fp, Dali::Devel::PixelBuffer& pixelBuffer )
255 DALI_LOG_TRACE_METHOD( gLogFilter );
261 Dali::ImageLoader::LoadBitmapFunction function;
262 Dali::ImageLoader::LoadBitmapHeaderFunction header;
264 Bitmap::Profile profile;
266 if ( GetBitmapLoaderFunctions( fp,
267 GetFormatHint( path ),
273 const Dali::ImageLoader::ScalingParameters scalingParameters( resource.size, resource.scalingMode, resource.samplingMode );
274 const Dali::ImageLoader::Input input( fp, scalingParameters, resource.orientationCorrection );
276 // Run the image type decoder:
277 result = function( input, pixelBuffer );
281 DALI_LOG_WARNING( "Unable to convert %s\n", path.c_str() );
285 pixelBuffer = Internal::Platform::ApplyAttributesToBitmap( pixelBuffer, resource.size, resource.scalingMode, resource.samplingMode );
289 DALI_LOG_WARNING( "Image Decoder for %s unavailable\n", path.c_str() );
296 ResourcePointer LoadImageSynchronously( const Integration::BitmapResourceType& resource, const std::string& path )
298 ResourcePointer result;
299 Dali::Devel::PixelBuffer bitmap;
301 Internal::Platform::FileReader fileReader( path );
302 FILE * const fp = fileReader.GetFile();
305 bool success = ConvertStreamToBitmap(resource, path, fp, bitmap);
306 if (success && bitmap)
308 Bitmap::Profile profile{Bitmap::Profile::BITMAP_2D_PACKED_PIXELS};
310 // For backward compatibility the Bitmap must be created
311 auto retval = Bitmap::New(profile, Dali::ResourcePolicy::OWNED_DISCARD);
313 DALI_LOG_SET_OBJECT_STRING( retval, path );
315 retval->GetPackedPixelsProfile()->ReserveBuffer(
316 bitmap.GetPixelFormat(),
323 auto& impl = Dali::GetImplementation(bitmap);
325 std::copy( impl.GetBuffer(), impl.GetBuffer()+impl.GetBufferSize(), retval->GetBuffer());
326 result.Reset(retval);
332 ///@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?]
333 ImageDimensions GetClosestImageSize( const std::string& filename,
334 ImageDimensions size,
335 FittingMode::Type fittingMode,
336 SamplingMode::Type samplingMode,
337 bool orientationCorrection )
339 unsigned int width = 0;
340 unsigned int height = 0;
342 Internal::Platform::FileReader fileReader( filename );
343 FILE *fp = fileReader.GetFile();
346 Dali::ImageLoader::LoadBitmapFunction loaderFunction;
347 Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
348 Bitmap::Profile profile;
350 if ( GetBitmapLoaderFunctions( fp,
351 GetFormatHint(filename),
357 const Dali::ImageLoader::Input input( fp, Dali::ImageLoader::ScalingParameters( size, fittingMode, samplingMode ), orientationCorrection );
359 const bool read_res = headerFunction( input, width, height );
362 DALI_LOG_WARNING("Image Decoder failed to read header for %s\n", filename.c_str());
367 DALI_LOG_WARNING("Image Decoder for %s unavailable\n", filename.c_str());
370 return ImageDimensions( width, height );
373 ImageDimensions GetClosestImageSize( Integration::ResourcePointer resourceBuffer,
374 ImageDimensions size,
375 FittingMode::Type fittingMode,
376 SamplingMode::Type samplingMode,
377 bool orientationCorrection )
379 unsigned int width = 0;
380 unsigned int height = 0;
382 // Get the blob of binary data that we need to decode:
383 DALI_ASSERT_DEBUG( resourceBuffer );
384 Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( resourceBuffer.Get() );
386 if( encodedBlob != 0 )
388 if( encodedBlob->GetVector().Size() )
390 // Open a file handle on the memory buffer:
391 Internal::Platform::FileReader fileReader( encodedBlob->GetVector() );
392 FILE *fp = fileReader.GetFile();
395 Dali::ImageLoader::LoadBitmapFunction loaderFunction;
396 Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
397 Bitmap::Profile profile;
399 if ( GetBitmapLoaderFunctions( fp,
406 const Dali::ImageLoader::Input input( fp, Dali::ImageLoader::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 );
419 void SetMaxTextureSize( unsigned int size )
421 gMaxTextureSize = size;
424 unsigned int GetMaxTextureSize()
426 return gMaxTextureSize;