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