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