supports remote URL gif image
[platform/core/uifw/dali-adaptor.git] / adaptors / devel-api / adaptor-framework / gif-loading.cpp
1 /*
2  * Copyright (c) 2018 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  * Copyright notice for the EFL:
17  * Copyright (C) EFL developers (see AUTHORS)
18  */
19
20 // CLASS HEADER
21 #include "gif-loading.h"
22
23 // EXTERNAL INCLUDES
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <sys/mman.h>
29 #include <gif_lib.h>
30 #include <cstring>
31 #include <dali/integration-api/debug.h>
32 #include <dali/public-api/images/pixel-data.h>
33 #include <resource-loader/network/file-download.h>
34 #include <platform-abstractions/portable/file-reader.h>
35
36 #define IMG_TOO_BIG( w, h )                                                        \
37   ( ( static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h) ) >= \
38     ( (1ULL << (29 * (sizeof(void *) / 4))) - 2048) )
39
40 #define LOADERR( x )                              \
41   do {                                            \
42     DALI_LOG_ERROR( x );                          \
43     goto on_error;                                \
44   } while ( 0 )
45
46 namespace Dali
47 {
48
49 namespace
50 {
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter *gGifLoadingLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_GIF_LOADING" );
53 #endif
54
55 const int IMG_MAX_SIZE = 65000;
56 constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE  = 50 * 1024 * 1024;
57
58 #if GIFLIB_MAJOR < 5
59 const int DISPOSE_BACKGROUND = 2;       /* Set area too background color */
60 const int DISPOSE_PREVIOUS = 3;         /* Restore to previous content */
61 #endif
62
63 struct FrameInfo
64 {
65   FrameInfo()
66   : x( 0 ),
67     y( 0 ),
68     w( 0 ),
69     h( 0 ),
70     delay( 0 ),
71     transparent( -1 ),
72     dispose( DISPOSE_BACKGROUND ),
73     interlace( 0 )
74   {
75   }
76
77   int x, y, w, h;
78   unsigned short delay; // delay time in 1/100ths of a sec
79   short transparent : 10; // -1 == not, anything else == index
80   short dispose : 6; // 0, 1, 2, 3 (others invalid)
81   short interlace : 1; // interlaced or not
82 };
83
84 struct ImageFrame
85 {
86   ImageFrame()
87   : index( 0 ),
88     data( nullptr ),
89     info(),
90     loaded( false )
91   {
92   }
93
94   ~ImageFrame()
95   {
96   }
97
98   int       index;
99   uint32_t  *data;     /* frame decoding data */
100   FrameInfo  info;     /* special image type info */
101   bool loaded : 1;
102 };
103
104 struct GifAnimationData
105 {
106   GifAnimationData()
107   : frames( ),
108     frameCount( 0 ),
109     loopCount( 0 ),
110     currentFrame( 0 ),
111     animated( false )
112   {
113   }
114
115   std::vector<ImageFrame> frames;
116   int frameCount;
117   int loopCount;
118   int currentFrame;
119   bool animated;
120 };
121
122 struct LoaderInfo
123 {
124   LoaderInfo()
125   : gif( nullptr ) ,
126     imageNumber ( 0 )
127   {
128   }
129
130   struct FileData
131   {
132     FileData()
133     : fileName( nullptr ),
134       globalMap ( nullptr ),
135       length( 0 ),
136       fileDescriptor( -1 ),
137       isLocalResource( true )
138     {
139     }
140
141     const char *fileName;  /**< The absolute path of the file. */
142     unsigned char *globalMap ;      /**< A pointer to the entire contents of the file that have been mapped with mmap(2). */
143     long long length;  /**< The length of the file in bytes. */
144     int fileDescriptor; /**< The file descriptor. */
145     bool isLocalResource; /**< The flag whether the file is a local resource */
146   };
147
148   struct FileInfo
149   {
150     FileInfo()
151     : map( nullptr ),
152       position( 0 ),
153       length( 0 )
154     {
155     }
156
157     unsigned char *map;
158     int position, length; // yes - gif uses ints for file sizes.
159   };
160
161   FileData fileData;
162   GifAnimationData animated;
163   GifFileType *gif;
164   int imageNumber;
165   FileInfo fileInfo;
166 };
167
168 struct ImageProperties
169 {
170   unsigned int w;
171   unsigned int h;
172   bool alpha;
173 };
174
175 /**
176  * @brief This combines R, G, B and Alpha values into a single 32-bit (ABGR) value.
177  *
178  * @param[in] animated A structure containing GIF animation data
179  * @param[in] index Frame index to be searched in GIF
180  * @return A pointer to the ImageFrame.
181  */
182 inline int CombinePixelABGR( int a, int r, int g, int b )
183 {
184   return ( ((a) << 24) + ((b) << 16) + ((g) << 8) + (r) );
185 }
186
187 inline int PixelLookup( ColorMapObject *colorMap, int index )
188 {
189   return CombinePixelABGR( 0xFF, colorMap->Colors[index].Red, colorMap->Colors[index].Green, colorMap->Colors[index].Blue );
190 }
191
192 /**
193  * @brief Brute force find frame index - gifs are normally small so ok for now.
194  *
195  * @param[in] animated A structure containing GIF animation data
196  * @param[in] index Frame index to be searched in GIF
197  * @return A pointer to the ImageFrame.
198  */
199 ImageFrame *FindFrame( const GifAnimationData &animated, int index )
200 {
201   for( auto &&elem : animated.frames )
202   {
203     if( elem.index == index )
204     {
205       return const_cast<ImageFrame *>( &elem );
206     }
207   }
208   return nullptr;
209 }
210
211 /**
212  * @brief Fill in an image with a specific rgba color value.
213  *
214  * @param[in] data A pointer pointing to an image data
215  * @param[in] row A int containing the number of rows in an image
216  * @param[in] val A uint32_t containing rgba color value
217  * @param[in] x X-coordinate used an offset to calculate pixel position
218  * @param[in] y Y-coordinate used an offset to calculate pixel position
219  * @param[in] width Width of the image
220  * @param[in] height Height of the image
221  */
222 void FillImage( uint32_t *data, int row, uint32_t val, int x, int y, int width, int height )
223 {
224   int xAxis, yAxis;
225   uint32_t *pixelPosition;
226
227   for( yAxis = 0; yAxis < height; yAxis++ )
228   {
229     pixelPosition = data + ((y + yAxis) * row) + x;
230     for( xAxis = 0; xAxis < width; xAxis++ )
231     {
232       *pixelPosition = val;
233       pixelPosition++;
234     }
235   }
236 }
237
238 /**
239  * @brief Fill a rgba data pixle blob with a frame color (bg or trans)
240  *
241  * @param[in] data A pointer pointing to an image data
242  * @param[in] row A int containing the number of rows in an image
243  * @param[in] gif A pointer pointing to GIF File Type
244  * @param[in] frameInfo A pointer pointing to Frame Information data
245  * @param[in] x X-coordinate used an offset to calculate pixel position
246  * @param[in] y Y-coordinate used an offset to calculate pixel position
247  * @param[in] width Width of the image
248  * @param[in] height Height of the image
249  */
250 void FillFrame( uint32_t *data, int row, GifFileType *gif, FrameInfo *frameInfo, int x, int y, int w, int h )
251 {
252   // solid color fill for pre frame region
253   if( frameInfo->transparent < 0 )
254   {
255     ColorMapObject *colorMap;
256     int backGroundColor;
257
258     // work out color to use from colorMap
259     if( gif->Image.ColorMap )
260     {
261       colorMap = gif->Image.ColorMap;
262     }
263     else
264     {
265       colorMap = gif->SColorMap;
266     }
267     backGroundColor = gif->SBackGroundColor;
268     // and do the fill
269     FillImage( data, row,
270               CombinePixelABGR( 0xff, colorMap->Colors[backGroundColor].Red,
271                                 colorMap->Colors[backGroundColor].Green,
272                                 colorMap->Colors[backGroundColor].Blue ),
273               x, y, w, h );
274   }
275   // fill in region with 0 (transparent)
276   else
277   {
278     FillImage( data, row, 0, x, y, w, h );
279   }
280 }
281
282 /**
283  * @brief Store common fields from gif file info into frame info
284  *
285  * @param[in] gif A pointer pointing to GIF File Type
286  * @param[in] frameInfo A pointer pointing to Frame Information data
287  */
288 void StoreFrameInfo( GifFileType *gif, FrameInfo *frameInfo )
289 {
290   frameInfo->x = gif->Image.Left;
291   frameInfo->y = gif->Image.Top;
292   frameInfo->w = gif->Image.Width;
293   frameInfo->h = gif->Image.Height;
294   frameInfo->interlace = gif->Image.Interlace;
295 }
296
297 /**
298  * @brief Check if image fills "screen space" and if so, if it is transparent
299  * at all then the image could be transparent - OR if image doesnt fill,
300  * then it could be trasnparent (full coverage of screen). Some gifs will
301  * be recognized as solid here for faster rendering, but not all.
302  *
303  * @param[out] full A boolean to show whether image is transparent or not
304  * @param[in] frameInfo A pointer pointing to Frame Information data
305  * @param[in] width Width of the image
306  * @param[in] height Height of the image
307  */
308 void CheckTransparency( bool &full, FrameInfo *frameInfo, int width, int height )
309 {
310   if( ( frameInfo->x == 0 ) && ( frameInfo->y == 0 ) &&
311       ( frameInfo->w == width ) && ( frameInfo->h == height ) )
312   {
313     if( frameInfo->transparent >= 0 )
314     {
315       full = false;
316     }
317   }
318   else
319   {
320     full = false;
321   }
322 }
323
324 /**
325  * @brief Fix coords and work out an x and y inset in orig data if out of image bounds.
326  */
327 void ClipCoordinates( int imageWidth, int imageHeight, int *xin, int *yin, int x0, int y0, int w0, int h0, int *x, int *y, int *w, int *h )
328 {
329   if( x0 < 0 )
330   {
331     w0 += x0;
332     *xin = -x0;
333     x0 = 0;
334   }
335   if( (x0 + w0) > imageWidth )
336   {
337     w0 = imageWidth - x0;
338   }
339   if( y0 < 0 )
340   {
341     h0 += y0;
342     *yin = -y0;
343     y0 = 0;
344   }
345   if( (y0 + h0) > imageHeight )
346   {
347     h0 = imageHeight - y0;
348   }
349   *x = x0;
350   *y = y0;
351   *w = w0;
352   *h = h0;
353 }
354
355 /**
356  * @brief Flush out rgba frame images to save memory but skip current,
357  * previous and lastPreservedFrame frames (needed for dispose mode DISPOSE_PREVIOUS)
358  *
359  * @param[in] animated A structure containing GIF animation data
360  * @param[in] width Width of the image
361  * @param[in] height Height of the image
362  * @param[in] thisframe The current frame
363  * @param[in] prevframe The previous frame
364  * @param[in] lastPreservedFrame The last preserved frame
365  */
366 void FlushFrames( GifAnimationData &animated, int width, int height, ImageFrame *thisframe, ImageFrame *prevframe, ImageFrame *lastPreservedFrame )
367 {
368   DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "FlushFrames() START \n" );
369
370   // target is the amount of memory we want to be under for stored frames
371   int total = 0, target = 512 * 1024;
372
373   // total up the amount of memory used by stored frames for this image
374   for( auto &&frame : animated.frames )
375   {
376     if( frame.data )
377     {
378       total++;
379     }
380   }
381   total *= ( width * height * sizeof( uint32_t ) );
382
383   DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "Total used frame size: %d\n", total );
384
385   // If we use more than target (512k) for frames - flush
386   if( total > target )
387   {
388     // Clean frames (except current and previous) until below target
389     for( auto &&frame : animated.frames )
390     {
391       if( (frame.index != thisframe->index) && (!prevframe || frame.index != prevframe->index) &&
392           (!lastPreservedFrame || frame.index != lastPreservedFrame->index) )
393       {
394         if( frame.data != nullptr )
395         {
396           delete[] frame.data;
397           frame.data = nullptr;
398
399           // subtract memory used and if below target - stop flush
400           total -= ( width * height * sizeof( uint32_t ) );
401           if( total < target )
402           {
403             break;
404           }
405         }
406       }
407     }
408   }
409
410   DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "FlushFrames() END \n" );
411 }
412
413 /**
414  * @brief allocate frame and frame info and append to list and store fields.
415  *
416  * @param[in] animated A structure containing GIF animation data
417  * @param[in] transparent Transparent index of the new frame
418  * @param[in] dispose Dispose mode of new frame
419  * @param[in] delay The frame delay of new frame
420  * @param[in] index The index of new frame
421  */
422 FrameInfo *NewFrame( GifAnimationData &animated, int transparent, int dispose, int delay, int index )
423 {
424   ImageFrame frame;
425
426   // record transparent index to be used or -1 if none
427   // for this SPECIFIC frame
428   frame.info.transparent = transparent;
429   // record dispose mode (3 bits)
430   frame.info.dispose = dispose;
431   // record delay (2 bytes so max 65546 /100 sec)
432   frame.info.delay = delay;
433   // record the index number we are at
434   frame.index = index;
435   // that frame is stored AT image/screen size
436
437   animated.frames.push_back( frame );
438
439   DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "NewFrame: animated.frames.size() = %d\n", animated.frames.size() );
440
441   return &( animated.frames.back().info );
442 }
443
444 /**
445  * @brief Copy data from gif file into buffer.
446  *
447  * @param[in] gifFileType A pointer pointing to GIF File Type
448  * @param[out] buffer A pointer to buffer containing GIF raw data
449  * @param[in] len The length in bytes to be copied
450  * @return The data length of the image in bytes
451  */
452 int FileRead( GifFileType *gifFileType, GifByteType *buffer, int length )
453 {
454   LoaderInfo::FileInfo *fi = reinterpret_cast<LoaderInfo::FileInfo *>( gifFileType->UserData );
455
456   if( fi->position >= fi->length )
457   {
458     return 0; // if at or past end - no
459   }
460   if( (fi->position + length) >= fi->length )
461   {
462     length = fi->length - fi->position;
463   }
464   memcpy( buffer, fi->map + fi->position, length );
465   fi->position += length;
466   return length;
467 }
468
469 /**
470  * @brief Decode a gif image into rows then expand to 32bit into the destination
471  * data pointer.
472  */
473 bool DecodeImage( GifFileType *gif, uint32_t *data, int rowpix, int xin, int yin,
474                   int transparent, int x, int y, int w, int h, bool fill )
475 {
476   int intoffset[] = {0, 4, 2, 1};
477   int intjump[] = {8, 8, 4, 2};
478   int i, xx, yy, pix;
479   GifRowType *rows = NULL;
480   bool ret = false;
481   ColorMapObject *colorMap;
482   uint32_t *p;
483
484   // what we need is image size.
485   SavedImage *sp;
486   sp = &gif->SavedImages[ gif->ImageCount - 1 ];
487   if( !sp )
488   {
489     goto on_error;
490   }
491   w = sp->ImageDesc.Width;
492   h = sp->ImageDesc.Height;
493
494   // build a blob of memory to have pointers to rows of pixels
495   // AND store the decoded gif pixels (1 byte per pixel) as welll
496   rows = static_cast<GifRowType *>(malloc( (h * sizeof(GifRowType) ) + ( w * h * sizeof(GifPixelType) )));
497   if( !rows )
498   {
499     goto on_error;
500   }
501
502   // fill in the pointers at the start
503   for( yy = 0; yy < h; yy++ )
504   {
505     rows[yy] = reinterpret_cast<unsigned char *>(rows) + (h * sizeof(GifRowType)) + (yy * w * sizeof(GifPixelType));
506   }
507
508   // if gif is interlaced, walk interlace pattern and decode into rows
509   if( gif->Image.Interlace )
510   {
511     for( i = 0; i < 4; i++ )
512     {
513       for( yy = intoffset[i]; yy < h; yy += intjump[i] )
514       {
515         if( DGifGetLine( gif, rows[yy], w ) != GIF_OK )
516         {
517           goto on_error;
518         }
519       }
520     }
521   }
522   // normal top to bottom - decode into rows
523   else
524   {
525     for( yy = 0; yy < h; yy++ )
526     {
527       if( DGifGetLine( gif, rows[yy], w ) != GIF_OK )
528       {
529         goto on_error;
530       }
531     }
532   }
533
534   // work out what colormap to use
535   if( gif->Image.ColorMap )
536   {
537     colorMap = gif->Image.ColorMap;
538   }
539   else
540   {
541     colorMap = gif->SColorMap;
542   }
543
544   // if we need to deal with transparent pixels at all...
545   if( transparent >= 0 )
546   {
547     // if we are told to FILL (overwrite with transparency kept)
548     if( fill )
549     {
550       for( yy = 0; yy < h; yy++ )
551       {
552         p = data + ((y + yy) * rowpix) + x;
553         for( xx = 0; xx < w; xx++ )
554         {
555           pix = rows[yin + yy][xin + xx];
556           if( pix != transparent )
557           {
558             *p = PixelLookup( colorMap, pix );
559           }
560           else
561           {
562             *p = 0;
563           }
564           p++;
565         }
566       }
567     }
568     // paste on top with transparent pixels untouched
569     else
570     {
571       for( yy = 0; yy < h; yy++ )
572       {
573         p = data + ((y + yy) * rowpix) + x;
574         for( xx = 0; xx < w; xx++ )
575         {
576           pix = rows[yin + yy][xin + xx];
577           if( pix != transparent )
578           {
579             *p = PixelLookup( colorMap, pix );
580           }
581           p++;
582         }
583       }
584     }
585   }
586   else
587   {
588     // walk pixels without worring about transparency at all
589     for( yy = 0; yy < h; yy++ )
590     {
591       p = data + ((y + yy) * rowpix) + x;
592       for( xx = 0; xx < w; xx++ )
593       {
594         pix = rows[yin + yy][xin + xx];
595         *p = PixelLookup( colorMap, pix );
596         p++;
597       }
598     }
599   }
600   ret = true;
601
602 on_error:
603   if( rows )
604   {
605     free( rows );
606   }
607   return ret;
608 }
609
610 /**
611  * @brief Reader header from the gif file and populates structures accordingly.
612  *
613  * @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
614  * @param[out] prop A ImageProperties structure for storing information about GIF data.
615  * @param[out] error Error code
616  * @return The true or false whether reading was successful or not.
617  */
618 bool ReadHeader( LoaderInfo &loaderInfo,
619                  ImageProperties &prop, //output struct
620                  int *error )
621 {
622   GifAnimationData &animated = loaderInfo.animated;
623   LoaderInfo::FileData &fileData = loaderInfo.fileData;
624   bool ret = false;
625   LoaderInfo::FileInfo fileInfo;
626   GifRecordType rec;
627   GifFileType *gif = NULL;
628   // it is possible which gif file have error midle of frames,
629   // in that case we should play gif file until meet error frame.
630   int imageNumber = 0;
631   int loopCount = -1;
632   FrameInfo *frameInfo = NULL;
633   bool full = true;
634
635   // init prop struct with some default null values
636   prop.w = 0;
637   prop.h = 0;
638
639   if( fileData.isLocalResource )
640   {
641     // local file
642     fileData.fileDescriptor = open( fileData.fileName, O_RDONLY );
643
644     if( fileData.fileDescriptor == -1 )
645     {
646       return false;
647     }
648
649     fileData.length = lseek( fileData.fileDescriptor, 0, SEEK_END );
650     if( fileData.length <= -1 )
651     {
652       close( fileData.fileDescriptor );
653       return false;
654     }
655
656     if( lseek( fileData.fileDescriptor, 0, SEEK_SET ) == -1 )
657     {
658       close( fileData.fileDescriptor );
659       return false;
660     }
661
662     // map the file and store/track info
663     fileData.globalMap  = reinterpret_cast<unsigned char *>( mmap(NULL, fileData.length, PROT_READ, MAP_SHARED, fileData.fileDescriptor, 0 ));
664     fileInfo.map = fileData.globalMap ;
665
666     close(fileData.fileDescriptor);
667     fileData.fileDescriptor = -1;
668   }
669   else
670   {
671     // remote file
672     bool succeeded;
673     Dali::Vector<uint8_t> dataBuffer;
674     size_t dataSize;
675
676     succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory( fileData.fileName, dataBuffer, dataSize,
677                                                                       MAXIMUM_DOWNLOAD_IMAGE_SIZE );
678     if( succeeded )
679     {
680       size_t blobSize = dataBuffer.Size();
681       if( blobSize > 0U )
682       {
683         // Open a file handle on the memory buffer:
684         Dali::Internal::Platform::FileReader fileReader( dataBuffer, blobSize );
685         FILE * const fp = fileReader.GetFile();
686         if ( NULL != fp )
687         {
688           fseek( fp, 0, SEEK_SET );
689           fileData.globalMap = reinterpret_cast<GifByteType*>( malloc(sizeof( GifByteType ) * blobSize ) );
690           fileData.length = fread( fileData.globalMap, sizeof( GifByteType ), blobSize, fp);
691           fileInfo.map = fileData.globalMap;
692         }
693       }
694     }
695   }
696
697   if( !fileInfo.map )
698   {
699     LOADERR("LOAD_ERROR_CORRUPT_FILE");
700   }
701   fileInfo.length = fileData.length;
702   fileInfo.position = 0;
703
704 // actually ask libgif to open the file
705 #if GIFLIB_MAJOR >= 5
706   gif = DGifOpen( &fileInfo, FileRead, NULL );
707 #else
708   gif = DGifOpen( &fileInfo, FileRead );
709 #endif
710
711   if (!gif)
712   {
713     LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
714   }
715
716   // get the gif "screen size" (the actual image size)
717   prop.w = gif->SWidth;
718   prop.h = gif->SHeight;
719
720   // if size is invalid - abort here
721   if( (prop.w < 1) || (prop.h < 1) || (prop.w > IMG_MAX_SIZE) || (prop.h > IMG_MAX_SIZE) || IMG_TOO_BIG(prop.w, prop.h) )
722   {
723     if( IMG_TOO_BIG(prop.w, prop.h) )
724     {
725       LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
726     }
727     LOADERR("LOAD_ERROR_GENERIC");
728   }
729   // walk through gif records in file to figure out info
730   do
731   {
732     if( DGifGetRecordType(gif, &rec) == GIF_ERROR )
733     {
734       // if we have a gif that ends part way through a sequence
735       // (or animation) consider it valid and just break - no error
736       if( imageNumber > 1 )
737       {
738         break;
739       }
740       LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
741     }
742
743     // get image description section
744     if( rec == IMAGE_DESC_RECORD_TYPE )
745     {
746       int img_code;
747       GifByteType *img;
748
749       // get image desc
750       if( DGifGetImageDesc(gif) == GIF_ERROR )
751       {
752         LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
753       }
754       // skip decoding and just walk image to next
755       if( DGifGetCode(gif, &img_code, &img) == GIF_ERROR )
756       {
757         LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
758       }
759       // skip till next...
760       while( img )
761       {
762         img = NULL;
763         DGifGetCodeNext( gif, &img );
764       }
765       // store geometry in the last frame info data
766       if( frameInfo )
767       {
768         StoreFrameInfo( gif, frameInfo );
769         CheckTransparency( full, frameInfo, prop.w, prop.h );
770       }
771       // or if we dont have a frameInfo entry - create one even for stills
772       else
773       {
774         // allocate and save frame with field data
775         frameInfo = NewFrame( animated, -1, 0, 0, imageNumber + 1 );
776         if (!frameInfo)
777         {
778           LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
779         }
780         // store geometry info from gif image
781         StoreFrameInfo( gif, frameInfo );
782         // check for transparency/alpha
783         CheckTransparency( full, frameInfo, prop.w, prop.h );
784       }
785       imageNumber++;
786     }
787     // we have an extension code block - for animated gifs for sure
788     else if( rec == EXTENSION_RECORD_TYPE )
789     {
790       int ext_code;
791       GifByteType *ext;
792
793       ext = NULL;
794       // get the first extension entry
795       DGifGetExtension( gif, &ext_code, &ext );
796       while( ext )
797       {
798         // graphic control extension - for animated gif data
799         // and transparent index + flag
800         if( ext_code == 0xf9 )
801         {
802           // create frame and store it in image
803           int transparencyIndex = (ext[1] & 1) ? ext[4] : -1;
804           int disposeMode = (ext[1] >> 2) & 0x7;
805           int delay = (int(ext[3]) << 8) | int(ext[2]);
806           frameInfo = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
807           if( !frameInfo )
808           {
809             LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
810           }
811         }
812         // netscape extension indicating loop count.
813         else if( ext_code == 0xff ) /* application extension */
814         {
815           if( !strncmp(reinterpret_cast<char *>(&ext[1]), "NETSCAPE2.0", 11) ||
816               !strncmp(reinterpret_cast<char *>(&ext[1]), "ANIMEXTS1.0", 11) )
817           {
818             ext = NULL;
819             DGifGetExtensionNext( gif, &ext );
820             if( ext[1] == 0x01 )
821             {
822               loopCount = (int(ext[3]) << 8) | int(ext[2]);
823               if( loopCount > 0 )
824               {
825                 loopCount++;
826               }
827             }
828           }
829         }
830         // and continue onto the next extension entry
831         ext = NULL;
832         DGifGetExtensionNext( gif, &ext );
833       }
834     }
835   } while( rec != TERMINATE_RECORD_TYPE );
836
837   // if the gif main says we have more than one image or our image counting
838   // says so, then this image is animated - indicate this
839   if( (gif->ImageCount > 1) || (imageNumber > 1) )
840   {
841     animated.animated = 1;
842     animated.loopCount = loopCount;
843   }
844   animated.frameCount = std::min( gif->ImageCount, imageNumber );
845
846   if( !full )
847   {
848     prop.alpha = 1;
849   }
850
851   animated.currentFrame = 1;
852
853   // no errors in header scan etc. so set err and return value
854   *error = 0;
855   ret = true;
856
857 on_error: // jump here on any errors to clean up
858 #if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
859   if( gif )
860   {
861     DGifCloseFile( gif, NULL );
862   }
863 #else
864   if( gif )
865   {
866     DGifCloseFile( gif );
867   }
868 #endif
869
870   return ret;
871 }
872
873 /**
874  * @brief Reader next frame of the gif file and populates structures accordingly.
875  *
876  * @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
877  * @param[in/out] prop A ImageProperties structure containing information about gif data.
878  * @param[out] pixels A pointer to buffer which will contain all pixel data of the frame on return.
879  * @param[out] error Error code
880  * @return The true or false whether reading was successful or not.
881  */
882 bool ReadNextFrame( LoaderInfo &loaderInfo, ImageProperties &prop, //  use for w and h
883                     unsigned char *pixels, int *error )
884 {
885   GifAnimationData &animated = loaderInfo.animated;
886   LoaderInfo::FileData &fileData = loaderInfo.fileData;
887   bool ret = false;
888   GifRecordType rec;
889   GifFileType *gif = NULL;
890   int index = 0, imageNumber = 0;
891   FrameInfo *frameInfo;
892   ImageFrame *frame = NULL;
893   ImageFrame *lastPreservedFrame = NULL;
894
895   index = animated.currentFrame;
896
897   // if index is invalid for animated image - error out
898   if ((animated.animated) && ((index <= 0) || (index > animated.frameCount)))
899   {
900     LOADERR("LOAD_ERROR_GENERIC");
901   }
902
903   // find the given frame index
904   frame = FindFrame( animated, index );
905   if( frame )
906   {
907     if( (frame->loaded) && (frame->data) )
908     {
909       // frame is already there and decoded - jump to end
910       goto on_ok;
911     }
912   }
913   else
914   {
915     LOADERR("LOAD_ERROR_CORRUPT_FILE");
916   }
917
918 open_file:
919   // actually ask libgif to open the file
920   gif = loaderInfo.gif;
921   if( !gif )
922   {
923     loaderInfo.fileInfo.map = fileData.globalMap ;
924     if( !loaderInfo.fileInfo.map )
925     {
926       LOADERR("LOAD_ERROR_CORRUPT_FILE");
927     }
928     loaderInfo.fileInfo.length = fileData.length;
929     loaderInfo.fileInfo.position = 0;
930
931 #if GIFLIB_MAJOR >= 5
932     gif = DGifOpen( &( loaderInfo.fileInfo ), FileRead, NULL );
933 #else
934     gif = DGifOpen( &( loaderInfo.fileInfo ), FileRead );
935 #endif
936     // if gif open failed... get out of here
937     if( !gif )
938     {
939       LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
940     }
941     loaderInfo.gif = gif;
942     loaderInfo.imageNumber = 1;
943   }
944
945   // if we want to go backwards, we likely need/want to re-decode from the
946   // start as we have nothing to build on
947   if( (index > 0) && (index < loaderInfo.imageNumber) && (animated.animated) )
948   {
949 #if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
950     if( loaderInfo.gif )
951       DGifCloseFile( loaderInfo.gif, NULL );
952 #else
953     if( loaderInfo.gif )
954       DGifCloseFile( loaderInfo.gif );
955 #endif
956     loaderInfo.gif = NULL;
957     loaderInfo.imageNumber = 0;
958     goto open_file;
959   }
960
961   // our current position is the previous frame we decoded from the file
962   imageNumber = loaderInfo.imageNumber;
963
964   // walk through gif records in file to figure out info
965   do
966   {
967     if( DGifGetRecordType( gif, &rec ) == GIF_ERROR )
968     {
969       LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
970     }
971
972     if( rec == EXTENSION_RECORD_TYPE )
973     {
974       int ext_code;
975       GifByteType *ext = NULL;
976       DGifGetExtension( gif, &ext_code, &ext );
977
978       while( ext )
979       {
980         ext = NULL;
981         DGifGetExtensionNext( gif, &ext );
982       }
983     }
984     // get image description section
985     else if( rec == IMAGE_DESC_RECORD_TYPE )
986     {
987       int xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
988       int img_code;
989       GifByteType *img;
990       ImageFrame *previousFrame = NULL;
991       ImageFrame *thisFrame = NULL;
992
993       // get image desc
994       if( DGifGetImageDesc(gif) == GIF_ERROR )
995       {
996         LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
997       }
998
999       // get the previous frame entry AND the current one to fill in
1000       previousFrame = FindFrame(animated, imageNumber - 1);
1001       thisFrame = FindFrame(animated, imageNumber);
1002
1003       // if we have a frame AND we're animated AND we have no data...
1004       if( (thisFrame) && (!thisFrame->data) && (animated.animated) )
1005       {
1006         bool first = false;
1007
1008         // allocate it
1009         thisFrame->data = new uint32_t[prop.w * prop.h];
1010
1011         if( !thisFrame->data )
1012         {
1013           LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
1014         }
1015
1016         // if we have no prior frame OR prior frame data... empty
1017         if( (!previousFrame) || (!previousFrame->data) )
1018         {
1019           first = true;
1020           frameInfo = &(thisFrame->info);
1021           memset( thisFrame->data, 0, prop.w * prop.h * sizeof(uint32_t) );
1022         }
1023         // we have a prior frame to copy data from...
1024         else
1025         {
1026           frameInfo = &( previousFrame->info );
1027
1028           // fix coords of sub image in case it goes out...
1029           ClipCoordinates( prop.w, prop.h, &xin, &yin,
1030                            frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1031                            &x, &y, &w, &h );
1032
1033           // if dispose mode is not restore - then copy pre frame
1034           if( frameInfo->dispose != DISPOSE_PREVIOUS )
1035           {
1036             memcpy( thisFrame->data, previousFrame->data, prop.w * prop.h * sizeof(uint32_t) );
1037           }
1038
1039           // if dispose mode is "background" then fill with bg
1040           if( frameInfo->dispose == DISPOSE_BACKGROUND )
1041           {
1042             FillFrame( thisFrame->data, prop.w, gif, frameInfo, x, y, w, h );
1043           }
1044           else if( frameInfo->dispose == DISPOSE_PREVIOUS ) // GIF_DISPOSE_RESTORE
1045           {
1046             int prevIndex = 2;
1047             do
1048             {
1049               // Find last preserved frame.
1050               lastPreservedFrame = FindFrame( animated, imageNumber - prevIndex );
1051               if( ! lastPreservedFrame )
1052               {
1053                 LOADERR( "LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND" );
1054               }
1055               prevIndex++;
1056             } while( lastPreservedFrame && lastPreservedFrame->info.dispose == DISPOSE_PREVIOUS );
1057
1058             if ( lastPreservedFrame )
1059             {
1060               memcpy( thisFrame->data, lastPreservedFrame->data, prop.w * prop.h * sizeof(uint32_t) );
1061             }
1062           }
1063         }
1064         // now draw this frame on top
1065         frameInfo = &( thisFrame->info );
1066         ClipCoordinates( prop.w, prop.h, &xin, &yin,
1067                          frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1068                          &x, &y, &w, &h );
1069         if( !DecodeImage( gif, thisFrame->data, prop.w,
1070                           xin, yin, frameInfo->transparent,
1071                           x, y, w, h, first) )
1072         {
1073           LOADERR("LOAD_ERROR_CORRUPT_FILE");
1074         }
1075
1076         // mark as loaded and done
1077         thisFrame->loaded = true;
1078
1079         FlushFrames( animated, prop.w, prop.h, thisFrame, previousFrame, lastPreservedFrame );
1080       }
1081       // if we hve a frame BUT the image is not animated... different
1082       // path
1083       else if( (thisFrame) && (!thisFrame->data) && (!animated.animated) )
1084       {
1085         // if we don't have the data decoded yet - decode it
1086         if( (!thisFrame->loaded) || (!thisFrame->data) )
1087         {
1088           // use frame info but we WONT allocate frame pixels
1089           frameInfo = &( thisFrame->info );
1090           ClipCoordinates( prop.w, prop.h, &xin, &yin,
1091                            frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1092                            &x, &y, &w, &h );
1093
1094           // clear out all pixels
1095           FillFrame( reinterpret_cast<uint32_t *>(pixels), prop.w, gif, frameInfo, 0, 0, prop.w, prop.h );
1096
1097           // and decode the gif with overwriting
1098           if( !DecodeImage( gif, reinterpret_cast<uint32_t *>(pixels), prop.w,
1099                             xin, yin, frameInfo->transparent, x, y, w, h, true) )
1100           {
1101             LOADERR("LOAD_ERROR_CORRUPT_FILE");
1102           }
1103
1104           // mark as loaded and done
1105           thisFrame->loaded = true;
1106         }
1107         // flush mem we don't need (at expense of decode cpu)
1108       }
1109       else
1110       {
1111         // skip decoding and just walk image to next
1112         if( DGifGetCode( gif, &img_code, &img ) == GIF_ERROR )
1113         {
1114           LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
1115         }
1116
1117         while( img )
1118         {
1119           img = NULL;
1120           DGifGetCodeNext( gif, &img );
1121         }
1122       }
1123
1124       imageNumber++;
1125       // if we found the image we wanted - get out of here
1126       if( imageNumber > index )
1127       {
1128         break;
1129       }
1130     }
1131   } while( rec != TERMINATE_RECORD_TYPE );
1132
1133   // if we are at the end of the animation or not animated, close file
1134   loaderInfo.imageNumber = imageNumber;
1135   if( (animated.frameCount <= 1) || (rec == TERMINATE_RECORD_TYPE) )
1136   {
1137 #if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
1138     if( loaderInfo.gif )
1139     {
1140       DGifCloseFile( loaderInfo.gif, NULL );
1141     }
1142 #else
1143     if( loaderInfo.gif )
1144     {
1145       DGifCloseFile( loaderInfo.gif );
1146     }
1147 #endif
1148
1149     loaderInfo.gif = NULL;
1150     loaderInfo.imageNumber = 0;
1151   }
1152
1153 on_ok:
1154   // no errors in header scan etc. so set err and return value
1155   *error = 0;
1156   ret = true;
1157
1158   // if it was an animated image we need to copy the data to the
1159   // pixels for the image from the frame holding the data
1160   if( animated.animated && frame->data )
1161   {
1162     memcpy( pixels, frame->data, prop.w * prop.h * sizeof( uint32_t ) );
1163   }
1164
1165 on_error: // jump here on any errors to clean up
1166   return ret;
1167 }
1168
1169 } // unnamed namespace
1170
1171 struct GifLoading::Impl
1172 {
1173 public:
1174   Impl( const std::string& url, bool isLocalResource )
1175   : mUrl( url )
1176   {
1177     loaderInfo.gif = nullptr;
1178     int error;
1179     loaderInfo.fileData.fileName = mUrl.c_str();
1180     loaderInfo.fileData.isLocalResource = isLocalResource;
1181
1182     ReadHeader( loaderInfo, imageProperties, &error );
1183   }
1184
1185   ~Impl()
1186   {
1187     if( loaderInfo.fileData.globalMap  )
1188     {
1189       if( loaderInfo.fileData.isLocalResource )
1190       {
1191         munmap( loaderInfo.fileData.globalMap , loaderInfo.fileData.length );
1192       }
1193       else
1194       {
1195         free( loaderInfo.fileData.globalMap );
1196       }
1197       loaderInfo.fileData.globalMap  = nullptr;
1198     }
1199   }
1200
1201   std::string mUrl;
1202   LoaderInfo loaderInfo;
1203   ImageProperties imageProperties;
1204 };
1205
1206 std::unique_ptr<GifLoading> GifLoading::New( const std::string &url, bool isLocalResource )
1207 {
1208   return std::unique_ptr<GifLoading>( new GifLoading( url, isLocalResource ) );
1209 }
1210
1211 GifLoading::GifLoading( const std::string &url, bool isLocalResource )
1212 : mImpl( new GifLoading::Impl( url, isLocalResource ) )
1213 {
1214 }
1215
1216
1217 GifLoading::~GifLoading()
1218 {
1219   delete mImpl;
1220 }
1221
1222 bool GifLoading::LoadNextNFrames( int frameStartIndex, int count, std::vector<Dali::PixelData> &pixelData )
1223 {
1224   int error;
1225   bool ret = false;
1226
1227   const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof( uint32_t );
1228
1229   DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count );
1230
1231   for( int i = 0; i < count; ++i )
1232   {
1233     auto pixelBuffer = new unsigned char[ bufferSize ];
1234
1235     mImpl->loaderInfo.animated.currentFrame = 1 + ( (frameStartIndex + i) % mImpl->loaderInfo.animated.frameCount );
1236
1237     if( ReadNextFrame( mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer, &error ) )
1238     {
1239       if( pixelBuffer )
1240       {
1241         pixelData.push_back( Dali::PixelData::New( pixelBuffer, bufferSize,
1242                                                    mImpl->imageProperties.w, mImpl->imageProperties.h,
1243                                                    Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY) );
1244         ret = true;
1245       }
1246     }
1247   }
1248
1249   return ret;
1250 }
1251
1252 bool GifLoading::LoadAllFrames( std::vector<Dali::PixelData> &pixelData, Dali::Vector<uint32_t> &frameDelays )
1253 {
1254   if( LoadFrameDelays( frameDelays ) )
1255   {
1256     return LoadNextNFrames( 0, mImpl->loaderInfo.animated.frameCount, pixelData );
1257   }
1258   return false;
1259 }
1260
1261 ImageDimensions GifLoading::GetImageSize()
1262 {
1263   return ImageDimensions( mImpl->imageProperties.w, mImpl->imageProperties.w );
1264 }
1265
1266 int GifLoading::GetImageCount()
1267 {
1268   return mImpl->loaderInfo.animated.frameCount;
1269 }
1270
1271 bool GifLoading::LoadFrameDelays( Dali::Vector<uint32_t> &frameDelays )
1272 {
1273   frameDelays.Clear();
1274
1275   for( auto &&elem : mImpl->loaderInfo.animated.frames )
1276   {
1277     // Read frame delay time, multiply 10 to change time unit to milliseconds
1278     frameDelays.PushBack( elem.info.delay * 10 );
1279   }
1280
1281   return true;
1282 }
1283
1284 } // namespace Dali