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