Merge "Refactoring Animated image loading" into devel/master
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / gif-loading.cpp
1 /*
2  * Copyright (c) 2022 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 /**
645  * @brief Decode a gif image into rows then expand to 32bit into the destination
646  * data pointer.
647  */
648 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)
649 {
650   int             intoffset[] = {0, 4, 2, 1};
651   int             intjump[]   = {8, 8, 4, 2};
652   int             i, xx, yy, pix, gifW, gifH;
653   GifRowType*     rows = NULL;
654   bool            ret  = false;
655   ColorMapObject* colorMap;
656   uint32_t*       p;
657
658   // cached color data.
659   const std::uint32_t* cachedColorPtr = nullptr;
660
661   // what we need is image size.
662   SavedImage* sp;
663   sp = &gif->SavedImages[gif->ImageCount - 1];
664
665   gifW = sp->ImageDesc.Width;
666   gifH = sp->ImageDesc.Height;
667
668   if(DALI_UNLIKELY((gifW < w) || (gifH < h)))
669   {
670     DALI_LOG_ERROR("gifW : %d, w : %d, gifH : %d, h : %d\n", gifW, w, gifH, h);
671     DALI_ASSERT_DEBUG(false && "Dimensions are bigger than the Gif image size");
672     goto on_error;
673   }
674
675   // build a blob of memory to have pointers to rows of pixels
676   // AND store the decoded gif pixels (1 byte per pixel) as welll
677   rows = static_cast<GifRowType*>(malloc((gifH * sizeof(GifRowType)) + (gifW * gifH * sizeof(GifPixelType))));
678   if(DALI_UNLIKELY(!rows))
679   {
680     goto on_error;
681   }
682
683   // fill in the pointers at the start
684   for(yy = 0; yy < gifH; yy++)
685   {
686     rows[yy] = reinterpret_cast<unsigned char*>(rows) + (gifH * sizeof(GifRowType)) + (yy * gifW * sizeof(GifPixelType));
687   }
688
689   // if gif is interlaced, walk interlace pattern and decode into rows
690   if(gif->Image.Interlace)
691   {
692     for(i = 0; i < 4; i++)
693     {
694       for(yy = intoffset[i]; yy < gifH; yy += intjump[i])
695       {
696         if(DALI_UNLIKELY(DGifGetLine(gif, rows[yy], gifW) != GIF_OK))
697         {
698           goto on_error;
699         }
700       }
701     }
702   }
703   // normal top to bottom - decode into rows
704   else
705   {
706     for(yy = 0; yy < gifH; yy++)
707     {
708       if(DALI_UNLIKELY(DGifGetLine(gif, rows[yy], gifW) != GIF_OK))
709       {
710         goto on_error;
711       }
712     }
713   }
714
715   // work out what colormap to use
716   if(gif->Image.ColorMap)
717   {
718     colorMap = gif->Image.ColorMap;
719     // if w * h is big enough, generate local cached color.
720     if(colorMap->ColorCount * LOCAL_CACHED_COLOR_GENERATE_THRESHOLD < w * h)
721     {
722       gifCachedColor.localCachedColor.resize(colorMap->ColorCount);
723       for(i = 0; i < colorMap->ColorCount; ++i)
724       {
725         gifCachedColor.localCachedColor[i] = PixelLookup(colorMap, i);
726       }
727
728       cachedColorPtr = gifCachedColor.localCachedColor.data();
729     }
730   }
731   else
732   {
733     colorMap       = gif->SColorMap;
734     cachedColorPtr = gifCachedColor.globalCachedColor.data();
735   }
736
737   // HARD-CODING optimize
738   // if we need to deal with transparent pixels at all...
739   if(transparent >= 0)
740   {
741     // if we are told to FILL (overwrite with transparency kept)
742     if(fill)
743     {
744       // if we use cachedColor, use it
745       if(cachedColorPtr)
746       {
747         for(yy = 0; yy < h; yy++)
748         {
749           p = data + ((y + yy) * rowpix) + x;
750           for(xx = 0; xx < w; xx++)
751           {
752             pix = rows[yin + yy][xin + xx];
753             if(pix != transparent)
754             {
755               *p = cachedColorPtr[pix];
756             }
757             else
758             {
759               *p = fillColor;
760             }
761             p++;
762           }
763         }
764       }
765       // we don't have cachedColor. use PixelLookup function.
766       else
767       {
768         for(yy = 0; yy < h; yy++)
769         {
770           p = data + ((y + yy) * rowpix) + x;
771           for(xx = 0; xx < w; xx++)
772           {
773             pix = rows[yin + yy][xin + xx];
774             if(pix != transparent)
775             {
776               *p = PixelLookup(colorMap, pix);
777             }
778             else
779             {
780               *p = fillColor;
781             }
782             p++;
783           }
784         }
785       }
786     }
787     // paste on top with transparent pixels untouched
788     else
789     {
790       // if we use cachedColor, use it
791       if(cachedColorPtr)
792       {
793         for(yy = 0; yy < h; yy++)
794         {
795           p = data + ((y + yy) * rowpix) + x;
796           for(xx = 0; xx < w; xx++)
797           {
798             pix = rows[yin + yy][xin + xx];
799             if(pix != transparent)
800             {
801               *p = cachedColorPtr[pix];
802             }
803             p++;
804           }
805         }
806       }
807       // we don't have cachedColor. use PixelLookup function.
808       else
809       {
810         for(yy = 0; yy < h; yy++)
811         {
812           p = data + ((y + yy) * rowpix) + x;
813           for(xx = 0; xx < w; xx++)
814           {
815             pix = rows[yin + yy][xin + xx];
816             if(pix != transparent)
817             {
818               *p = PixelLookup(colorMap, pix);
819             }
820             p++;
821           }
822         }
823       }
824     }
825   }
826   else
827   {
828     // if we use cachedColor, use it
829     if(cachedColorPtr)
830     {
831       // walk pixels without worring about transparency at all
832       for(yy = 0; yy < h; yy++)
833       {
834         p = data + ((y + yy) * rowpix) + x;
835         for(xx = 0; xx < w; xx++)
836         {
837           pix = rows[yin + yy][xin + xx];
838           *p  = cachedColorPtr[pix];
839           p++;
840         }
841       }
842     }
843     // we don't have cachedColor. use PixelLookup function.
844     else
845     {
846       // walk pixels without worring about transparency at all
847       for(yy = 0; yy < h; yy++)
848       {
849         p = data + ((y + yy) * rowpix) + x;
850         for(xx = 0; xx < w; xx++)
851         {
852           pix = rows[yin + yy][xin + xx];
853           *p  = PixelLookup(colorMap, pix);
854           p++;
855         }
856       }
857     }
858   }
859   ret = true;
860
861 on_error:
862   if(rows)
863   {
864     free(rows);
865   }
866   return ret;
867 }
868
869 /**
870  * @brief Reader header from the gif file and populates structures accordingly.
871  *
872  * @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
873  * @param[out] prop A ImageProperties structure for storing information about GIF data.
874  * @return The true or false whether reading was successful or not.
875  */
876 bool ReadHeader(LoaderInfo&      loaderInfo,
877                 ImageProperties& prop)
878 {
879   GifAnimationData&     animated    = loaderInfo.animated;
880   GifCachedColorData&   cachedColor = loaderInfo.cachedColor;
881   LoaderInfo::FileData& fileData    = loaderInfo.fileData;
882   bool                  success     = false;
883   LoaderInfo::FileInfo  fileInfo;
884   GifRecordType         rec;
885
886   // it is possible which gif file have error midle of frames,
887   // in that case we should play gif file until meet error frame.
888   int        imageNumber = 0;
889   int        loopCount   = -1;
890   FrameInfo* frameInfo   = NULL;
891   bool       full        = true;
892
893   success = fileData.LoadFile();
894   if(DALI_UNLIKELY(!success || !fileData.globalMap))
895   {
896     success = false;
897     DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
898   }
899   else
900   {
901     fileInfo.map      = fileData.globalMap;
902     fileInfo.length   = fileData.length;
903     fileInfo.position = 0;
904     GifAccessor gifAccessor(fileInfo);
905
906     if(gifAccessor.gif)
907     {
908       // get the gif "screen size" (the actual image size)
909       prop.w = gifAccessor.gif->SWidth;
910       prop.h = gifAccessor.gif->SHeight;
911
912       // if size is invalid - abort here
913       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)))
914       {
915         if(IMG_TOO_BIG(prop.w, prop.h))
916         {
917           success = false;
918           DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
919         }
920         else
921         {
922           success = false;
923           DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
924         }
925       }
926       else
927       {
928         // walk through gif records in file to figure out info
929         success = true;
930         do
931         {
932           if(DGifGetRecordType(gifAccessor.gif, &rec) == GIF_ERROR)
933           {
934             // if we have a gif that ends part way through a sequence
935             // (or animation) consider it valid and just break - no error
936             if(imageNumber <= 1)
937             {
938               success = true;
939             }
940             else
941             {
942               success = false;
943               DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
944             }
945             break;
946           }
947
948           // get image description section
949           if(rec == IMAGE_DESC_RECORD_TYPE)
950           {
951             int          img_code;
952             GifByteType* img;
953
954             // get image desc
955             if(DALI_UNLIKELY(DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR))
956             {
957               success = false;
958               DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
959               break;
960             }
961             // skip decoding and just walk image to next
962             if(DALI_UNLIKELY(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR))
963             {
964               success = false;
965               DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
966               break;
967             }
968             // skip till next...
969             while(img)
970             {
971               img = NULL;
972               DGifGetCodeNext(gifAccessor.gif, &img);
973             }
974             // store geometry in the last frame info data
975             if(frameInfo)
976             {
977               StoreFrameInfo(gifAccessor.gif, frameInfo);
978               CheckTransparency(full, frameInfo, prop.w, prop.h);
979             }
980             // or if we dont have a frameInfo entry - create one even for stills
981             else
982             {
983               // allocate and save frame with field data
984               frameInfo = NewFrame(animated, -1, 0, 0, imageNumber + 1);
985               if(DALI_UNLIKELY(!frameInfo))
986               {
987                 success = false;
988                 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
989                 break;
990               }
991               // store geometry info from gif image
992               StoreFrameInfo(gifAccessor.gif, frameInfo);
993               // check for transparency/alpha
994               CheckTransparency(full, frameInfo, prop.w, prop.h);
995             }
996             imageNumber++;
997           }
998           // we have an extension code block - for animated gifs for sure
999           else if(rec == EXTENSION_RECORD_TYPE)
1000           {
1001             int          ext_code;
1002             GifByteType* ext = NULL;
1003
1004             // get the first extension entry
1005             DGifGetExtension(gifAccessor.gif, &ext_code, &ext);
1006             while(ext)
1007             {
1008               // graphic control extension - for animated gif data
1009               // and transparent index + flag
1010               if(ext_code == 0xf9)
1011               {
1012                 // create frame and store it in image
1013                 int transparencyIndex = (ext[1] & 1) ? ext[4] : -1;
1014                 int disposeMode       = (ext[1] >> 2) & 0x7;
1015                 int delay             = (int(ext[3]) << 8) | int(ext[2]);
1016                 frameInfo             = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
1017                 if(DALI_UNLIKELY(!frameInfo))
1018                 {
1019                   success = false;
1020                   DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
1021                   break;
1022                 }
1023               }
1024               // netscape extension indicating loop count.
1025               else if(ext_code == 0xff) /* application extension */
1026               {
1027                 if(!strncmp(reinterpret_cast<char*>(&ext[1]), "NETSCAPE2.0", 11) ||
1028                    !strncmp(reinterpret_cast<char*>(&ext[1]), "ANIMEXTS1.0", 11))
1029                 {
1030                   ext = NULL;
1031                   DGifGetExtensionNext(gifAccessor.gif, &ext);
1032                   if(ext[1] == 0x01)
1033                   {
1034                     loopCount = (int(ext[3]) << 8) | int(ext[2]);
1035                     if(loopCount > 0)
1036                     {
1037                       loopCount++;
1038                     }
1039                   }
1040                 }
1041               }
1042
1043               // and continue onto the next extension entry
1044               ext = NULL;
1045               DGifGetExtensionNext(gifAccessor.gif, &ext);
1046             }
1047           }
1048         } while(rec != TERMINATE_RECORD_TYPE && success);
1049
1050         if(success)
1051         {
1052           // if the gif main says we have more than one image or our image counting
1053           // says so, then this image is animated - indicate this
1054           if((gifAccessor.gif->ImageCount > 1) || (imageNumber > 1))
1055           {
1056             animated.animated  = 1;
1057             animated.loopCount = loopCount;
1058           }
1059           animated.frameCount = std::min(gifAccessor.gif->ImageCount, imageNumber);
1060
1061           if(!full)
1062           {
1063             prop.alpha = 1;
1064           }
1065
1066           animated.currentFrame = 1;
1067
1068           // cache global color map
1069           ColorMapObject* colorMap = gifAccessor.gif->SColorMap;
1070           if(colorMap)
1071           {
1072             cachedColor.globalCachedColor.resize(colorMap->ColorCount);
1073             for(int i = 0; i < colorMap->ColorCount; ++i)
1074             {
1075               cachedColor.globalCachedColor[i] = PixelLookup(colorMap, i);
1076             }
1077           }
1078         }
1079       }
1080     }
1081   }
1082   return success;
1083 }
1084
1085 /**
1086  * @brief Reader next frame of the gif file and populates structures accordingly.
1087  *
1088  * @param[in,out] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
1089  * @param[in,out] prop A ImageProperties structure containing information about gif data.
1090  * @param[out] pixels A pointer to buffer which will contain all pixel data of the frame on return.
1091  * @param[out] error Error code
1092  * @return The true or false whether reading was successful or not.
1093  */
1094 bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w and h
1095                    unsigned char* pixels,
1096                    int*           error)
1097 {
1098   GifAnimationData&     animated = loaderInfo.animated;
1099   LoaderInfo::FileData& fileData = loaderInfo.fileData;
1100   bool                  ret      = false;
1101   GifRecordType         rec;
1102   int                   index = 0, imageNumber = 0;
1103   FrameInfo*            frameInfo;
1104   ImageFrame*           frame              = NULL;
1105   ImageFrame*           lastPreservedFrame = NULL;
1106
1107   index = animated.currentFrame;
1108
1109   // if index is invalid for animated image - error out
1110   if(DALI_UNLIKELY((animated.animated) && ((index <= 0) || (index > animated.frameCount))))
1111   {
1112     DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
1113     return false;
1114   }
1115
1116   // find the given frame index
1117   frame = FindFrame(animated, index);
1118   if(DALI_UNLIKELY(!frame))
1119   {
1120     DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1121     return false;
1122   }
1123   else if(!(frame->loaded) || !(frame->data))
1124   {
1125     // if we want to go backwards, we likely need/want to re-decode from the
1126     // start as we have nothing to build on. If there is a gif, imageNumber
1127     // has been set already.
1128     if(loaderInfo.gifAccessor && loaderInfo.imageNumber > 0)
1129     {
1130       if((index > 0) && (index < loaderInfo.imageNumber) && (animated.animated))
1131       {
1132         loaderInfo.gifAccessor.reset();
1133         loaderInfo.imageNumber = 0;
1134       }
1135     }
1136
1137     // actually ask libgif to open the file
1138     if(!loaderInfo.gifAccessor)
1139     {
1140       loaderInfo.fileInfo.map      = fileData.globalMap;
1141       loaderInfo.fileInfo.length   = fileData.length;
1142       loaderInfo.fileInfo.position = 0;
1143       if(DALI_UNLIKELY(!loaderInfo.fileInfo.map))
1144       {
1145         DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1146         return false;
1147       }
1148       std::unique_ptr<GifAccessor> gifAccessor = std::make_unique<GifAccessor>(loaderInfo.fileInfo);
1149       if(DALI_UNLIKELY(!gifAccessor->gif))
1150       {
1151         DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1152         return false;
1153       }
1154       loaderInfo.gifAccessor = std::move(gifAccessor);
1155       loaderInfo.imageNumber = 1;
1156     }
1157
1158     // our current position is the previous frame we decoded from the file
1159     imageNumber = loaderInfo.imageNumber;
1160
1161     // walk through gif records in file to figure out info
1162     do
1163     {
1164       if(DALI_UNLIKELY(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR))
1165       {
1166         DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1167         return false;
1168       }
1169
1170       if(rec == EXTENSION_RECORD_TYPE)
1171       {
1172         int          ext_code;
1173         GifByteType* ext = NULL;
1174         DGifGetExtension(loaderInfo.gifAccessor->gif, &ext_code, &ext);
1175
1176         while(ext)
1177         {
1178           ext = NULL;
1179           DGifGetExtensionNext(loaderInfo.gifAccessor->gif, &ext);
1180         }
1181       }
1182       // get image description section
1183       else if(rec == IMAGE_DESC_RECORD_TYPE)
1184       {
1185         int          xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
1186         int          img_code;
1187         GifByteType* img;
1188         ImageFrame*  previousFrame = NULL;
1189         ImageFrame*  thisFrame     = NULL;
1190
1191         // get image desc
1192         if(DALI_UNLIKELY(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR))
1193         {
1194           DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1195           return false;
1196         }
1197
1198         // get the previous frame entry AND the current one to fill in
1199         previousFrame = FindFrame(animated, imageNumber - 1);
1200         thisFrame     = FindFrame(animated, imageNumber);
1201
1202         // if we have a frame AND we're animated AND we have no data...
1203         if((thisFrame) && (!thisFrame->data) && (animated.animated))
1204         {
1205           bool first = false;
1206
1207           // allocate it
1208           thisFrame->data = new uint32_t[prop.w * prop.h];
1209
1210           if(DALI_UNLIKELY(!thisFrame->data))
1211           {
1212             DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
1213             return false;
1214           }
1215
1216           // Lazy fill background color feature.
1217           // DecodeImage function draw range is EQUAL with previous FillImage range,
1218           // We don't need to fill background that time.
1219           // It will try to reduce the number of FillImage API call
1220           // Note : We might check overlapping. But that operation looks expensive
1221           // So, just optimize only if EQUAL case.
1222           bool     updateBackgroundColorLazy = false;
1223           uint32_t backgroundColor           = 0u;
1224           int      prevX                     = 0;
1225           int      prevY                     = 0;
1226           int      prevW                     = 0;
1227           int      prevH                     = 0;
1228
1229           // if we have no prior frame OR prior frame data... empty
1230           if((!previousFrame) || (!previousFrame->data))
1231           {
1232             first                     = true;
1233             frameInfo                 = &(thisFrame->info);
1234             updateBackgroundColorLazy = true;
1235             backgroundColor           = 0u;
1236             prevX                     = 0;
1237             prevY                     = 0;
1238             prevW                     = prop.w;
1239             prevH                     = prop.h;
1240           }
1241           // we have a prior frame to copy data from...
1242           else
1243           {
1244             frameInfo = &(previousFrame->info);
1245
1246             // fix coords of sub image in case it goes out...
1247             ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
1248
1249             // if dispose mode is not restore - then copy pre frame
1250             if(frameInfo->dispose != DISPOSE_PREVIOUS)
1251             {
1252               memcpy(thisFrame->data, previousFrame->data, prop.w * prop.h * sizeof(uint32_t));
1253             }
1254
1255             // if dispose mode is "background" then fill with bg
1256             if(frameInfo->dispose == DISPOSE_BACKGROUND)
1257             {
1258               updateBackgroundColorLazy = true;
1259               backgroundColor           = GetBackgroundColor(loaderInfo.gifAccessor->gif, frameInfo);
1260               prevX                     = x;
1261               prevY                     = y;
1262               prevW                     = w;
1263               prevH                     = h;
1264             }
1265             else if(frameInfo->dispose == DISPOSE_PREVIOUS) // GIF_DISPOSE_RESTORE
1266             {
1267               int prevIndex = 2;
1268               do
1269               {
1270                 // Find last preserved frame.
1271                 lastPreservedFrame = FindFrame(animated, imageNumber - prevIndex);
1272                 if(DALI_UNLIKELY(!lastPreservedFrame))
1273                 {
1274                   DALI_LOG_ERROR("LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND");
1275                   return false;
1276                 }
1277                 prevIndex++;
1278               } while(lastPreservedFrame && lastPreservedFrame->info.dispose == DISPOSE_PREVIOUS);
1279
1280               if(lastPreservedFrame)
1281               {
1282                 memcpy(thisFrame->data, lastPreservedFrame->data, prop.w * prop.h * sizeof(uint32_t));
1283               }
1284             }
1285           }
1286           // now draw this frame on top
1287           frameInfo = &(thisFrame->info);
1288           ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
1289
1290           if(updateBackgroundColorLazy)
1291           {
1292             // If this frame's x,y,w,h is not equal with previous x,y,w,h, FillImage. else, don't fill
1293             if(prevX != x || prevY != y || prevW != w || prevH != h)
1294             {
1295               FillImage(thisFrame->data, prop.w, backgroundColor, prevX, prevY, prevW, prevH);
1296               // Don't send background color information to DecodeImage function.
1297               updateBackgroundColorLazy = false;
1298             }
1299           }
1300           if(DALI_UNLIKELY(!DecodeImage(loaderInfo.gifAccessor->gif, loaderInfo.cachedColor, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first || updateBackgroundColorLazy, backgroundColor)))
1301           {
1302             DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1303             return false;
1304           }
1305
1306           // mark as loaded and done
1307           thisFrame->loaded = true;
1308
1309           FlushFrames(animated, prop.w, prop.h, thisFrame, previousFrame, lastPreservedFrame);
1310         }
1311         // if we have a frame BUT the image is not animated. different
1312         // path
1313         else if((thisFrame) && (!thisFrame->data) && (!animated.animated))
1314         {
1315           // if we don't have the data decoded yet - decode it
1316           if((!thisFrame->loaded) || (!thisFrame->data))
1317           {
1318             // use frame info but we WONT allocate frame pixels
1319             frameInfo = &(thisFrame->info);
1320             ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
1321
1322             // clear out all pixels only if x,y,w,h is not whole image.
1323             if(x != 0 || y != 0 || w != static_cast<int>(prop.w) || h != static_cast<int>(prop.h))
1324             {
1325               const std::uint32_t backgroundColor = GetBackgroundColor(loaderInfo.gifAccessor->gif, frameInfo);
1326               FillImage(reinterpret_cast<uint32_t*>(pixels), prop.w, backgroundColor, 0, 0, prop.w, prop.h);
1327             }
1328
1329             // and decode the gif with overwriting
1330             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)))
1331             {
1332               DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1333               return false;
1334             }
1335
1336             // mark as loaded and done
1337             thisFrame->loaded = true;
1338           }
1339           // flush mem we don't need (at expense of decode cpu)
1340         }
1341         else
1342         {
1343           // skip decoding and just walk image to next
1344           if(DALI_UNLIKELY(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR))
1345           {
1346             DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1347             return false;
1348           }
1349
1350           while(img)
1351           {
1352             img = NULL;
1353             DGifGetCodeNext(loaderInfo.gifAccessor->gif, &img);
1354           }
1355         }
1356
1357         imageNumber++;
1358         // if we found the image we wanted - get out of here
1359         if(imageNumber > index)
1360         {
1361           break;
1362         }
1363       }
1364     } while(rec != TERMINATE_RECORD_TYPE);
1365
1366     // if we are at the end of the animation or not animated, close file
1367     loaderInfo.imageNumber = imageNumber;
1368     if((animated.frameCount <= 1) || (rec == TERMINATE_RECORD_TYPE))
1369     {
1370       loaderInfo.gifAccessor.reset();
1371       loaderInfo.imageNumber = 0;
1372     }
1373   }
1374
1375   // no errors in header scan etc. so set err and return value
1376   *error = 0;
1377   ret    = true;
1378
1379   // if it was an animated image we need to copy the data to the
1380   // pixels for the image from the frame holding the data
1381   if(animated.animated && frame->data)
1382   {
1383     memcpy(pixels, frame->data, prop.w * prop.h * sizeof(uint32_t));
1384   }
1385
1386   return ret;
1387 }
1388
1389 } // unnamed namespace
1390
1391 struct GifLoading::Impl
1392 {
1393 public:
1394   Impl(const std::string& url, bool isLocalResource)
1395   : mUrl(url),
1396     mLoadSucceeded(false),
1397     mMutex()
1398   {
1399     loaderInfo.gifAccessor = nullptr;
1400     loaderInfo.fileData.fileName        = mUrl.c_str();
1401     loaderInfo.fileData.isLocalResource = isLocalResource;
1402   }
1403
1404   bool LoadGifInformation()
1405   {
1406     // Block to do not load this file again.
1407     Mutex::ScopedLock lock(mMutex);
1408     if(DALI_LIKELY(mLoadSucceeded))
1409     {
1410       return mLoadSucceeded;
1411     }
1412
1413     mLoadSucceeded = ReadHeader(loaderInfo, imageProperties);
1414     return mLoadSucceeded;
1415   }
1416
1417   // Moveable but not copyable
1418   Impl(const Impl&) = delete;
1419   Impl& operator=(const Impl&) = delete;
1420   Impl(Impl&&)                 = default;
1421   Impl& operator=(Impl&&) = default;
1422
1423   std::string     mUrl;
1424   LoaderInfo      loaderInfo;
1425   ImageProperties imageProperties;
1426   bool            mLoadSucceeded;
1427   Mutex           mMutex;
1428 };
1429
1430 AnimatedImageLoadingPtr GifLoading::New(const std::string& url, bool isLocalResource)
1431 {
1432   return AnimatedImageLoadingPtr(new GifLoading(url, isLocalResource));
1433 }
1434
1435 GifLoading::GifLoading(const std::string& url, bool isLocalResource)
1436 : mImpl(new GifLoading::Impl(url, isLocalResource))
1437 {
1438 }
1439
1440 GifLoading::~GifLoading()
1441 {
1442   delete mImpl;
1443 }
1444
1445 bool GifLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
1446 {
1447   int  error;
1448   bool ret = false;
1449
1450   if(DALI_UNLIKELY(!mImpl->LoadGifInformation()))
1451   {
1452     return false;
1453   }
1454
1455   Mutex::ScopedLock lock(mImpl->mMutex);
1456   const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof(uint32_t);
1457
1458   DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count);
1459
1460   for(int i = 0; i < count; ++i)
1461   {
1462     auto pixelBuffer = new unsigned char[bufferSize];
1463
1464     mImpl->loaderInfo.animated.currentFrame = 1 + ((frameStartIndex + i) % mImpl->loaderInfo.animated.frameCount);
1465
1466     if(ReadNextFrame(mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer, &error))
1467     {
1468       if(pixelBuffer)
1469       {
1470         pixelData.push_back(Dali::PixelData::New(pixelBuffer, bufferSize, mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY));
1471         ret = true;
1472       }
1473     }
1474   }
1475
1476   return ret;
1477 }
1478
1479 Dali::Devel::PixelBuffer GifLoading::LoadFrame(uint32_t frameIndex)
1480 {
1481   int                      error;
1482   Dali::Devel::PixelBuffer pixelBuffer;
1483
1484   DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
1485
1486   // If Gif file is still not loaded, Load the information.
1487   if(DALI_UNLIKELY(!mImpl->LoadGifInformation()))
1488   {
1489     return pixelBuffer;
1490   }
1491
1492   Mutex::ScopedLock lock(mImpl->mMutex);
1493   pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888);
1494
1495   mImpl->loaderInfo.animated.currentFrame = 1 + (frameIndex % mImpl->loaderInfo.animated.frameCount);
1496   ReadNextFrame(mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer.GetBuffer(), &error);
1497
1498   if(error != 0)
1499   {
1500     pixelBuffer = Dali::Devel::PixelBuffer();
1501   }
1502   return pixelBuffer;
1503 }
1504
1505 ImageDimensions GifLoading::GetImageSize() const
1506 {
1507   if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
1508   {
1509     mImpl->LoadGifInformation();
1510   }
1511   return ImageDimensions(mImpl->imageProperties.w, mImpl->imageProperties.h);
1512 }
1513
1514 uint32_t GifLoading::GetImageCount() const
1515 {
1516   if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
1517   {
1518     mImpl->LoadGifInformation();
1519   }
1520   return mImpl->loaderInfo.animated.frameCount;
1521 }
1522
1523 uint32_t GifLoading::GetFrameInterval(uint32_t frameIndex) const
1524 {
1525   if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
1526   {
1527     mImpl->LoadGifInformation();
1528   }
1529
1530   uint32_t interval = 0u;
1531   if(DALI_LIKELY(mImpl->mLoadSucceeded))
1532   {
1533     interval = mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
1534   }
1535   return interval;
1536 }
1537
1538 std::string GifLoading::GetUrl() const
1539 {
1540   return mImpl->mUrl;
1541 }
1542
1543 bool GifLoading::HasLoadingSucceeded() const
1544 {
1545   return mImpl->mLoadSucceeded;
1546 }
1547
1548 } // namespace Adaptor
1549
1550 } // namespace Internal
1551
1552 } // namespace Dali