2 * Copyright (c) 2024 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-ico.h>
29 #include <dali/internal/imaging/common/loader-jpeg.h>
30 #include <dali/internal/imaging/common/loader-ktx.h>
31 #include <dali/internal/imaging/common/loader-pkm.h>
32 #include <dali/internal/imaging/common/loader-png.h>
33 #include <dali/internal/imaging/common/loader-wbmp.h>
34 #include <dali/internal/imaging/common/loader-webp.h>
35 #include <dali/internal/system/common/file-reader.h>
37 using namespace Dali::Integration;
41 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;
51 static bool gMaxTextureSizeUpdated = false;
54 * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
58 // Unsupported file format that we should not process image loader.
59 FORMAT_UNSUPPORTED = -2,
61 // Unknown file format
64 // 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
86 const Dali::ImageLoader::BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
88 {Png::MAGIC_BYTE_1, Png::MAGIC_BYTE_2, LoadBitmapFromPng, nullptr, LoadPngHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
89 {Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadPlanesFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
90 {Bmp::MAGIC_BYTE_1, Bmp::MAGIC_BYTE_2, LoadBitmapFromBmp, nullptr, LoadBmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
91 {Gif::MAGIC_BYTE_1, Gif::MAGIC_BYTE_2, LoadBitmapFromGif, nullptr, LoadGifHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
92 {Webp::MAGIC_BYTE_1, Webp::MAGIC_BYTE_2, LoadBitmapFromWebp, nullptr, LoadWebpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
93 {Ktx::MAGIC_BYTE_1, Ktx::MAGIC_BYTE_2, LoadBitmapFromKtx, nullptr, LoadKtxHeader, Bitmap::BITMAP_COMPRESSED },
94 {Astc::MAGIC_BYTE_1, Astc::MAGIC_BYTE_2, LoadBitmapFromAstc, nullptr, LoadAstcHeader, Bitmap::BITMAP_COMPRESSED },
95 {Pkm::MAGIC_BYTE_1, Pkm::MAGIC_BYTE_2, LoadBitmapFromPkm, nullptr, LoadPkmHeader, Bitmap::BITMAP_COMPRESSED },
96 {Ico::MAGIC_BYTE_1, Ico::MAGIC_BYTE_2, LoadBitmapFromIco, nullptr, LoadIcoHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
97 {0x0, 0x0, LoadBitmapFromWbmp, nullptr, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
101 const unsigned int MAGIC_LENGTH = 2;
104 * This code tries to predict the file format from the filename to help with format picking.
106 struct FormatExtension
108 const std::string_view extension;
113 constexpr FormatExtension FORMAT_EXTENSIONS[] =
115 {".png", FORMAT_PNG },
116 {".jpg", FORMAT_JPEG},
117 {".bmp", FORMAT_BMP },
118 {".gif", FORMAT_GIF },
119 {".webp", FORMAT_WEBP },
120 {".ktx", FORMAT_KTX },
121 {".astc", FORMAT_ASTC},
122 {".pkm", FORMAT_PKM},
123 {".ico", FORMAT_ICO },
124 {".wbmp", FORMAT_WBMP},
125 {".svg", FORMAT_UNSUPPORTED}, // SVG
126 {".tvg", FORMAT_UNSUPPORTED}, // ThorVG
127 {".json", FORMAT_UNSUPPORTED}, // Lottie
131 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
133 FileFormats GetFormatHint(const std::string& filename)
135 FileFormats format = FORMAT_UNKNOWN;
137 for(unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i)
139 unsigned int length = FORMAT_EXTENSIONS[i].extension.size();
140 if((filename.size() > length) &&
141 (0 == filename.compare(filename.size() - length, length, FORMAT_EXTENSIONS[i].extension)))
143 format = FORMAT_EXTENSIONS[i].format;
152 * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
154 * @param[in] fp The file to decode
155 * @param[in] format Hint about what format to try first
156 * @param[out] loader Set with the function to use to decode the image
157 * @param[out] header Set with the function to use to decode the header
158 * @param[out] profile The kind of bitmap to hold the bits loaded for the bitmap.
159 * @return true, if we can decode the image, false otherwise
161 bool GetBitmapLoaderFunctions(FILE* fp,
163 Dali::ImageLoader::LoadBitmapFunction& loader,
164 Dali::ImageLoader::LoadPlanesFunction& planeLoader,
165 Dali::ImageLoader::LoadBitmapHeaderFunction& header,
166 Bitmap::Profile& profile,
167 const std::string& filename)
169 // Fast out if image loader doesn't support the format.
170 if(DALI_UNLIKELY(format == FORMAT_UNSUPPORTED))
175 unsigned char magic[MAGIC_LENGTH];
176 size_t read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
178 // Reset to the start of the file.
179 if(fseek(fp, 0, SEEK_SET))
181 DALI_LOG_ERROR("Error seeking to start of file\n");
184 if(read != MAGIC_LENGTH)
189 bool loaderFound = false;
190 const Dali::ImageLoader::BitmapLoader* lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
191 Dali::ImageLoader::Input defaultInput(fp);
193 // try plugin image loader
194 const Dali::ImageLoader::BitmapLoader* data = Internal::Adaptor::ImageLoaderPluginProxy::BitmapLoaderLookup(filename);
198 unsigned int width = 0;
199 unsigned int height = 0;
200 loaderFound = lookupPtr->header(fp, width, height);
204 if(false == loaderFound && format != FORMAT_UNKNOWN)
206 lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
207 if(format >= FORMAT_MAGIC_BYTE_COUNT ||
208 (lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1]))
210 unsigned int width = 0;
211 unsigned int height = 0;
212 loaderFound = lookupPtr->header(fp, width, height);
216 // then try to get a match with formats that have magic bytes
217 if(false == loaderFound)
219 for(lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
220 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
223 if(lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1])
225 // to seperate ico file format and wbmp file format
226 unsigned int width = 0;
227 unsigned int height = 0;
228 loaderFound = lookupPtr->header(fp, width, height);
237 // finally try formats that do not use magic bytes
238 if(false == loaderFound)
240 for(lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
241 lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
244 // to seperate ico file format and wbmp file format
245 unsigned int width = 0;
246 unsigned int height = 0;
247 loaderFound = lookupPtr->header(fp, width, height);
255 // if a loader was found set the outputs
258 loader = lookupPtr->loader;
259 planeLoader = lookupPtr->planeLoader;
260 header = lookupPtr->header;
261 profile = lookupPtr->profile;
264 // Reset to the start of the file.
265 if(fseek(fp, 0, SEEK_SET))
267 DALI_LOG_ERROR("Error seeking to start of file\n");
273 } // anonymous namespace
275 namespace ImageLoader
277 bool ConvertStreamToBitmap(const BitmapResourceType& resource, const std::string& path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer)
279 DALI_LOG_TRACE_METHOD(gLogFilter);
285 Dali::ImageLoader::LoadBitmapFunction function;
286 Dali::ImageLoader::LoadPlanesFunction planeLoader;
287 Dali::ImageLoader::LoadBitmapHeaderFunction header;
289 Bitmap::Profile profile;
291 if(GetBitmapLoaderFunctions(fp,
299 const Dali::ImageLoader::ScalingParameters scalingParameters(resource.size, resource.scalingMode, resource.samplingMode);
300 const Dali::ImageLoader::Input input(fp, scalingParameters, resource.orientationCorrection);
302 // Run the image type decoder:
303 result = function(input, pixelBuffer);
307 DALI_LOG_ERROR("Unable to convert %s\n", path.c_str());
311 pixelBuffer = Internal::Platform::ApplyAttributesToBitmap(pixelBuffer, resource.size, resource.scalingMode, resource.samplingMode);
315 DALI_LOG_ERROR("Image Decoder for %s unavailable\n", path.c_str());
322 bool ConvertStreamToPlanes(const Integration::BitmapResourceType& resource, const std::string& path, FILE* const fp, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers)
324 DALI_LOG_TRACE_METHOD(gLogFilter);
330 Dali::ImageLoader::LoadBitmapFunction loader;
331 Dali::ImageLoader::LoadPlanesFunction planeLoader;
332 Dali::ImageLoader::LoadBitmapHeaderFunction header;
334 Bitmap::Profile profile;
336 if(GetBitmapLoaderFunctions(fp,
344 const Dali::ImageLoader::ScalingParameters scalingParameters(resource.size, resource.scalingMode, resource.samplingMode);
345 const Dali::ImageLoader::Input input(fp, scalingParameters, resource.orientationCorrection);
347 pixelBuffers.clear();
349 // Run the image type decoder:
352 result = planeLoader(input, pixelBuffers);
353 if(!result || pixelBuffers.empty())
355 DALI_LOG_ERROR("Unable to convert %s\n", path.c_str());
359 bool applyAttributes = true;
360 for(auto&& pixelBuffer : pixelBuffers)
362 pixelBuffer = Internal::Platform::ApplyAttributesToBitmap(pixelBuffer, resource.size, resource.scalingMode, resource.samplingMode);
365 applyAttributes = false;
371 DALI_LOG_ERROR("ApplyAttributesToBitmap is failed [%s]\n", path.c_str());
377 Dali::Devel::PixelBuffer pixelBuffer;
378 result = loader(input, pixelBuffer);
381 DALI_LOG_ERROR("Unable to convert %s\n", path.c_str());
385 pixelBuffer = Internal::Platform::ApplyAttributesToBitmap(pixelBuffer, resource.size, resource.scalingMode, resource.samplingMode);
388 pixelBuffers.push_back(pixelBuffer);
392 DALI_LOG_ERROR("ApplyAttributesToBitmap is failed [%s]\n", path.c_str());
399 DALI_LOG_ERROR("Image Decoder for %s unavailable\n", path.c_str());
406 ResourcePointer LoadImageSynchronously(const Integration::BitmapResourceType& resource, const std::string& path)
408 ResourcePointer result;
409 Dali::Devel::PixelBuffer bitmap;
411 Internal::Platform::FileReader fileReader(path);
412 FILE* const fp = fileReader.GetFile();
415 bool success = ConvertStreamToBitmap(resource, path, fp, bitmap);
416 if(success && bitmap)
418 Bitmap::Profile profile{Bitmap::Profile::BITMAP_2D_PACKED_PIXELS};
420 // For backward compatibility the Bitmap must be created
421 auto retval = Bitmap::New(profile, Dali::ResourcePolicy::OWNED_DISCARD);
423 DALI_LOG_SET_OBJECT_STRING(retval, path);
425 retval->GetPackedPixelsProfile()->ReserveBuffer(
426 bitmap.GetPixelFormat(),
432 auto& impl = Dali::GetImplementation(bitmap);
434 std::copy(impl.GetBuffer(), impl.GetBuffer() + impl.GetBufferSize(), retval->GetBuffer());
435 result.Reset(retval);
441 ///@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?]
442 ImageDimensions GetClosestImageSize(const std::string& filename,
443 ImageDimensions size,
444 FittingMode::Type fittingMode,
445 SamplingMode::Type samplingMode,
446 bool orientationCorrection)
448 unsigned int width = 0;
449 unsigned int height = 0;
451 const FileFormats formatHint = GetFormatHint(filename);
453 // Fast out if image loader doesn't support the format.
454 if(formatHint == FileFormats::FORMAT_UNSUPPORTED)
456 return ImageDimensions(width, height);
459 Internal::Platform::FileReader fileReader(filename);
460 FILE* fp = fileReader.GetFile();
463 Dali::ImageLoader::LoadBitmapFunction loaderFunction;
464 Dali::ImageLoader::LoadPlanesFunction planeLoader;
465 Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
466 Bitmap::Profile profile;
468 if(GetBitmapLoaderFunctions(fp,
476 const Dali::ImageLoader::Input input(fp, Dali::ImageLoader::ScalingParameters(size, fittingMode, samplingMode), orientationCorrection);
478 const bool read_res = headerFunction(input, width, height);
481 DALI_LOG_ERROR("Image Decoder failed to read header for %s\n", filename.c_str());
486 DALI_LOG_ERROR("Image Decoder for %s unavailable\n", filename.c_str());
489 return ImageDimensions(width, height);
492 ImageDimensions GetClosestImageSize(Integration::ResourcePointer resourceBuffer,
493 ImageDimensions size,
494 FittingMode::Type fittingMode,
495 SamplingMode::Type samplingMode,
496 bool orientationCorrection)
498 unsigned int width = 0;
499 unsigned int height = 0;
501 // Get the blob of binary data that we need to decode:
502 DALI_ASSERT_DEBUG(resourceBuffer);
503 Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>(resourceBuffer.Get());
507 if(encodedBlob->GetVector().Size())
509 // Open a file handle on the memory buffer:
510 Internal::Platform::FileReader fileReader(encodedBlob->GetVector());
511 FILE* fp = fileReader.GetFile();
514 Dali::ImageLoader::LoadBitmapFunction loaderFunction;
515 Dali::ImageLoader::LoadPlanesFunction planeLoader;
516 Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
517 Bitmap::Profile profile;
519 if(GetBitmapLoaderFunctions(fp,
527 const Dali::ImageLoader::Input input(fp, Dali::ImageLoader::ScalingParameters(size, fittingMode, samplingMode), orientationCorrection);
528 const bool read_res = headerFunction(input, width, height);
531 DALI_LOG_ERROR("Image Decoder failed to read header for resourceBuffer\n");
537 return ImageDimensions(width, height);
540 void SetMaxTextureSize(unsigned int size)
542 gMaxTextureSize = size;
543 gMaxTextureSizeUpdated = true;
546 unsigned int GetMaxTextureSize()
548 return gMaxTextureSize;
551 bool MaxTextureSizeUpdated()
553 return gMaxTextureSizeUpdated;
556 } // namespace ImageLoader
557 } // namespace TizenPlatform