2 * Copyright (c) 2021 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/devel-api/adaptor-framework/image-loader-input.h>
23 #include <dali/internal/imaging/common/image-loader-plugin-proxy.h>
24 #include <dali/internal/imaging/common/image-operations.h>
25 #include <dali/internal/imaging/common/loader-astc.h>
26 #include <dali/internal/imaging/common/loader-bmp.h>
27 #include <dali/internal/imaging/common/loader-gif.h>
28 #include <dali/internal/imaging/common/loader-webp.h>
29 #include <dali/internal/imaging/common/loader-ico.h>
30 #include <dali/internal/imaging/common/loader-jpeg.h>
31 #include <dali/internal/imaging/common/loader-ktx.h>
32 #include <dali/internal/imaging/common/loader-png.h>
33 #include <dali/internal/imaging/common/loader-wbmp.h>
34 #include <dali/internal/system/common/file-reader.h>
36 using namespace Dali::Integration;
40 namespace TizenPlatform
44 #if defined(DEBUG_ENABLED)
45 Integration::Log::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_IMAGE_LOADING");
48 static unsigned int gMaxTextureSize = 4096;
50 static bool gMaxTextureSizeUpdated = false;
53 * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
57 // Unknown file format
60 // formats that use magic bytes
69 FORMAT_MAGIC_BYTE_COUNT,
71 // formats after this one do not use magic bytes
72 FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
77 * A lookup table containing all the bitmap loaders with the appropriate information.
78 * Has to be in sync with enum FileFormats
81 const Dali::ImageLoader::BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
83 {Png::MAGIC_BYTE_1, Png::MAGIC_BYTE_2, LoadBitmapFromPng, LoadPngHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
84 {Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
85 {Bmp::MAGIC_BYTE_1, Bmp::MAGIC_BYTE_2, LoadBitmapFromBmp, LoadBmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
86 {Gif::MAGIC_BYTE_1, Gif::MAGIC_BYTE_2, LoadBitmapFromGif, LoadGifHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
87 {Webp::MAGIC_BYTE_1, Webp::MAGIC_BYTE_2, LoadBitmapFromWebp, LoadWebpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
88 {Ktx::MAGIC_BYTE_1, Ktx::MAGIC_BYTE_2, LoadBitmapFromKtx, LoadKtxHeader, Bitmap::BITMAP_COMPRESSED },
89 {Astc::MAGIC_BYTE_1, Astc::MAGIC_BYTE_2, LoadBitmapFromAstc, LoadAstcHeader, Bitmap::BITMAP_COMPRESSED },
90 {Ico::MAGIC_BYTE_1, Ico::MAGIC_BYTE_2, LoadBitmapFromIco, LoadIcoHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
91 {0x0, 0x0, LoadBitmapFromWbmp, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
95 const unsigned int MAGIC_LENGTH = 2;
98 * This code tries to predict the file format from the filename to help with format picking.
100 struct FormatExtension
102 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 {".webp", FORMAT_WEBP },
114 {".ktx", FORMAT_KTX },
115 {".astc", FORMAT_ASTC},
116 {".ico", FORMAT_ICO },
117 {".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 Dali::ImageLoader::LoadBitmapFunction& loader,
154 Dali::ImageLoader::LoadBitmapHeaderFunction& header,
155 Bitmap::Profile& profile,
156 const std::string& filename)
158 unsigned char magic[MAGIC_LENGTH];
159 size_t read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
161 // Reset to the start of the file.
162 if(fseek(fp, 0, SEEK_SET))
164 DALI_LOG_ERROR("Error seeking to start of file\n");
167 if(read != MAGIC_LENGTH)
172 bool loaderFound = false;
173 const Dali::ImageLoader::BitmapLoader* lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
174 Dali::ImageLoader::Input defaultInput(fp);
176 // try plugin image loader
177 const Dali::ImageLoader::BitmapLoader* data = Internal::Adaptor::ImageLoaderPluginProxy::BitmapLoaderLookup(filename);
181 unsigned int width = 0;
182 unsigned int height = 0;
183 loaderFound = lookupPtr->header(fp, width, height);
187 if(false == loaderFound && format != FORMAT_UNKNOWN)
189 lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
190 if(format >= FORMAT_MAGIC_BYTE_COUNT ||
191 (lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1]))
193 unsigned int width = 0;
194 unsigned int height = 0;
195 loaderFound = lookupPtr->header(fp, width, height);
199 // then try to get a match with formats that have magic bytes
200 if(false == loaderFound)
202 for(lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
203 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
206 if(lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1])
208 // to seperate ico file format and wbmp file format
209 unsigned int width = 0;
210 unsigned int height = 0;
211 loaderFound = lookupPtr->header(fp, width, height);
220 // finally try formats that do not use magic bytes
221 if(false == loaderFound)
223 for(lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
224 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
227 // to seperate ico file format and wbmp file format
228 unsigned int width = 0;
229 unsigned int height = 0;
230 loaderFound = lookupPtr->header(fp, width, height);
238 // if a loader was found set the outputs
241 loader = lookupPtr->loader;
242 header = lookupPtr->header;
243 profile = lookupPtr->profile;
246 // Reset to the start of the file.
247 if(fseek(fp, 0, SEEK_SET))
249 DALI_LOG_ERROR("Error seeking to start of file\n");
255 } // anonymous namespace
257 namespace ImageLoader
259 bool ConvertStreamToBitmap(const BitmapResourceType& resource, std::string path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer)
261 DALI_LOG_TRACE_METHOD(gLogFilter);
267 Dali::ImageLoader::LoadBitmapFunction function;
268 Dali::ImageLoader::LoadBitmapHeaderFunction header;
270 Bitmap::Profile profile;
272 if(GetBitmapLoaderFunctions(fp,
279 const Dali::ImageLoader::ScalingParameters scalingParameters(resource.size, resource.scalingMode, resource.samplingMode);
280 const Dali::ImageLoader::Input input(fp, scalingParameters, resource.orientationCorrection);
282 // Run the image type decoder:
283 result = function(input, pixelBuffer);
287 DALI_LOG_ERROR("Unable to convert %s\n", path.c_str());
291 pixelBuffer = Internal::Platform::ApplyAttributesToBitmap(pixelBuffer, resource.size, resource.scalingMode, resource.samplingMode);
295 DALI_LOG_ERROR("Image Decoder for %s unavailable\n", path.c_str());
302 ResourcePointer LoadImageSynchronously(const Integration::BitmapResourceType& resource, const std::string& path)
304 ResourcePointer result;
305 Dali::Devel::PixelBuffer bitmap;
307 Internal::Platform::FileReader fileReader(path);
308 FILE* const fp = fileReader.GetFile();
311 bool success = ConvertStreamToBitmap(resource, path, fp, bitmap);
312 if(success && bitmap)
314 Bitmap::Profile profile{Bitmap::Profile::BITMAP_2D_PACKED_PIXELS};
316 // For backward compatibility the Bitmap must be created
317 auto retval = Bitmap::New(profile, Dali::ResourcePolicy::OWNED_DISCARD);
319 DALI_LOG_SET_OBJECT_STRING(retval, path);
321 retval->GetPackedPixelsProfile()->ReserveBuffer(
322 bitmap.GetPixelFormat(),
328 auto& impl = Dali::GetImplementation(bitmap);
330 std::copy(impl.GetBuffer(), impl.GetBuffer() + impl.GetBufferSize(), retval->GetBuffer());
331 result.Reset(retval);
337 ///@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?]
338 ImageDimensions GetClosestImageSize(const std::string& filename,
339 ImageDimensions size,
340 FittingMode::Type fittingMode,
341 SamplingMode::Type samplingMode,
342 bool orientationCorrection)
344 unsigned int width = 0;
345 unsigned int height = 0;
347 Internal::Platform::FileReader fileReader(filename);
348 FILE* fp = fileReader.GetFile();
351 Dali::ImageLoader::LoadBitmapFunction loaderFunction;
352 Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
353 Bitmap::Profile profile;
355 if(GetBitmapLoaderFunctions(fp,
356 GetFormatHint(filename),
362 const Dali::ImageLoader::Input input(fp, Dali::ImageLoader::ScalingParameters(size, fittingMode, samplingMode), orientationCorrection);
364 const bool read_res = headerFunction(input, width, height);
367 DALI_LOG_ERROR("Image Decoder failed to read header for %s\n", filename.c_str());
372 DALI_LOG_ERROR("Image Decoder for %s unavailable\n", filename.c_str());
375 return ImageDimensions(width, height);
378 ImageDimensions GetClosestImageSize(Integration::ResourcePointer resourceBuffer,
379 ImageDimensions size,
380 FittingMode::Type fittingMode,
381 SamplingMode::Type samplingMode,
382 bool orientationCorrection)
384 unsigned int width = 0;
385 unsigned int height = 0;
387 // Get the blob of binary data that we need to decode:
388 DALI_ASSERT_DEBUG(resourceBuffer);
389 Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>(resourceBuffer.Get());
393 if(encodedBlob->GetVector().Size())
395 // Open a file handle on the memory buffer:
396 Internal::Platform::FileReader fileReader(encodedBlob->GetVector());
397 FILE* fp = fileReader.GetFile();
400 Dali::ImageLoader::LoadBitmapFunction loaderFunction;
401 Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
402 Bitmap::Profile profile;
404 if(GetBitmapLoaderFunctions(fp,
411 const Dali::ImageLoader::Input input(fp, Dali::ImageLoader::ScalingParameters(size, fittingMode, samplingMode), orientationCorrection);
412 const bool read_res = headerFunction(input, width, height);
415 DALI_LOG_ERROR("Image Decoder failed to read header for resourceBuffer\n");
421 return ImageDimensions(width, height);
424 void SetMaxTextureSize(unsigned int size)
426 gMaxTextureSize = size;
427 gMaxTextureSizeUpdated = true;
430 unsigned int GetMaxTextureSize()
432 return gMaxTextureSize;
435 bool MaxTextureSizeUpdated()
437 return gMaxTextureSizeUpdated;
440 } // namespace ImageLoader
441 } // namespace TizenPlatform