Initialize ImageProperties structure
[platform/core/uifw/dali-adaptor.git] / adaptors / devel-api / adaptor-framework / gif-loading.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * Copyright notice for the EFL:
17  * Copyright (C) EFL developers (see AUTHORS)
18  */
19
20 // CLASS HEADER
21 #include "gif-loading.h"
22
23 // EXTERNAL INCLUDES
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <sys/mman.h>
29 #include <gif_lib.h>
30 #include <cstring>
31 #include <dali/integration-api/debug.h>
32 #include <dali/public-api/images/pixel-data.h>
33 #include <resource-loader/network/file-download.h>
34 #include <platform-abstractions/portable/file-reader.h>
35
36 #define IMG_TOO_BIG( w, h )                                                        \
37   ( ( static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h) ) >= \
38     ( (1ULL << (29 * (sizeof(void *) / 4))) - 2048) )
39
40 #define LOADERR( x )                              \
41   do {                                            \
42     DALI_LOG_ERROR( x );                          \
43     goto on_error;                                \
44   } while ( 0 )
45
46 namespace Dali
47 {
48
49 namespace
50 {
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter *gGifLoadingLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_GIF_LOADING" );
53 #endif
54
55 const int IMG_MAX_SIZE = 65000;
56 constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE  = 50 * 1024 * 1024;
57
58 #if GIFLIB_MAJOR < 5
59 const int DISPOSE_BACKGROUND = 2;       /* Set area too background color */
60 const int DISPOSE_PREVIOUS = 3;         /* Restore to previous content */
61 #endif
62
63 struct FrameInfo
64 {
65   FrameInfo()
66   : x( 0 ),
67     y( 0 ),
68     w( 0 ),
69     h( 0 ),
70     delay( 0 ),
71     transparent( -1 ),
72     dispose( DISPOSE_BACKGROUND ),
73     interlace( 0 )
74   {
75   }
76
77   int x, y, w, h;
78   unsigned short delay; // delay time in 1/100ths of a sec
79   short transparent : 10; // -1 == not, anything else == index
80   short dispose : 6; // 0, 1, 2, 3 (others invalid)
81   short interlace : 1; // interlaced or not
82 };
83
84 struct ImageFrame
85 {
86   ImageFrame()
87   : index( 0 ),
88     data( nullptr ),
89     info(),
90     loaded( false )
91   {
92   }
93
94   ~ImageFrame()
95   {
96   }
97
98   int       index;
99   uint32_t  *data;     /* frame decoding data */
100   FrameInfo  info;     /* special image type info */
101   bool loaded : 1;
102 };
103
104 struct GifAnimationData
105 {
106   GifAnimationData()
107   : frames( ),
108     frameCount( 0 ),
109     loopCount( 0 ),
110     currentFrame( 0 ),
111     animated( false )
112   {
113   }
114
115   std::vector<ImageFrame> frames;
116   int frameCount;
117   int loopCount;
118   int currentFrame;
119   bool animated;
120 };
121
122 struct LoaderInfo
123 {
124   LoaderInfo()
125   : gif( nullptr ) ,
126     imageNumber ( 0 )
127   {
128   }
129
130   struct FileData
131   {
132     FileData()
133     : fileName( nullptr ),
134       globalMap ( nullptr ),
135       length( 0 ),
136       fileDescriptor( -1 ),
137       isLocalResource( true )
138     {
139     }
140
141     const char *fileName;  /**< The absolute path of the file. */
142     unsigned char *globalMap ;      /**< A pointer to the entire contents of the file that have been mapped with mmap(2). */
143     long long length;  /**< The length of the file in bytes. */
144     int fileDescriptor; /**< The file descriptor. */
145     bool isLocalResource; /**< The flag whether the file is a local resource */
146   };
147
148   struct FileInfo
149   {
150     FileInfo()
151     : map( nullptr ),
152       position( 0 ),
153       length( 0 )
154     {
155     }
156
157     unsigned char *map;
158     int position, length; // yes - gif uses ints for file sizes.
159   };
160
161   FileData fileData;
162   GifAnimationData animated;
163   GifFileType *gif;
164   int imageNumber;
165   FileInfo fileInfo;
166 };
167
168 struct ImageProperties
169 {
170   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           fseek( fp, 0, SEEK_SET );
692           fileData.globalMap = reinterpret_cast<GifByteType*>( malloc(sizeof( GifByteType ) * blobSize ) );
693           fileData.length = fread( fileData.globalMap, sizeof( GifByteType ), blobSize, fp);
694           fileInfo.map = fileData.globalMap;
695         }
696       }
697     }
698   }
699
700   if( !fileInfo.map )
701   {
702     LOADERR("LOAD_ERROR_CORRUPT_FILE");
703   }
704   fileInfo.length = fileData.length;
705   fileInfo.position = 0;
706
707 // actually ask libgif to open the file
708 #if GIFLIB_MAJOR >= 5
709   gif = DGifOpen( &fileInfo, FileRead, NULL );
710 #else
711   gif = DGifOpen( &fileInfo, FileRead );
712 #endif
713
714   if (!gif)
715   {
716     LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
717   }
718
719   // get the gif "screen size" (the actual image size)
720   prop.w = gif->SWidth;
721   prop.h = gif->SHeight;
722
723   // if size is invalid - abort here
724   if( (prop.w < 1) || (prop.h < 1) || (prop.w > IMG_MAX_SIZE) || (prop.h > IMG_MAX_SIZE) || IMG_TOO_BIG(prop.w, prop.h) )
725   {
726     if( IMG_TOO_BIG(prop.w, prop.h) )
727     {
728       LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
729     }
730     LOADERR("LOAD_ERROR_GENERIC");
731   }
732   // walk through gif records in file to figure out info
733   do
734   {
735     if( DGifGetRecordType(gif, &rec) == GIF_ERROR )
736     {
737       // if we have a gif that ends part way through a sequence
738       // (or animation) consider it valid and just break - no error
739       if( imageNumber > 1 )
740       {
741         break;
742       }
743       LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
744     }
745
746     // get image description section
747     if( rec == IMAGE_DESC_RECORD_TYPE )
748     {
749       int img_code;
750       GifByteType *img;
751
752       // get image desc
753       if( DGifGetImageDesc(gif) == GIF_ERROR )
754       {
755         LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
756       }
757       // skip decoding and just walk image to next
758       if( DGifGetCode(gif, &img_code, &img) == GIF_ERROR )
759       {
760         LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
761       }
762       // skip till next...
763       while( img )
764       {
765         img = NULL;
766         DGifGetCodeNext( gif, &img );
767       }
768       // store geometry in the last frame info data
769       if( frameInfo )
770       {
771         StoreFrameInfo( gif, frameInfo );
772         CheckTransparency( full, frameInfo, prop.w, prop.h );
773       }
774       // or if we dont have a frameInfo entry - create one even for stills
775       else
776       {
777         // allocate and save frame with field data
778         frameInfo = NewFrame( animated, -1, 0, 0, imageNumber + 1 );
779         if (!frameInfo)
780         {
781           LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
782         }
783         // store geometry info from gif image
784         StoreFrameInfo( gif, frameInfo );
785         // check for transparency/alpha
786         CheckTransparency( full, frameInfo, prop.w, prop.h );
787       }
788       imageNumber++;
789     }
790     // we have an extension code block - for animated gifs for sure
791     else if( rec == EXTENSION_RECORD_TYPE )
792     {
793       int ext_code;
794       GifByteType *ext;
795
796       ext = NULL;
797       // get the first extension entry
798       DGifGetExtension( gif, &ext_code, &ext );
799       while( ext )
800       {
801         // graphic control extension - for animated gif data
802         // and transparent index + flag
803         if( ext_code == 0xf9 )
804         {
805           // create frame and store it in image
806           int transparencyIndex = (ext[1] & 1) ? ext[4] : -1;
807           int disposeMode = (ext[1] >> 2) & 0x7;
808           int delay = (int(ext[3]) << 8) | int(ext[2]);
809           frameInfo = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
810           if( !frameInfo )
811           {
812             LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
813           }
814         }
815         // netscape extension indicating loop count.
816         else if( ext_code == 0xff ) /* application extension */
817         {
818           if( !strncmp(reinterpret_cast<char *>(&ext[1]), "NETSCAPE2.0", 11) ||
819               !strncmp(reinterpret_cast<char *>(&ext[1]), "ANIMEXTS1.0", 11) )
820           {
821             ext = NULL;
822             DGifGetExtensionNext( gif, &ext );
823             if( ext[1] == 0x01 )
824             {
825               loopCount = (int(ext[3]) << 8) | int(ext[2]);
826               if( loopCount > 0 )
827               {
828                 loopCount++;
829               }
830             }
831           }
832         }
833         // and continue onto the next extension entry
834         ext = NULL;
835         DGifGetExtensionNext( gif, &ext );
836       }
837     }
838   } while( rec != TERMINATE_RECORD_TYPE );
839
840   // if the gif main says we have more than one image or our image counting
841   // says so, then this image is animated - indicate this
842   if( (gif->ImageCount > 1) || (imageNumber > 1) )
843   {
844     animated.animated = 1;
845     animated.loopCount = loopCount;
846   }
847   animated.frameCount = std::min( gif->ImageCount, imageNumber );
848
849   if( !full )
850   {
851     prop.alpha = 1;
852   }
853
854   animated.currentFrame = 1;
855
856   // no errors in header scan etc. so set err and return value
857   *error = 0;
858   ret = true;
859
860 on_error: // jump here on any errors to clean up
861 #if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
862   if( gif )
863   {
864     DGifCloseFile( gif, NULL );
865   }
866 #else
867   if( gif )
868   {
869     DGifCloseFile( gif );
870   }
871 #endif
872
873   return ret;
874 }
875
876 /**
877  * @brief Reader next frame of the gif file and populates structures accordingly.
878  *
879  * @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
880  * @param[in/out] prop A ImageProperties structure containing information about gif data.
881  * @param[out] pixels A pointer to buffer which will contain all pixel data of the frame on return.
882  * @param[out] error Error code
883  * @return The true or false whether reading was successful or not.
884  */
885 bool ReadNextFrame( LoaderInfo &loaderInfo, ImageProperties &prop, //  use for w and h
886                     unsigned char *pixels, int *error )
887 {
888   GifAnimationData &animated = loaderInfo.animated;
889   LoaderInfo::FileData &fileData = loaderInfo.fileData;
890   bool ret = false;
891   GifRecordType rec;
892   GifFileType *gif = NULL;
893   int index = 0, imageNumber = 0;
894   FrameInfo *frameInfo;
895   ImageFrame *frame = NULL;
896   ImageFrame *lastPreservedFrame = NULL;
897
898   index = animated.currentFrame;
899
900   // if index is invalid for animated image - error out
901   if ((animated.animated) && ((index <= 0) || (index > animated.frameCount)))
902   {
903     LOADERR("LOAD_ERROR_GENERIC");
904   }
905
906   // find the given frame index
907   frame = FindFrame( animated, index );
908   if( frame )
909   {
910     if( (frame->loaded) && (frame->data) )
911     {
912       // frame is already there and decoded - jump to end
913       goto on_ok;
914     }
915   }
916   else
917   {
918     LOADERR("LOAD_ERROR_CORRUPT_FILE");
919   }
920
921 open_file:
922   // actually ask libgif to open the file
923   gif = loaderInfo.gif;
924   if( !gif )
925   {
926     loaderInfo.fileInfo.map = fileData.globalMap ;
927     if( !loaderInfo.fileInfo.map )
928     {
929       LOADERR("LOAD_ERROR_CORRUPT_FILE");
930     }
931     loaderInfo.fileInfo.length = fileData.length;
932     loaderInfo.fileInfo.position = 0;
933
934 #if GIFLIB_MAJOR >= 5
935     gif = DGifOpen( &( loaderInfo.fileInfo ), FileRead, NULL );
936 #else
937     gif = DGifOpen( &( loaderInfo.fileInfo ), FileRead );
938 #endif
939     // if gif open failed... get out of here
940     if( !gif )
941     {
942       LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
943     }
944     loaderInfo.gif = gif;
945     loaderInfo.imageNumber = 1;
946   }
947
948   // if we want to go backwards, we likely need/want to re-decode from the
949   // start as we have nothing to build on
950   if( (index > 0) && (index < loaderInfo.imageNumber) && (animated.animated) )
951   {
952 #if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
953     if( loaderInfo.gif )
954       DGifCloseFile( loaderInfo.gif, NULL );
955 #else
956     if( loaderInfo.gif )
957       DGifCloseFile( loaderInfo.gif );
958 #endif
959     loaderInfo.gif = NULL;
960     loaderInfo.imageNumber = 0;
961     goto open_file;
962   }
963
964   // our current position is the previous frame we decoded from the file
965   imageNumber = loaderInfo.imageNumber;
966
967   // walk through gif records in file to figure out info
968   do
969   {
970     if( DGifGetRecordType( gif, &rec ) == GIF_ERROR )
971     {
972       LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
973     }
974
975     if( rec == EXTENSION_RECORD_TYPE )
976     {
977       int ext_code;
978       GifByteType *ext = NULL;
979       DGifGetExtension( gif, &ext_code, &ext );
980
981       while( ext )
982       {
983         ext = NULL;
984         DGifGetExtensionNext( gif, &ext );
985       }
986     }
987     // get image description section
988     else if( rec == IMAGE_DESC_RECORD_TYPE )
989     {
990       int xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
991       int img_code;
992       GifByteType *img;
993       ImageFrame *previousFrame = NULL;
994       ImageFrame *thisFrame = NULL;
995
996       // get image desc
997       if( DGifGetImageDesc(gif) == GIF_ERROR )
998       {
999         LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
1000       }
1001
1002       // get the previous frame entry AND the current one to fill in
1003       previousFrame = FindFrame(animated, imageNumber - 1);
1004       thisFrame = FindFrame(animated, imageNumber);
1005
1006       // if we have a frame AND we're animated AND we have no data...
1007       if( (thisFrame) && (!thisFrame->data) && (animated.animated) )
1008       {
1009         bool first = false;
1010
1011         // allocate it
1012         thisFrame->data = new uint32_t[prop.w * prop.h];
1013
1014         if( !thisFrame->data )
1015         {
1016           LOADERR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
1017         }
1018
1019         // if we have no prior frame OR prior frame data... empty
1020         if( (!previousFrame) || (!previousFrame->data) )
1021         {
1022           first = true;
1023           frameInfo = &(thisFrame->info);
1024           memset( thisFrame->data, 0, prop.w * prop.h * sizeof(uint32_t) );
1025         }
1026         // we have a prior frame to copy data from...
1027         else
1028         {
1029           frameInfo = &( previousFrame->info );
1030
1031           // fix coords of sub image in case it goes out...
1032           ClipCoordinates( prop.w, prop.h, &xin, &yin,
1033                            frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1034                            &x, &y, &w, &h );
1035
1036           // if dispose mode is not restore - then copy pre frame
1037           if( frameInfo->dispose != DISPOSE_PREVIOUS )
1038           {
1039             memcpy( thisFrame->data, previousFrame->data, prop.w * prop.h * sizeof(uint32_t) );
1040           }
1041
1042           // if dispose mode is "background" then fill with bg
1043           if( frameInfo->dispose == DISPOSE_BACKGROUND )
1044           {
1045             FillFrame( thisFrame->data, prop.w, gif, frameInfo, x, y, w, h );
1046           }
1047           else if( frameInfo->dispose == DISPOSE_PREVIOUS ) // GIF_DISPOSE_RESTORE
1048           {
1049             int prevIndex = 2;
1050             do
1051             {
1052               // Find last preserved frame.
1053               lastPreservedFrame = FindFrame( animated, imageNumber - prevIndex );
1054               if( ! lastPreservedFrame )
1055               {
1056                 LOADERR( "LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND" );
1057               }
1058               prevIndex++;
1059             } while( lastPreservedFrame && lastPreservedFrame->info.dispose == DISPOSE_PREVIOUS );
1060
1061             if ( lastPreservedFrame )
1062             {
1063               memcpy( thisFrame->data, lastPreservedFrame->data, prop.w * prop.h * sizeof(uint32_t) );
1064             }
1065           }
1066         }
1067         // now draw this frame on top
1068         frameInfo = &( thisFrame->info );
1069         ClipCoordinates( prop.w, prop.h, &xin, &yin,
1070                          frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1071                          &x, &y, &w, &h );
1072         if( !DecodeImage( gif, thisFrame->data, prop.w,
1073                           xin, yin, frameInfo->transparent,
1074                           x, y, w, h, first) )
1075         {
1076           LOADERR("LOAD_ERROR_CORRUPT_FILE");
1077         }
1078
1079         // mark as loaded and done
1080         thisFrame->loaded = true;
1081
1082         FlushFrames( animated, prop.w, prop.h, thisFrame, previousFrame, lastPreservedFrame );
1083       }
1084       // if we hve a frame BUT the image is not animated... different
1085       // path
1086       else if( (thisFrame) && (!thisFrame->data) && (!animated.animated) )
1087       {
1088         // if we don't have the data decoded yet - decode it
1089         if( (!thisFrame->loaded) || (!thisFrame->data) )
1090         {
1091           // use frame info but we WONT allocate frame pixels
1092           frameInfo = &( thisFrame->info );
1093           ClipCoordinates( prop.w, prop.h, &xin, &yin,
1094                            frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h,
1095                            &x, &y, &w, &h );
1096
1097           // clear out all pixels
1098           FillFrame( reinterpret_cast<uint32_t *>(pixels), prop.w, gif, frameInfo, 0, 0, prop.w, prop.h );
1099
1100           // and decode the gif with overwriting
1101           if( !DecodeImage( gif, reinterpret_cast<uint32_t *>(pixels), prop.w,
1102                             xin, yin, frameInfo->transparent, x, y, w, h, true) )
1103           {
1104             LOADERR("LOAD_ERROR_CORRUPT_FILE");
1105           }
1106
1107           // mark as loaded and done
1108           thisFrame->loaded = true;
1109         }
1110         // flush mem we don't need (at expense of decode cpu)
1111       }
1112       else
1113       {
1114         // skip decoding and just walk image to next
1115         if( DGifGetCode( gif, &img_code, &img ) == GIF_ERROR )
1116         {
1117           LOADERR("LOAD_ERROR_UNKNOWN_FORMAT");
1118         }
1119
1120         while( img )
1121         {
1122           img = NULL;
1123           DGifGetCodeNext( gif, &img );
1124         }
1125       }
1126
1127       imageNumber++;
1128       // if we found the image we wanted - get out of here
1129       if( imageNumber > index )
1130       {
1131         break;
1132       }
1133     }
1134   } while( rec != TERMINATE_RECORD_TYPE );
1135
1136   // if we are at the end of the animation or not animated, close file
1137   loaderInfo.imageNumber = imageNumber;
1138   if( (animated.frameCount <= 1) || (rec == TERMINATE_RECORD_TYPE) )
1139   {
1140 #if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1))
1141     if( loaderInfo.gif )
1142     {
1143       DGifCloseFile( loaderInfo.gif, NULL );
1144     }
1145 #else
1146     if( loaderInfo.gif )
1147     {
1148       DGifCloseFile( loaderInfo.gif );
1149     }
1150 #endif
1151
1152     loaderInfo.gif = NULL;
1153     loaderInfo.imageNumber = 0;
1154   }
1155
1156 on_ok:
1157   // no errors in header scan etc. so set err and return value
1158   *error = 0;
1159   ret = true;
1160
1161   // if it was an animated image we need to copy the data to the
1162   // pixels for the image from the frame holding the data
1163   if( animated.animated && frame->data )
1164   {
1165     memcpy( pixels, frame->data, prop.w * prop.h * sizeof( uint32_t ) );
1166   }
1167
1168 on_error: // jump here on any errors to clean up
1169   return ret;
1170 }
1171
1172 } // unnamed namespace
1173
1174 struct GifLoading::Impl
1175 {
1176 public:
1177   Impl( const std::string& url, bool isLocalResource )
1178   : mUrl( url )
1179   {
1180     loaderInfo.gif = nullptr;
1181     int error;
1182     loaderInfo.fileData.fileName = mUrl.c_str();
1183     loaderInfo.fileData.isLocalResource = isLocalResource;
1184
1185     ReadHeader( loaderInfo, imageProperties, &error );
1186   }
1187
1188   ~Impl()
1189   {
1190     if( loaderInfo.fileData.globalMap  )
1191     {
1192       if( loaderInfo.fileData.isLocalResource )
1193       {
1194         munmap( loaderInfo.fileData.globalMap , loaderInfo.fileData.length );
1195       }
1196       else
1197       {
1198         free( loaderInfo.fileData.globalMap );
1199       }
1200       loaderInfo.fileData.globalMap  = nullptr;
1201     }
1202   }
1203
1204   std::string mUrl;
1205   LoaderInfo loaderInfo;
1206   ImageProperties imageProperties;
1207 };
1208
1209 std::unique_ptr<GifLoading> GifLoading::New( const std::string &url, bool isLocalResource )
1210 {
1211   return std::unique_ptr<GifLoading>( new GifLoading( url, isLocalResource ) );
1212 }
1213
1214 GifLoading::GifLoading( const std::string &url, bool isLocalResource )
1215 : mImpl( new GifLoading::Impl( url, isLocalResource ) )
1216 {
1217 }
1218
1219
1220 GifLoading::~GifLoading()
1221 {
1222   delete mImpl;
1223 }
1224
1225 bool GifLoading::LoadNextNFrames( int frameStartIndex, int count, std::vector<Dali::PixelData> &pixelData )
1226 {
1227   int error;
1228   bool ret = false;
1229
1230   const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof( uint32_t );
1231
1232   DALI_LOG_INFO( gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count );
1233
1234   for( int i = 0; i < count; ++i )
1235   {
1236     auto pixelBuffer = new unsigned char[ bufferSize ];
1237
1238     mImpl->loaderInfo.animated.currentFrame = 1 + ( (frameStartIndex + i) % mImpl->loaderInfo.animated.frameCount );
1239
1240     if( ReadNextFrame( mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer, &error ) )
1241     {
1242       if( pixelBuffer )
1243       {
1244         pixelData.push_back( Dali::PixelData::New( pixelBuffer, bufferSize,
1245                                                    mImpl->imageProperties.w, mImpl->imageProperties.h,
1246                                                    Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY) );
1247         ret = true;
1248       }
1249     }
1250   }
1251
1252   return ret;
1253 }
1254
1255 bool GifLoading::LoadAllFrames( std::vector<Dali::PixelData> &pixelData, Dali::Vector<uint32_t> &frameDelays )
1256 {
1257   if( LoadFrameDelays( frameDelays ) )
1258   {
1259     return LoadNextNFrames( 0, mImpl->loaderInfo.animated.frameCount, pixelData );
1260   }
1261   return false;
1262 }
1263
1264 ImageDimensions GifLoading::GetImageSize()
1265 {
1266   return ImageDimensions( mImpl->imageProperties.w, mImpl->imageProperties.h );
1267 }
1268
1269 int GifLoading::GetImageCount()
1270 {
1271   return mImpl->loaderInfo.animated.frameCount;
1272 }
1273
1274 bool GifLoading::LoadFrameDelays( Dali::Vector<uint32_t> &frameDelays )
1275 {
1276   frameDelays.Clear();
1277
1278   for( auto &&elem : mImpl->loaderInfo.animated.frames )
1279   {
1280     // Read frame delay time, multiply 10 to change time unit to milliseconds
1281     frameDelays.PushBack( elem.info.delay * 10 );
1282   }
1283
1284   return true;
1285 }
1286
1287 } // namespace Dali