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