685f206eeb16e11496af57b27f00f7134cb25c73
[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  * @param[out] error Error code
875  * @return The true or false whether reading was successful or not.
876  */
877 bool ReadHeader(LoaderInfo&      loaderInfo,
878                 ImageProperties& prop, //output struct
879                 int*             error)
880 {
881   GifAnimationData&     animated    = loaderInfo.animated;
882   GifCachedColorData&   cachedColor = loaderInfo.cachedColor;
883   LoaderInfo::FileData& fileData    = loaderInfo.fileData;
884   bool                  success     = false;
885   LoaderInfo::FileInfo  fileInfo;
886   GifRecordType         rec;
887
888   // it is possible which gif file have error midle of frames,
889   // in that case we should play gif file until meet error frame.
890   int        imageNumber = 0;
891   int        loopCount   = -1;
892   FrameInfo* frameInfo   = NULL;
893   bool       full        = true;
894
895   success = fileData.LoadFile();
896   if(DALI_UNLIKELY(!success || !fileData.globalMap))
897   {
898     success = false;
899     DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
900   }
901   else
902   {
903     fileInfo.map      = fileData.globalMap;
904     fileInfo.length   = fileData.length;
905     fileInfo.position = 0;
906     GifAccessor gifAccessor(fileInfo);
907
908     if(gifAccessor.gif)
909     {
910       // get the gif "screen size" (the actual image size)
911       prop.w = gifAccessor.gif->SWidth;
912       prop.h = gifAccessor.gif->SHeight;
913
914       // if size is invalid - abort here
915       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)))
916       {
917         if(IMG_TOO_BIG(prop.w, prop.h))
918         {
919           success = false;
920           DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
921         }
922         else
923         {
924           success = false;
925           DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
926         }
927       }
928       else
929       {
930         // walk through gif records in file to figure out info
931         success = true;
932         do
933         {
934           if(DGifGetRecordType(gifAccessor.gif, &rec) == GIF_ERROR)
935           {
936             // if we have a gif that ends part way through a sequence
937             // (or animation) consider it valid and just break - no error
938             if(imageNumber <= 1)
939             {
940               success = true;
941             }
942             else
943             {
944               success = false;
945               DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
946             }
947             break;
948           }
949
950           // get image description section
951           if(rec == IMAGE_DESC_RECORD_TYPE)
952           {
953             int          img_code;
954             GifByteType* img;
955
956             // get image desc
957             if(DALI_UNLIKELY(DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR))
958             {
959               success = false;
960               DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
961               break;
962             }
963             // skip decoding and just walk image to next
964             if(DALI_UNLIKELY(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR))
965             {
966               success = false;
967               DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
968               break;
969             }
970             // skip till next...
971             while(img)
972             {
973               img = NULL;
974               DGifGetCodeNext(gifAccessor.gif, &img);
975             }
976             // store geometry in the last frame info data
977             if(frameInfo)
978             {
979               StoreFrameInfo(gifAccessor.gif, frameInfo);
980               CheckTransparency(full, frameInfo, prop.w, prop.h);
981             }
982             // or if we dont have a frameInfo entry - create one even for stills
983             else
984             {
985               // allocate and save frame with field data
986               frameInfo = NewFrame(animated, -1, 0, 0, imageNumber + 1);
987               if(DALI_UNLIKELY(!frameInfo))
988               {
989                 success = false;
990                 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
991                 break;
992               }
993               // store geometry info from gif image
994               StoreFrameInfo(gifAccessor.gif, frameInfo);
995               // check for transparency/alpha
996               CheckTransparency(full, frameInfo, prop.w, prop.h);
997             }
998             imageNumber++;
999           }
1000           // we have an extension code block - for animated gifs for sure
1001           else if(rec == EXTENSION_RECORD_TYPE)
1002           {
1003             int          ext_code;
1004             GifByteType* ext = NULL;
1005
1006             // get the first extension entry
1007             DGifGetExtension(gifAccessor.gif, &ext_code, &ext);
1008             while(ext)
1009             {
1010               // graphic control extension - for animated gif data
1011               // and transparent index + flag
1012               if(ext_code == 0xf9)
1013               {
1014                 // create frame and store it in image
1015                 int transparencyIndex = (ext[1] & 1) ? ext[4] : -1;
1016                 int disposeMode       = (ext[1] >> 2) & 0x7;
1017                 int delay             = (int(ext[3]) << 8) | int(ext[2]);
1018                 frameInfo             = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
1019                 if(DALI_UNLIKELY(!frameInfo))
1020                 {
1021                   success = false;
1022                   DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
1023                   break;
1024                 }
1025               }
1026               // netscape extension indicating loop count.
1027               else if(ext_code == 0xff) /* application extension */
1028               {
1029                 if(!strncmp(reinterpret_cast<char*>(&ext[1]), "NETSCAPE2.0", 11) ||
1030                    !strncmp(reinterpret_cast<char*>(&ext[1]), "ANIMEXTS1.0", 11))
1031                 {
1032                   ext = NULL;
1033                   DGifGetExtensionNext(gifAccessor.gif, &ext);
1034                   if(ext[1] == 0x01)
1035                   {
1036                     loopCount = (int(ext[3]) << 8) | int(ext[2]);
1037                     if(loopCount > 0)
1038                     {
1039                       loopCount++;
1040                     }
1041                   }
1042                 }
1043               }
1044
1045               // and continue onto the next extension entry
1046               ext = NULL;
1047               DGifGetExtensionNext(gifAccessor.gif, &ext);
1048             }
1049           }
1050         } while(rec != TERMINATE_RECORD_TYPE && success);
1051
1052         if(success)
1053         {
1054           // if the gif main says we have more than one image or our image counting
1055           // says so, then this image is animated - indicate this
1056           if((gifAccessor.gif->ImageCount > 1) || (imageNumber > 1))
1057           {
1058             animated.animated  = 1;
1059             animated.loopCount = loopCount;
1060           }
1061           animated.frameCount = std::min(gifAccessor.gif->ImageCount, imageNumber);
1062
1063           if(!full)
1064           {
1065             prop.alpha = 1;
1066           }
1067
1068           animated.currentFrame = 1;
1069
1070           // cache global color map
1071           ColorMapObject* colorMap = gifAccessor.gif->SColorMap;
1072           if(colorMap)
1073           {
1074             cachedColor.globalCachedColor.resize(colorMap->ColorCount);
1075             for(int i = 0; i < colorMap->ColorCount; ++i)
1076             {
1077               cachedColor.globalCachedColor[i] = PixelLookup(colorMap, i);
1078             }
1079           }
1080
1081           // no errors in header scan etc. so set err and return value
1082           *error = 0;
1083         }
1084       }
1085     }
1086   }
1087   return success;
1088 }
1089
1090 /**
1091  * @brief Reader next frame of the gif file and populates structures accordingly.
1092  *
1093  * @param[in,out] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
1094  * @param[in,out] prop A ImageProperties structure containing information about gif data.
1095  * @param[out] pixels A pointer to buffer which will contain all pixel data of the frame on return.
1096  * @param[out] error Error code
1097  * @return The true or false whether reading was successful or not.
1098  */
1099 bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w and h
1100                    unsigned char* pixels,
1101                    int*           error)
1102 {
1103   GifAnimationData&     animated = loaderInfo.animated;
1104   LoaderInfo::FileData& fileData = loaderInfo.fileData;
1105   bool                  ret      = false;
1106   GifRecordType         rec;
1107   int                   index = 0, imageNumber = 0;
1108   FrameInfo*            frameInfo;
1109   ImageFrame*           frame              = NULL;
1110   ImageFrame*           lastPreservedFrame = NULL;
1111
1112   index = animated.currentFrame;
1113
1114   // if index is invalid for animated image - error out
1115   if(DALI_UNLIKELY((animated.animated) && ((index <= 0) || (index > animated.frameCount))))
1116   {
1117     DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
1118     return false;
1119   }
1120
1121   // find the given frame index
1122   frame = FindFrame(animated, index);
1123   if(DALI_UNLIKELY(!frame))
1124   {
1125     DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1126     return false;
1127   }
1128   else if(!(frame->loaded) || !(frame->data))
1129   {
1130     // if we want to go backwards, we likely need/want to re-decode from the
1131     // start as we have nothing to build on. If there is a gif, imageNumber
1132     // has been set already.
1133     if(loaderInfo.gifAccessor && loaderInfo.imageNumber > 0)
1134     {
1135       if((index > 0) && (index < loaderInfo.imageNumber) && (animated.animated))
1136       {
1137         loaderInfo.gifAccessor.reset();
1138         loaderInfo.imageNumber = 0;
1139       }
1140     }
1141
1142     // actually ask libgif to open the file
1143     if(!loaderInfo.gifAccessor)
1144     {
1145       loaderInfo.fileInfo.map      = fileData.globalMap;
1146       loaderInfo.fileInfo.length   = fileData.length;
1147       loaderInfo.fileInfo.position = 0;
1148       if(DALI_UNLIKELY(!loaderInfo.fileInfo.map))
1149       {
1150         DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1151         return false;
1152       }
1153       std::unique_ptr<GifAccessor> gifAccessor = std::make_unique<GifAccessor>(loaderInfo.fileInfo);
1154       if(DALI_UNLIKELY(!gifAccessor->gif))
1155       {
1156         DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1157         return false;
1158       }
1159       loaderInfo.gifAccessor = std::move(gifAccessor);
1160       loaderInfo.imageNumber = 1;
1161     }
1162
1163     // our current position is the previous frame we decoded from the file
1164     imageNumber = loaderInfo.imageNumber;
1165
1166     // walk through gif records in file to figure out info
1167     do
1168     {
1169       if(DALI_UNLIKELY(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR))
1170       {
1171         DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1172         return false;
1173       }
1174
1175       if(rec == EXTENSION_RECORD_TYPE)
1176       {
1177         int          ext_code;
1178         GifByteType* ext = NULL;
1179         DGifGetExtension(loaderInfo.gifAccessor->gif, &ext_code, &ext);
1180
1181         while(ext)
1182         {
1183           ext = NULL;
1184           DGifGetExtensionNext(loaderInfo.gifAccessor->gif, &ext);
1185         }
1186       }
1187       // get image description section
1188       else if(rec == IMAGE_DESC_RECORD_TYPE)
1189       {
1190         int          xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
1191         int          img_code;
1192         GifByteType* img;
1193         ImageFrame*  previousFrame = NULL;
1194         ImageFrame*  thisFrame     = NULL;
1195
1196         // get image desc
1197         if(DALI_UNLIKELY(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR))
1198         {
1199           DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1200           return false;
1201         }
1202
1203         // get the previous frame entry AND the current one to fill in
1204         previousFrame = FindFrame(animated, imageNumber - 1);
1205         thisFrame     = FindFrame(animated, imageNumber);
1206
1207         // if we have a frame AND we're animated AND we have no data...
1208         if((thisFrame) && (!thisFrame->data) && (animated.animated))
1209         {
1210           bool first = false;
1211
1212           // allocate it
1213           thisFrame->data = new uint32_t[prop.w * prop.h];
1214
1215           if(DALI_UNLIKELY(!thisFrame->data))
1216           {
1217             DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
1218             return false;
1219           }
1220
1221           // Lazy fill background color feature.
1222           // DecodeImage function draw range is EQUAL with previous FillImage range,
1223           // We don't need to fill background that time.
1224           // It will try to reduce the number of FillImage API call
1225           // Note : We might check overlapping. But that operation looks expensive
1226           // So, just optimize only if EQUAL case.
1227           bool     updateBackgroundColorLazy = false;
1228           uint32_t backgroundColor           = 0u;
1229           int      prevX                     = 0;
1230           int      prevY                     = 0;
1231           int      prevW                     = 0;
1232           int      prevH                     = 0;
1233
1234           // if we have no prior frame OR prior frame data... empty
1235           if((!previousFrame) || (!previousFrame->data))
1236           {
1237             first                     = true;
1238             frameInfo                 = &(thisFrame->info);
1239             updateBackgroundColorLazy = true;
1240             backgroundColor           = 0u;
1241             prevX                     = 0;
1242             prevY                     = 0;
1243             prevW                     = prop.w;
1244             prevH                     = prop.h;
1245           }
1246           // we have a prior frame to copy data from...
1247           else
1248           {
1249             frameInfo = &(previousFrame->info);
1250
1251             // fix coords of sub image in case it goes out...
1252             ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
1253
1254             // if dispose mode is not restore - then copy pre frame
1255             if(frameInfo->dispose != DISPOSE_PREVIOUS)
1256             {
1257               memcpy(thisFrame->data, previousFrame->data, prop.w * prop.h * sizeof(uint32_t));
1258             }
1259
1260             // if dispose mode is "background" then fill with bg
1261             if(frameInfo->dispose == DISPOSE_BACKGROUND)
1262             {
1263               updateBackgroundColorLazy = true;
1264               backgroundColor           = GetBackgroundColor(loaderInfo.gifAccessor->gif, frameInfo);
1265               prevX                     = x;
1266               prevY                     = y;
1267               prevW                     = w;
1268               prevH                     = h;
1269             }
1270             else if(frameInfo->dispose == DISPOSE_PREVIOUS) // GIF_DISPOSE_RESTORE
1271             {
1272               int prevIndex = 2;
1273               do
1274               {
1275                 // Find last preserved frame.
1276                 lastPreservedFrame = FindFrame(animated, imageNumber - prevIndex);
1277                 if(DALI_UNLIKELY(!lastPreservedFrame))
1278                 {
1279                   DALI_LOG_ERROR("LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND");
1280                   return false;
1281                 }
1282                 prevIndex++;
1283               } while(lastPreservedFrame && lastPreservedFrame->info.dispose == DISPOSE_PREVIOUS);
1284
1285               if(lastPreservedFrame)
1286               {
1287                 memcpy(thisFrame->data, lastPreservedFrame->data, prop.w * prop.h * sizeof(uint32_t));
1288               }
1289             }
1290           }
1291           // now draw this frame on top
1292           frameInfo = &(thisFrame->info);
1293           ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
1294
1295           if(updateBackgroundColorLazy)
1296           {
1297             // If this frame's x,y,w,h is not equal with previous x,y,w,h, FillImage. else, don't fill
1298             if(prevX != x || prevY != y || prevW != w || prevH != h)
1299             {
1300               FillImage(thisFrame->data, prop.w, backgroundColor, prevX, prevY, prevW, prevH);
1301               // Don't send background color information to DecodeImage function.
1302               updateBackgroundColorLazy = false;
1303             }
1304           }
1305           if(DALI_UNLIKELY(!DecodeImage(loaderInfo.gifAccessor->gif, loaderInfo.cachedColor, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first || updateBackgroundColorLazy, backgroundColor)))
1306           {
1307             DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1308             return false;
1309           }
1310
1311           // mark as loaded and done
1312           thisFrame->loaded = true;
1313
1314           FlushFrames(animated, prop.w, prop.h, thisFrame, previousFrame, lastPreservedFrame);
1315         }
1316         // if we have a frame BUT the image is not animated. different
1317         // path
1318         else if((thisFrame) && (!thisFrame->data) && (!animated.animated))
1319         {
1320           // if we don't have the data decoded yet - decode it
1321           if((!thisFrame->loaded) || (!thisFrame->data))
1322           {
1323             // use frame info but we WONT allocate frame pixels
1324             frameInfo = &(thisFrame->info);
1325             ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
1326
1327             // clear out all pixels only if x,y,w,h is not whole image.
1328             if(x != 0 || y != 0 || w != static_cast<int>(prop.w) || h != static_cast<int>(prop.h))
1329             {
1330               const std::uint32_t backgroundColor = GetBackgroundColor(loaderInfo.gifAccessor->gif, frameInfo);
1331               FillImage(reinterpret_cast<uint32_t*>(pixels), prop.w, backgroundColor, 0, 0, prop.w, prop.h);
1332             }
1333
1334             // and decode the gif with overwriting
1335             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)))
1336             {
1337               DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
1338               return false;
1339             }
1340
1341             // mark as loaded and done
1342             thisFrame->loaded = true;
1343           }
1344           // flush mem we don't need (at expense of decode cpu)
1345         }
1346         else
1347         {
1348           // skip decoding and just walk image to next
1349           if(DALI_UNLIKELY(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR))
1350           {
1351             DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
1352             return false;
1353           }
1354
1355           while(img)
1356           {
1357             img = NULL;
1358             DGifGetCodeNext(loaderInfo.gifAccessor->gif, &img);
1359           }
1360         }
1361
1362         imageNumber++;
1363         // if we found the image we wanted - get out of here
1364         if(imageNumber > index)
1365         {
1366           break;
1367         }
1368       }
1369     } while(rec != TERMINATE_RECORD_TYPE);
1370
1371     // if we are at the end of the animation or not animated, close file
1372     loaderInfo.imageNumber = imageNumber;
1373     if((animated.frameCount <= 1) || (rec == TERMINATE_RECORD_TYPE))
1374     {
1375       loaderInfo.gifAccessor.reset();
1376       loaderInfo.imageNumber = 0;
1377     }
1378   }
1379
1380   // no errors in header scan etc. so set err and return value
1381   *error = 0;
1382   ret    = true;
1383
1384   // if it was an animated image we need to copy the data to the
1385   // pixels for the image from the frame holding the data
1386   if(animated.animated && frame->data)
1387   {
1388     memcpy(pixels, frame->data, prop.w * prop.h * sizeof(uint32_t));
1389   }
1390
1391   return ret;
1392 }
1393
1394 } // unnamed namespace
1395
1396 struct GifLoading::Impl
1397 {
1398 public:
1399   Impl(const std::string& url, bool isLocalResource)
1400   : mUrl(url),
1401     mLoadSucceeded(true),
1402     mMutex()
1403   {
1404     loaderInfo.gifAccessor = nullptr;
1405     int error;
1406     loaderInfo.fileData.fileName        = mUrl.c_str();
1407     loaderInfo.fileData.isLocalResource = isLocalResource;
1408
1409     mLoadSucceeded = ReadHeader(loaderInfo, imageProperties, &error);
1410   }
1411
1412   // Moveable but not copyable
1413   Impl(const Impl&) = delete;
1414   Impl& operator=(const Impl&) = delete;
1415   Impl(Impl&&)                 = default;
1416   Impl& operator=(Impl&&) = default;
1417
1418   std::string     mUrl;
1419   LoaderInfo      loaderInfo;
1420   ImageProperties imageProperties;
1421   bool            mLoadSucceeded;
1422   Mutex           mMutex;
1423 };
1424
1425 AnimatedImageLoadingPtr GifLoading::New(const std::string& url, bool isLocalResource)
1426 {
1427   return AnimatedImageLoadingPtr(new GifLoading(url, isLocalResource));
1428 }
1429
1430 GifLoading::GifLoading(const std::string& url, bool isLocalResource)
1431 : mImpl(new GifLoading::Impl(url, isLocalResource))
1432 {
1433 }
1434
1435 GifLoading::~GifLoading()
1436 {
1437   delete mImpl;
1438 }
1439
1440 bool GifLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
1441 {
1442   int  error;
1443   bool ret = false;
1444
1445   Mutex::ScopedLock lock(mImpl->mMutex);
1446   if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
1447   {
1448     return false;
1449   }
1450
1451   const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof(uint32_t);
1452
1453   DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count);
1454
1455   for(int i = 0; i < count; ++i)
1456   {
1457     auto pixelBuffer = new unsigned char[bufferSize];
1458
1459     mImpl->loaderInfo.animated.currentFrame = 1 + ((frameStartIndex + i) % mImpl->loaderInfo.animated.frameCount);
1460
1461     if(ReadNextFrame(mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer, &error))
1462     {
1463       if(pixelBuffer)
1464       {
1465         pixelData.push_back(Dali::PixelData::New(pixelBuffer, bufferSize, mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY));
1466         ret = true;
1467       }
1468     }
1469   }
1470
1471   return ret;
1472 }
1473
1474 Dali::Devel::PixelBuffer GifLoading::LoadFrame(uint32_t frameIndex)
1475 {
1476   int                      error;
1477   Dali::Devel::PixelBuffer pixelBuffer;
1478   if(!mImpl->mLoadSucceeded)
1479   {
1480     return pixelBuffer;
1481   }
1482
1483   Mutex::ScopedLock lock(mImpl->mMutex);
1484   DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
1485
1486   pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888);
1487
1488   mImpl->loaderInfo.animated.currentFrame = 1 + (frameIndex % mImpl->loaderInfo.animated.frameCount);
1489   ReadNextFrame(mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer.GetBuffer(), &error);
1490
1491   if(error != 0)
1492   {
1493     pixelBuffer = Dali::Devel::PixelBuffer();
1494   }
1495   return pixelBuffer;
1496 }
1497
1498 ImageDimensions GifLoading::GetImageSize() const
1499 {
1500   return ImageDimensions(mImpl->imageProperties.w, mImpl->imageProperties.h);
1501 }
1502
1503 uint32_t GifLoading::GetImageCount() const
1504 {
1505   return mImpl->loaderInfo.animated.frameCount;
1506 }
1507
1508 uint32_t GifLoading::GetFrameInterval(uint32_t frameIndex) const
1509 {
1510   return mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
1511 }
1512
1513 std::string GifLoading::GetUrl() const
1514 {
1515   return mImpl->mUrl;
1516 }
1517
1518 bool GifLoading::HasLoadingSucceeded() const
1519 {
1520   return mImpl->mLoadSucceeded;
1521 }
1522
1523 } // namespace Adaptor
1524
1525 } // namespace Internal
1526
1527 } // namespace Dali