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