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