(loader-webp.cpp) Fixed SVACE errors
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / image-loader.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <dali/internal/imaging/common/image-loader.h>
18
19 #include <dali/devel-api/common/ref-counted-dali-vector.h>
20 #include <dali/internal/imaging/common/pixel-buffer-impl.h>
21
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>
35
36 using namespace Dali::Integration;
37
38 namespace Dali
39 {
40 namespace TizenPlatform
41 {
42 namespace
43 {
44 #if defined(DEBUG_ENABLED)
45 Integration::Log::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_IMAGE_LOADING");
46 #endif
47
48 static unsigned int gMaxTextureSize = 4096;
49
50 static bool gMaxTextureSizeUpdated = false;
51
52 /**
53  * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
54  */
55 enum FileFormats
56 {
57   // Unknown file format
58   FORMAT_UNKNOWN = -1,
59
60   // formats that use magic bytes
61   FORMAT_PNG = 0,
62   FORMAT_JPEG,
63   FORMAT_BMP,
64   FORMAT_GIF,
65   FORMAT_WEBP,
66   FORMAT_KTX,
67   FORMAT_ASTC,
68   FORMAT_ICO,
69   FORMAT_MAGIC_BYTE_COUNT,
70
71   // formats after this one do not use magic bytes
72   FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
73   FORMAT_TOTAL_COUNT
74 };
75
76 /**
77  * A lookup table containing all the bitmap loaders with the appropriate information.
78  * Has to be in sync with enum FileFormats
79  */
80 // clang-format off
81 const Dali::ImageLoader::BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
82   {
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},
92   };
93 // clang-format on
94
95 const unsigned int MAGIC_LENGTH = 2;
96
97 /**
98  * This code tries to predict the file format from the filename to help with format picking.
99  */
100 struct FormatExtension
101 {
102   const std::string extension;
103   FileFormats       format;
104 };
105
106 // clang-format off
107 const FormatExtension FORMAT_EXTENSIONS[] =
108   {
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}
118   };
119 // clang-format on
120
121 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
122
123 FileFormats GetFormatHint(const std::string& filename)
124 {
125   FileFormats format = FORMAT_UNKNOWN;
126
127   for(unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i)
128   {
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)))
132     {
133       format = FORMAT_EXTENSIONS[i].format;
134       break;
135     }
136   }
137
138   return format;
139 }
140
141 /**
142  * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
143  * bitmap.
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
150  */
151 bool GetBitmapLoaderFunctions(FILE*                                        fp,
152                               FileFormats                                  format,
153                               Dali::ImageLoader::LoadBitmapFunction&       loader,
154                               Dali::ImageLoader::LoadBitmapHeaderFunction& header,
155                               Bitmap::Profile&                             profile,
156                               const std::string&                           filename)
157 {
158   unsigned char magic[MAGIC_LENGTH];
159   size_t        read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
160
161   // Reset to the start of the file.
162   if(fseek(fp, 0, SEEK_SET))
163   {
164     DALI_LOG_ERROR("Error seeking to start of file\n");
165   }
166
167   if(read != MAGIC_LENGTH)
168   {
169     return false;
170   }
171
172   bool                                   loaderFound = false;
173   const Dali::ImageLoader::BitmapLoader* lookupPtr   = BITMAP_LOADER_LOOKUP_TABLE;
174   Dali::ImageLoader::Input               defaultInput(fp);
175
176   // try plugin image loader
177   const Dali::ImageLoader::BitmapLoader* data = Internal::Adaptor::ImageLoaderPluginProxy::BitmapLoaderLookup(filename);
178   if(data != NULL)
179   {
180     lookupPtr           = data;
181     unsigned int width  = 0;
182     unsigned int height = 0;
183     loaderFound         = lookupPtr->header(fp, width, height);
184   }
185
186   // try hinted format
187   if(false == loaderFound && format != FORMAT_UNKNOWN)
188   {
189     lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
190     if(format >= FORMAT_MAGIC_BYTE_COUNT ||
191        (lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1]))
192     {
193       unsigned int width  = 0;
194       unsigned int height = 0;
195       loaderFound         = lookupPtr->header(fp, width, height);
196     }
197   }
198
199   // then try to get a match with formats that have magic bytes
200   if(false == loaderFound)
201   {
202     for(lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
203         lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
204         ++lookupPtr)
205     {
206       if(lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1])
207       {
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);
212       }
213       if(loaderFound)
214       {
215         break;
216       }
217     }
218   }
219
220   // finally try formats that do not use magic bytes
221   if(false == loaderFound)
222   {
223     for(lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
224         lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
225         ++lookupPtr)
226     {
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);
231       if(loaderFound)
232       {
233         break;
234       }
235     }
236   }
237
238   // if a loader was found set the outputs
239   if(loaderFound)
240   {
241     loader  = lookupPtr->loader;
242     header  = lookupPtr->header;
243     profile = lookupPtr->profile;
244   }
245
246   // Reset to the start of the file.
247   if(fseek(fp, 0, SEEK_SET))
248   {
249     DALI_LOG_ERROR("Error seeking to start of file\n");
250   }
251
252   return loaderFound;
253 }
254
255 } // anonymous namespace
256
257 namespace ImageLoader
258 {
259 bool ConvertStreamToBitmap(const BitmapResourceType& resource, std::string path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer)
260 {
261   DALI_LOG_TRACE_METHOD(gLogFilter);
262
263   bool result = false;
264
265   if(fp != NULL)
266   {
267     Dali::ImageLoader::LoadBitmapFunction       function;
268     Dali::ImageLoader::LoadBitmapHeaderFunction header;
269
270     Bitmap::Profile profile;
271
272     if(GetBitmapLoaderFunctions(fp,
273                                 GetFormatHint(path),
274                                 function,
275                                 header,
276                                 profile,
277                                 path))
278     {
279       const Dali::ImageLoader::ScalingParameters scalingParameters(resource.size, resource.scalingMode, resource.samplingMode);
280       const Dali::ImageLoader::Input             input(fp, scalingParameters, resource.orientationCorrection);
281
282       // Run the image type decoder:
283       result = function(input, pixelBuffer);
284
285       if(!result)
286       {
287         DALI_LOG_ERROR("Unable to convert %s\n", path.c_str());
288         pixelBuffer.Reset();
289       }
290
291       pixelBuffer = Internal::Platform::ApplyAttributesToBitmap(pixelBuffer, resource.size, resource.scalingMode, resource.samplingMode);
292     }
293     else
294     {
295       DALI_LOG_ERROR("Image Decoder for %s unavailable\n", path.c_str());
296     }
297   }
298
299   return result;
300 }
301
302 ResourcePointer LoadImageSynchronously(const Integration::BitmapResourceType& resource, const std::string& path)
303 {
304   ResourcePointer          result;
305   Dali::Devel::PixelBuffer bitmap;
306
307   Internal::Platform::FileReader fileReader(path);
308   FILE* const                    fp = fileReader.GetFile();
309   if(fp != NULL)
310   {
311     bool success = ConvertStreamToBitmap(resource, path, fp, bitmap);
312     if(success && bitmap)
313     {
314       Bitmap::Profile profile{Bitmap::Profile::BITMAP_2D_PACKED_PIXELS};
315
316       // For backward compatibility the Bitmap must be created
317       auto retval = Bitmap::New(profile, Dali::ResourcePolicy::OWNED_DISCARD);
318
319       DALI_LOG_SET_OBJECT_STRING(retval, path);
320
321       retval->GetPackedPixelsProfile()->ReserveBuffer(
322         bitmap.GetPixelFormat(),
323         bitmap.GetWidth(),
324         bitmap.GetHeight(),
325         bitmap.GetWidth(),
326         bitmap.GetHeight());
327
328       auto& impl = Dali::GetImplementation(bitmap);
329
330       std::copy(impl.GetBuffer(), impl.GetBuffer() + impl.GetBufferSize(), retval->GetBuffer());
331       result.Reset(retval);
332     }
333   }
334   return result;
335 }
336
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)
343 {
344   unsigned int width  = 0;
345   unsigned int height = 0;
346
347   Internal::Platform::FileReader fileReader(filename);
348   FILE*                          fp = fileReader.GetFile();
349   if(fp != NULL)
350   {
351     Dali::ImageLoader::LoadBitmapFunction       loaderFunction;
352     Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
353     Bitmap::Profile                             profile;
354
355     if(GetBitmapLoaderFunctions(fp,
356                                 GetFormatHint(filename),
357                                 loaderFunction,
358                                 headerFunction,
359                                 profile,
360                                 filename))
361     {
362       const Dali::ImageLoader::Input input(fp, Dali::ImageLoader::ScalingParameters(size, fittingMode, samplingMode), orientationCorrection);
363
364       const bool read_res = headerFunction(input, width, height);
365       if(!read_res)
366       {
367         DALI_LOG_ERROR("Image Decoder failed to read header for %s\n", filename.c_str());
368       }
369     }
370     else
371     {
372       DALI_LOG_ERROR("Image Decoder for %s unavailable\n", filename.c_str());
373     }
374   }
375   return ImageDimensions(width, height);
376 }
377
378 ImageDimensions GetClosestImageSize(Integration::ResourcePointer resourceBuffer,
379                                     ImageDimensions              size,
380                                     FittingMode::Type            fittingMode,
381                                     SamplingMode::Type           samplingMode,
382                                     bool                         orientationCorrection)
383 {
384   unsigned int width  = 0;
385   unsigned int height = 0;
386
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());
390
391   if(encodedBlob != 0)
392   {
393     if(encodedBlob->GetVector().Size())
394     {
395       // Open a file handle on the memory buffer:
396       Internal::Platform::FileReader fileReader(encodedBlob->GetVector());
397       FILE*                          fp = fileReader.GetFile();
398       if(fp != NULL)
399       {
400         Dali::ImageLoader::LoadBitmapFunction       loaderFunction;
401         Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
402         Bitmap::Profile                             profile;
403
404         if(GetBitmapLoaderFunctions(fp,
405                                     FORMAT_UNKNOWN,
406                                     loaderFunction,
407                                     headerFunction,
408                                     profile,
409                                     ""))
410         {
411           const Dali::ImageLoader::Input input(fp, Dali::ImageLoader::ScalingParameters(size, fittingMode, samplingMode), orientationCorrection);
412           const bool                     read_res = headerFunction(input, width, height);
413           if(!read_res)
414           {
415             DALI_LOG_ERROR("Image Decoder failed to read header for resourceBuffer\n");
416           }
417         }
418       }
419     }
420   }
421   return ImageDimensions(width, height);
422 }
423
424 void SetMaxTextureSize(unsigned int size)
425 {
426   gMaxTextureSize        = size;
427   gMaxTextureSizeUpdated = true;
428 }
429
430 unsigned int GetMaxTextureSize()
431 {
432   return gMaxTextureSize;
433 }
434
435 bool MaxTextureSizeUpdated()
436 {
437   return gMaxTextureSizeUpdated;
438 }
439
440 } // namespace ImageLoader
441 } // namespace TizenPlatform
442 } // namespace Dali