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