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