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