Revert "[Tizen] Add codes for Dali Windows Backend"
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / image-loader.cpp
1 /*
2  * Copyright (c) 2017 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/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>
34
35 using namespace Dali::Integration;
36
37 namespace Dali
38 {
39 namespace TizenPlatform
40 {
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 /**
51  * Enum for file formats, has to be in sync with BITMAP_LOADER_LOOKUP_TABLE
52  */
53 enum FileFormats
54 {
55   // Unknown file format
56   FORMAT_UNKNOWN = -1,
57
58   // formats that use magic bytes
59   FORMAT_PNG = 0,
60   FORMAT_JPEG,
61   FORMAT_BMP,
62   FORMAT_GIF,
63   FORMAT_KTX,
64   FORMAT_ASTC,
65   FORMAT_ICO,
66   FORMAT_MAGIC_BYTE_COUNT,
67
68   // formats after this one do not use magic bytes
69   FORMAT_WBMP = FORMAT_MAGIC_BYTE_COUNT,
70   FORMAT_TOTAL_COUNT
71 };
72
73 /**
74  * A lookup table containing all the bitmap loaders with the appropriate information.
75  * Has to be in sync with enum FileFormats
76  */
77 const Dali::ImageLoader::BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
78 {
79   { Png::MAGIC_BYTE_1,  Png::MAGIC_BYTE_2,  LoadBitmapFromPng,  LoadPngHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
80   { Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
81   { Bmp::MAGIC_BYTE_1,  Bmp::MAGIC_BYTE_2,  LoadBitmapFromBmp,  LoadBmpHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
82   { Gif::MAGIC_BYTE_1,  Gif::MAGIC_BYTE_2,  LoadBitmapFromGif,  LoadGifHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
83   { Ktx::MAGIC_BYTE_1,  Ktx::MAGIC_BYTE_2,  LoadBitmapFromKtx,  LoadKtxHeader,  Bitmap::BITMAP_COMPRESSED       },
84   { Astc::MAGIC_BYTE_1, Astc::MAGIC_BYTE_2, LoadBitmapFromAstc, LoadAstcHeader, Bitmap::BITMAP_COMPRESSED       },
85   { Ico::MAGIC_BYTE_1,  Ico::MAGIC_BYTE_2,  LoadBitmapFromIco,  LoadIcoHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS },
86   { 0x0,                0x0,                LoadBitmapFromWbmp, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS },
87 };
88
89 const unsigned int MAGIC_LENGTH = 2;
90
91 /**
92  * This code tries to predict the file format from the filename to help with format picking.
93  */
94 struct FormatExtension
95 {
96   const std::string extension;
97   FileFormats format;
98 };
99
100 const FormatExtension FORMAT_EXTENSIONS[] =
101 {
102  { ".png",  FORMAT_PNG  },
103  { ".jpg",  FORMAT_JPEG },
104  { ".bmp",  FORMAT_BMP  },
105  { ".gif",  FORMAT_GIF  },
106  { ".ktx",  FORMAT_KTX  },
107  { ".astc", FORMAT_ASTC },
108  { ".ico",  FORMAT_ICO  },
109  { ".wbmp", FORMAT_WBMP }
110 };
111
112 const unsigned int FORMAT_EXTENSIONS_COUNT = sizeof(FORMAT_EXTENSIONS) / sizeof(FormatExtension);
113
114 FileFormats GetFormatHint( const std::string& filename )
115 {
116   FileFormats format = FORMAT_UNKNOWN;
117
118   for ( unsigned int i = 0; i < FORMAT_EXTENSIONS_COUNT; ++i )
119   {
120     unsigned int length = FORMAT_EXTENSIONS[i].extension.size();
121     if ( ( filename.size() > length ) &&
122          ( 0 == filename.compare( filename.size() - length, length, FORMAT_EXTENSIONS[i].extension ) ) )
123     {
124       format = FORMAT_EXTENSIONS[i].format;
125       break;
126     }
127   }
128
129   return format;
130 }
131
132 /**
133  * Checks the magic bytes of the file first to determine which Image decoder to use to decode the
134  * bitmap.
135  * @param[in]   fp      The file to decode
136  * @param[in]   format  Hint about what format to try first
137  * @param[out]  loader  Set with the function to use to decode the image
138  * @param[out]  header  Set with the function to use to decode the header
139  * @param[out]  profile The kind of bitmap to hold the bits loaded for the bitmap.
140  * @return true, if we can decode the image, false otherwise
141  */
142 bool GetBitmapLoaderFunctions( FILE *fp,
143                                FileFormats format,
144                                Dali::ImageLoader::LoadBitmapFunction& loader,
145                                Dali::ImageLoader::LoadBitmapHeaderFunction& header,
146                                Bitmap::Profile& profile,
147                                const std::string& filename )
148 {
149   unsigned char magic[MAGIC_LENGTH];
150   size_t read = fread(magic, sizeof(unsigned char), MAGIC_LENGTH, fp);
151
152   // Reset to the start of the file.
153   if( fseek(fp, 0, SEEK_SET) )
154   {
155     DALI_LOG_ERROR("Error seeking to start of file\n");
156   }
157
158   if (read != MAGIC_LENGTH)
159   {
160     return false;
161   }
162
163   bool loaderFound = false;
164   const Dali::ImageLoader::BitmapLoader *lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
165   Dali::ImageLoader::Input defaultInput( fp );
166
167   // try plugin image loader
168   const Dali::ImageLoader::BitmapLoader* data = Internal::Adaptor::ImageLoaderPluginProxy::BitmapLoaderLookup( filename );
169   if( data != NULL )
170   {
171     lookupPtr = data;
172     unsigned int width = 0;
173     unsigned int height = 0;
174     loaderFound = lookupPtr->header( fp, width, height );
175   }
176
177   // try hinted format
178   if ( false == loaderFound && format != FORMAT_UNKNOWN )
179   {
180     lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + format;
181     if ( format >= FORMAT_MAGIC_BYTE_COUNT ||
182          ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] ) )
183     {
184       unsigned int width = 0;
185       unsigned int height = 0;
186       loaderFound = lookupPtr->header( fp, width, height );
187     }
188   }
189
190   // then try to get a match with formats that have magic bytes
191   if ( false == loaderFound )
192   {
193     for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE;
194           lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
195           ++lookupPtr )
196     {
197       if ( lookupPtr->magicByte1 == magic[0] && lookupPtr->magicByte2 == magic[1] )
198       {
199         // to seperate ico file format and wbmp file format
200         unsigned int width = 0;
201         unsigned int height = 0;
202         loaderFound = lookupPtr->header(fp, width, height);
203       }
204       if (loaderFound)
205       {
206         break;
207       }
208     }
209   }
210
211   // finally try formats that do not use magic bytes
212   if ( false == loaderFound )
213   {
214     for ( lookupPtr = BITMAP_LOADER_LOOKUP_TABLE + FORMAT_MAGIC_BYTE_COUNT;
215           lookupPtr < BITMAP_LOADER_LOOKUP_TABLE + FORMAT_TOTAL_COUNT;
216           ++lookupPtr )
217     {
218       // to seperate ico file format and wbmp file format
219       unsigned int width = 0;
220       unsigned int height = 0;
221       loaderFound = lookupPtr->header(fp, width, height);
222       if (loaderFound)
223       {
224         break;
225       }
226     }
227   }
228
229   // if a loader was found set the outputs
230   if ( loaderFound )
231   {
232     loader  = lookupPtr->loader;
233     header  = lookupPtr->header;
234     profile = lookupPtr->profile;
235   }
236
237   // Reset to the start of the file.
238   if( fseek(fp, 0, SEEK_SET) )
239   {
240     DALI_LOG_ERROR("Error seeking to start of file\n");
241   }
242
243   return loaderFound;
244 }
245
246 } // anonymous namespace
247
248
249 namespace ImageLoader
250 {
251
252 bool ConvertStreamToBitmap( const BitmapResourceType& resource, std::string path, FILE * const fp, Dali::Devel::PixelBuffer& pixelBuffer )
253 {
254   DALI_LOG_TRACE_METHOD( gLogFilter );
255
256   bool result = false;
257
258   if (fp != NULL)
259   {
260     Dali::ImageLoader::LoadBitmapFunction function;
261     Dali::ImageLoader::LoadBitmapHeaderFunction header;
262
263     Bitmap::Profile profile;
264
265     if ( GetBitmapLoaderFunctions( fp,
266                                    GetFormatHint( path ),
267                                    function,
268                                    header,
269                                    profile,
270                                    path ) )
271     {
272       const Dali::ImageLoader::ScalingParameters scalingParameters( resource.size, resource.scalingMode, resource.samplingMode );
273       const Dali::ImageLoader::Input input( fp, scalingParameters, resource.orientationCorrection );
274
275       // Run the image type decoder:
276       result = function( input, pixelBuffer );
277
278       if (!result)
279       {
280         DALI_LOG_WARNING( "Unable to convert %s\n", path.c_str() );
281         pixelBuffer.Reset();
282       }
283
284       pixelBuffer = Internal::Platform::ApplyAttributesToBitmap( pixelBuffer, resource.size, resource.scalingMode, resource.samplingMode );
285     }
286     else
287     {
288       DALI_LOG_WARNING( "Image Decoder for %s unavailable\n", path.c_str() );
289     }
290   }
291
292   return result;
293 }
294
295 ResourcePointer LoadImageSynchronously( const Integration::BitmapResourceType& resource, const std::string& path )
296 {
297   ResourcePointer result;
298   Dali::Devel::PixelBuffer bitmap;
299
300   Internal::Platform::FileReader fileReader( path );
301   FILE * const fp = fileReader.GetFile();
302   if( fp != NULL )
303   {
304     bool success = ConvertStreamToBitmap(resource, path, fp, bitmap);
305     if (success && bitmap)
306     {
307       Bitmap::Profile profile{Bitmap::Profile::BITMAP_2D_PACKED_PIXELS};
308
309       // For backward compatibility the Bitmap must be created
310       auto retval = Bitmap::New(profile, Dali::ResourcePolicy::OWNED_DISCARD);
311
312       DALI_LOG_SET_OBJECT_STRING( retval, path );
313
314       retval->GetPackedPixelsProfile()->ReserveBuffer(
315               bitmap.GetPixelFormat(),
316               bitmap.GetWidth(),
317               bitmap.GetHeight(),
318               bitmap.GetWidth(),
319               bitmap.GetHeight()
320             );
321
322       auto& impl = Dali::GetImplementation(bitmap);
323
324       std::copy( impl.GetBuffer(), impl.GetBuffer()+impl.GetBufferSize(), retval->GetBuffer());
325       result.Reset(retval);
326     }
327   }
328   return result;
329 }
330
331 ///@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?]
332 ImageDimensions  GetClosestImageSize( const std::string& filename,
333                                       ImageDimensions size,
334                                       FittingMode::Type fittingMode,
335                                       SamplingMode::Type samplingMode,
336                                       bool orientationCorrection )
337 {
338   unsigned int width = 0;
339   unsigned int height = 0;
340
341   Internal::Platform::FileReader fileReader( filename );
342   FILE *fp = fileReader.GetFile();
343   if (fp != NULL)
344   {
345     Dali::ImageLoader::LoadBitmapFunction loaderFunction;
346     Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
347     Bitmap::Profile profile;
348
349     if ( GetBitmapLoaderFunctions( fp,
350                                    GetFormatHint(filename),
351                                    loaderFunction,
352                                    headerFunction,
353                                    profile,
354                                    filename ) )
355     {
356       const Dali::ImageLoader::Input input( fp, Dali::ImageLoader::ScalingParameters( size, fittingMode, samplingMode ), orientationCorrection );
357
358       const bool read_res = headerFunction( input, width, height );
359       if(!read_res)
360       {
361         DALI_LOG_WARNING("Image Decoder failed to read header for %s\n", filename.c_str());
362       }
363     }
364     else
365     {
366       DALI_LOG_WARNING("Image Decoder for %s unavailable\n", filename.c_str());
367     }
368   }
369   return ImageDimensions( width, height );
370 }
371
372 ImageDimensions GetClosestImageSize( Integration::ResourcePointer resourceBuffer,
373                                      ImageDimensions size,
374                                      FittingMode::Type fittingMode,
375                                      SamplingMode::Type samplingMode,
376                                      bool orientationCorrection )
377 {
378   unsigned int width = 0;
379   unsigned int height = 0;
380
381   // Get the blob of binary data that we need to decode:
382   DALI_ASSERT_DEBUG( resourceBuffer );
383   Dali::RefCountedVector<uint8_t>* const encodedBlob = reinterpret_cast<Dali::RefCountedVector<uint8_t>*>( resourceBuffer.Get() );
384
385   if( encodedBlob != 0 )
386   {
387     if( encodedBlob->GetVector().Size() )
388     {
389       // Open a file handle on the memory buffer:
390       Internal::Platform::FileReader fileReader( encodedBlob->GetVector() );
391       FILE *fp = fileReader.GetFile();
392       if ( fp != NULL )
393       {
394         Dali::ImageLoader::LoadBitmapFunction loaderFunction;
395         Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
396         Bitmap::Profile profile;
397
398         if ( GetBitmapLoaderFunctions( fp,
399                                        FORMAT_UNKNOWN,
400                                        loaderFunction,
401                                        headerFunction,
402                                        profile,
403                                        "" ) )
404         {
405           const Dali::ImageLoader::Input input( fp, Dali::ImageLoader::ScalingParameters( size, fittingMode, samplingMode ), orientationCorrection );
406           const bool read_res = headerFunction( input, width, height );
407           if( !read_res )
408           {
409             DALI_LOG_WARNING( "Image Decoder failed to read header for resourceBuffer\n" );
410           }
411         }
412       }
413     }
414   }
415   return ImageDimensions( width, height );
416 }
417
418 void SetMaxTextureSize( unsigned int size )
419 {
420   gMaxTextureSize = size;
421 }
422
423 unsigned int GetMaxTextureSize()
424 {
425   return gMaxTextureSize;
426 }
427
428 } // ImageLoader
429 } // TizenPlatform
430 } // Dali