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