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