[dali_2.3.33] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / gif-loading.cpp
index 6b0d709..c35899a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
 #include <cstring>
 #include <memory>
 
+#include <dali/devel-api/threading/mutex.h>
 #include <dali/integration-api/debug.h>
 #include <dali/internal/imaging/common/file-download.h>
 #include <dali/internal/system/common/file-reader.h>
   ((static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h)) >= \
    ((1ULL << (29 * (sizeof(void*) / 4))) - 2048))
 
-#define LOADERR(x)     \
-  do                   \
-  {                    \
-    DALI_LOG_ERROR(x); \
-    goto on_error;     \
+#define PRINT_ERROR_AND_EXIT(errorFormat, ...)  \
+  do                                            \
+  {                                             \
+    DALI_LOG_ERROR(errorFormat, ##__VA_ARGS__); \
+    if(rows)                                    \
+    {                                           \
+      free(rows);                               \
+    }                                           \
+    return ret;                                 \
   } while(0)
 
 namespace Dali
@@ -60,6 +65,8 @@ Debug::Filter* gGifLoadingLogFilter = Debug::Filter::New(Debug::NoLogging, false
 const int        IMG_MAX_SIZE                = 65000;
 constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
 
+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
+
 #if GIFLIB_MAJOR < 5
 const int DISPOSE_BACKGROUND = 2; /* Set area too background color */
 const int DISPOSE_PREVIOUS   = 3; /* Restore to previous content */
@@ -130,6 +137,15 @@ struct GifAnimationData
   bool                    animated;
 };
 
+struct GifCachedColorData
+{
+  GifCachedColorData() = default;
+
+  // precalculated colormap table
+  std::vector<std::uint32_t> globalCachedColor{};
+  std::vector<std::uint32_t> localCachedColor{};
+};
+
 // Forward declaration
 struct GifAccessor;
 
@@ -186,6 +202,7 @@ struct LoaderInfo
 
   FileData                     fileData;
   GifAnimationData             animated;
+  GifCachedColorData           cachedColor;
   std::unique_ptr<GifAccessor> gifAccessor{nullptr};
   int                          imageNumber{0};
   FileInfo                     fileInfo;
@@ -246,7 +263,7 @@ struct GifAccessor
   {
     LoaderInfo::FileInfo* fi = reinterpret_cast<LoaderInfo::FileInfo*>(gifFileType->UserData);
 
-    if(fi->position >= fi->length)
+    if(DALI_UNLIKELY(fi->position >= fi->length))
     {
       return 0; // if at or past end - no
     }
@@ -280,26 +297,31 @@ bool LoaderInfo::FileData::LoadLocalFile()
 {
   Internal::Platform::FileReader fileReader(fileName);
   FILE*                          fp = fileReader.GetFile();
-  if(fp == NULL)
+  if(DALI_UNLIKELY(fp == NULL))
   {
     return false;
   }
 
-  if(fseek(fp, 0, SEEK_END) <= -1)
+  if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END) <= -1))
   {
     return false;
   }
 
   length = ftell(fp);
-  if(length <= -1)
+  if(DALI_UNLIKELY(length <= -1))
   {
     return false;
   }
 
-  if((!fseek(fp, 0, SEEK_SET)))
+  if(DALI_LIKELY(!fseek(fp, 0, SEEK_SET)))
   {
-    globalMap = reinterpret_cast<GifByteType*>(malloc(sizeof(GifByteType) * length));
-    length    = fread(globalMap, sizeof(GifByteType), length, fp);
+    globalMap = reinterpret_cast<GifByteType*>(malloc(sizeof(GifByteType) * static_cast<unsigned long long>(length)));
+    if(DALI_UNLIKELY(globalMap == nullptr))
+    {
+      DALI_LOG_ERROR("malloc is failed. request malloc size : %llu\n", sizeof(GifByteType) * static_cast<unsigned long long>(length));
+      return false;
+    }
+    length = fread(globalMap, sizeof(GifByteType), length, fp);
   }
   else
   {
@@ -316,21 +338,28 @@ bool LoaderInfo::FileData::LoadRemoteFile()
   size_t                dataSize;
 
   succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(fileName, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE);
-  if(succeeded)
+  if(DALI_LIKELY(succeeded))
   {
     size_t blobSize = dataBuffer.Size();
-    if(blobSize > 0U)
+    if(DALI_LIKELY(blobSize > 0U))
     {
       // Open a file handle on the memory buffer:
       Dali::Internal::Platform::FileReader fileReader(dataBuffer, blobSize);
       FILE* const                          fp = fileReader.GetFile();
-      if(NULL != fp)
+      if(DALI_LIKELY(NULL != fp))
       {
-        if((!fseek(fp, 0, SEEK_SET)))
+        if(DALI_LIKELY(!fseek(fp, 0, SEEK_SET)))
         {
           globalMap = reinterpret_cast<GifByteType*>(malloc(sizeof(GifByteType) * blobSize));
-          length    = fread(globalMap, sizeof(GifByteType), blobSize, fp);
-          succeeded = true;
+          if(DALI_UNLIKELY(globalMap == nullptr))
+          {
+            DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", sizeof(GifByteType) * blobSize);
+          }
+          else
+          {
+            length    = fread(globalMap, sizeof(GifByteType), blobSize, fp);
+            succeeded = true;
+          }
         }
         else
         {
@@ -352,19 +381,53 @@ bool LoaderInfo::FileData::LoadRemoteFile()
  *
  * @param[in] animated A structure containing GIF animation data
  * @param[in] index Frame index to be searched in GIF
- * @return A pointer to the ImageFrame.
+ * @return single 32-bit (ABGR) value.
  */
-inline int CombinePixelABGR(int a, int r, int g, int b)
+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)
 {
   return (((a) << 24) + ((b) << 16) + ((g) << 8) + (r));
 }
 
-inline int PixelLookup(ColorMapObject* colorMap, int index)
+inline std::uint32_t PixelLookup(const ColorMapObject* const& colorMap, int index)
 {
   return CombinePixelABGR(0xFF, colorMap->Colors[index].Red, colorMap->Colors[index].Green, colorMap->Colors[index].Blue);
 }
 
 /**
+ * @brief Get the Background Color from frameInfo
+ *
+ * @param[in] gif A pointer pointing to GIF File Type
+ * @param[in] frameInfo A pointer pointing to Frame Information data
+ * @return single 32-bit (ABGR) value. of background color
+ */
+std::uint32_t GetBackgroundColor(GifFileType* gif, FrameInfo* frameInfo)
+{
+  if(frameInfo->transparent < 0)
+  {
+    ColorMapObject* colorMap;
+    int             backGroundColor;
+
+    // work out color to use from colorMap
+    if(gif->Image.ColorMap)
+    {
+      colorMap = gif->Image.ColorMap;
+    }
+    else
+    {
+      colorMap = gif->SColorMap;
+    }
+    backGroundColor = gif->SBackGroundColor;
+    // Get background color from colormap
+    return PixelLookup(colorMap, backGroundColor);
+  }
+  else
+  {
+    // transparent
+    return 0;
+  }
+}
+
+/**
  * @brief Brute force find frame index - gifs are normally small so ok for now.
  *
  * @param[in] animated A structure containing GIF animation data
@@ -387,66 +450,48 @@ ImageFrame* FindFrame(const GifAnimationData& animated, int index)
  * @brief Fill in an image with a specific rgba color value.
  *
  * @param[in] data A pointer pointing to an image data
- * @param[in] row A int containing the number of rows in an image
+ * @param[in] stride A int containing the number of stride in an image
  * @param[in] val A uint32_t containing rgba color value
  * @param[in] x X-coordinate used an offset to calculate pixel position
  * @param[in] y Y-coordinate used an offset to calculate pixel position
  * @param[in] width Width of the image
  * @param[in] height Height of the image
  */
-void FillImage(uint32_t* data, int row, uint32_t val, int x, int y, int width, int height)
+void FillImage(uint32_t* data, int stride, uint32_t val, int x, int y, int width, int height)
 {
-  int       xAxis, yAxis;
   uint32_t* pixelPosition;
 
-  for(yAxis = 0; yAxis < height; yAxis++)
-  {
-    pixelPosition = data + ((y + yAxis) * row) + x;
-    for(xAxis = 0; xAxis < width; xAxis++)
-    {
-      *pixelPosition = val;
-      pixelPosition++;
-    }
-  }
-}
-
-/**
- * @brief Fill a rgba data pixle blob with a frame color (bg or trans)
- *
- * @param[in] data A pointer pointing to an image data
- * @param[in] row A int containing the number of rows in an image
- * @param[in] gif A pointer pointing to GIF File Type
- * @param[in] frameInfo A pointer pointing to Frame Information data
- * @param[in] x X-coordinate used an offset to calculate pixel position
- * @param[in] y Y-coordinate used an offset to calculate pixel position
- * @param[in] width Width of the image
- * @param[in] height Height of the image
- */
-void FillFrame(uint32_t* data, int row, GifFileType* gif, FrameInfo* frameInfo, int x, int y, int w, int h)
-{
-  // solid color fill for pre frame region
-  if(frameInfo->transparent < 0)
+  // Boost time if stride == width and x == 0. We can assume that all pointer is continuous.
+  if(x == 0 && stride == width)
   {
-    ColorMapObject* colorMap;
-    int             backGroundColor;
-
-    // work out color to use from colorMap
-    if(gif->Image.ColorMap)
+    pixelPosition = data + (y * stride);
+    // Clear as white or transparent
+    // Special case. we can use memset.
+    if(val == 0x00 || val == 0xffffffffu)
     {
-      colorMap = gif->Image.ColorMap;
+      const std::int8_t setupVal = val & 0xff;
+      memset(pixelPosition, setupVal, width * height * sizeof(std::uint32_t));
     }
     else
     {
-      colorMap = gif->SColorMap;
+      for(int byteCount = 0; byteCount < width * height; ++byteCount)
+      {
+        *pixelPosition = val;
+        ++pixelPosition;
+      }
     }
-    backGroundColor = gif->SBackGroundColor;
-    // and do the fill
-    FillImage(data, row, CombinePixelABGR(0xff, colorMap->Colors[backGroundColor].Red, colorMap->Colors[backGroundColor].Green, colorMap->Colors[backGroundColor].Blue), x, y, w, h);
   }
-  // fill in region with 0 (transparent)
   else
   {
-    FillImage(data, row, 0, x, y, w, h);
+    for(int yAxis = 0; yAxis < height; ++yAxis)
+    {
+      pixelPosition = data + ((y + yAxis) * stride) + x;
+      for(int xAxis = 0; xAxis < width; ++xAxis)
+      {
+        *pixelPosition = val;
+        ++pixelPosition;
+      }
+    }
   }
 }
 
@@ -612,11 +657,212 @@ FrameInfo* NewFrame(GifAnimationData& animated, int transparent, int dispose, in
   return &(animated.frames.back().info);
 }
 
+/// FILL (overwrite with transparency kept)
+void FillOverwriteWithTransparencyKept(
+  const std::uint32_t*& cachedColorPtr,
+  int&                  xx,
+  int&                  yy,
+  const int             x,
+  const int             y,
+  const int             w,
+  const int             h,
+  const int             xin,
+  const int             yin,
+  const int             rowpix,
+  const int             transparent,
+  const uint32_t        fillColor,
+  int&                  pix,
+  uint32_t*&            p,
+  uint32_t*&            data,
+  GifRowType*&          rows,
+  ColorMapObject*&      colorMap)
+{
+  // if we use cachedColor, use it
+  if(cachedColorPtr)
+  {
+    for(yy = 0; yy < h; yy++)
+    {
+      p = data + ((y + yy) * rowpix) + x;
+      for(xx = 0; xx < w; xx++)
+      {
+        pix = rows[yin + yy][xin + xx];
+        if(pix != transparent)
+        {
+          *p = cachedColorPtr[pix];
+        }
+        else
+        {
+          *p = fillColor;
+        }
+        p++;
+      }
+    }
+  }
+  // we don't have cachedColor. use PixelLookup function.
+  else
+  {
+    for(yy = 0; yy < h; yy++)
+    {
+      p = data + ((y + yy) * rowpix) + x;
+      for(xx = 0; xx < w; xx++)
+      {
+        pix = rows[yin + yy][xin + xx];
+        if(pix != transparent)
+        {
+          *p = PixelLookup(colorMap, pix);
+        }
+        else
+        {
+          *p = fillColor;
+        }
+        p++;
+      }
+    }
+  }
+}
+
+/// Paste on top with transparent pixels untouched
+void PasteOnTopWithTransparentPixelsUntouched(
+  const std::uint32_t*& cachedColorPtr,
+  int&                  xx,
+  int&                  yy,
+  const int             x,
+  const int             y,
+  const int             w,
+  const int             h,
+  const int             xin,
+  const int             yin,
+  const int             rowpix,
+  const int             transparent,
+  const uint32_t        fillColor,
+  int&                  pix,
+  uint32_t*&            p,
+  uint32_t*&            data,
+  GifRowType*&          rows,
+  ColorMapObject*&      colorMap)
+{
+  // if we use cachedColor, use it
+  if(cachedColorPtr)
+  {
+    for(yy = 0; yy < h; yy++)
+    {
+      p = data + ((y + yy) * rowpix) + x;
+      for(xx = 0; xx < w; xx++)
+      {
+        pix = rows[yin + yy][xin + xx];
+        if(pix != transparent)
+        {
+          *p = cachedColorPtr[pix];
+        }
+        p++;
+      }
+    }
+  }
+  // we don't have cachedColor. use PixelLookup function.
+  else
+  {
+    for(yy = 0; yy < h; yy++)
+    {
+      p = data + ((y + yy) * rowpix) + x;
+      for(xx = 0; xx < w; xx++)
+      {
+        pix = rows[yin + yy][xin + xx];
+        if(pix != transparent)
+        {
+          *p = PixelLookup(colorMap, pix);
+        }
+        p++;
+      }
+    }
+  }
+}
+
+void HandleTransparentPixels(
+  const bool            fill,
+  const std::uint32_t*& cachedColorPtr,
+  int&                  xx,
+  int&                  yy,
+  const int             x,
+  const int             y,
+  const int             w,
+  const int             h,
+  const int             xin,
+  const int             yin,
+  const int             rowpix,
+  const int             transparent,
+  const uint32_t        fillColor,
+  int&                  pix,
+  uint32_t*&            p,
+  uint32_t*&            data,
+  GifRowType*&          rows,
+  ColorMapObject*&      colorMap)
+{
+  if(fill)
+  {
+    FillOverwriteWithTransparencyKept(cachedColorPtr, xx, yy, x, y, w, h, xin, yin, rowpix, transparent, fillColor, pix, p, data, rows, colorMap);
+  }
+  else
+  {
+    PasteOnTopWithTransparentPixelsUntouched(cachedColorPtr, xx, yy, x, y, w, h, xin, yin, rowpix, transparent, fillColor, pix, p, data, rows, colorMap);
+  }
+}
+
+void HandleNonTransparentPixels(
+  const std::uint32_t*& cachedColorPtr,
+  int&                  xx,
+  int&                  yy,
+  const int             x,
+  const int             y,
+  const int             w,
+  const int             h,
+  const int             xin,
+  const int             yin,
+  const int             rowpix,
+  const int             transparent,
+  const uint32_t        fillColor,
+  int&                  pix,
+  uint32_t*&            p,
+  uint32_t*&            data,
+  GifRowType*&          rows,
+  ColorMapObject*&      colorMap)
+{
+  // if we use cachedColor, use it
+  if(cachedColorPtr)
+  {
+    // walk pixels without worring about transparency at all
+    for(yy = 0; yy < h; yy++)
+    {
+      p = data + ((y + yy) * rowpix) + x;
+      for(xx = 0; xx < w; xx++)
+      {
+        pix = rows[yin + yy][xin + xx];
+        *p  = cachedColorPtr[pix];
+        p++;
+      }
+    }
+  }
+  // we don't have cachedColor. use PixelLookup function.
+  else
+  {
+    // walk pixels without worring about transparency at all
+    for(yy = 0; yy < h; yy++)
+    {
+      p = data + ((y + yy) * rowpix) + x;
+      for(xx = 0; xx < w; xx++)
+      {
+        pix = rows[yin + yy][xin + xx];
+        *p  = PixelLookup(colorMap, pix);
+        p++;
+      }
+    }
+  }
+}
+
 /**
  * @brief Decode a gif image into rows then expand to 32bit into the destination
  * data pointer.
  */
-bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin, int transparent, int x, int y, int w, int h, bool fill)
+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)
 {
   int             intoffset[] = {0, 4, 2, 1};
   int             intjump[]   = {8, 8, 4, 2};
@@ -626,6 +872,9 @@ bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin,
   ColorMapObject* colorMap;
   uint32_t*       p;
 
+  // cached color data.
+  const std::uint32_t* cachedColorPtr = nullptr;
+
   // what we need is image size.
   SavedImage* sp;
   sp = &gif->SavedImages[gif->ImageCount - 1];
@@ -633,19 +882,19 @@ bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin,
   gifW = sp->ImageDesc.Width;
   gifH = sp->ImageDesc.Height;
 
-  if((gifW < w) || (gifH < h))
+  if(DALI_UNLIKELY((gifW < w) || (gifH < h)))
   {
     DALI_LOG_ERROR("gifW : %d, w : %d, gifH : %d, h : %d\n", gifW, w, gifH, h);
     DALI_ASSERT_DEBUG(false && "Dimensions are bigger than the Gif image size");
-    goto on_error;
+    PRINT_ERROR_AND_EXIT("GIF Loader: Dimensions are bigger than the Gif image size! gifW : %d, w : %d, gifH : %d, h : %d\n", gifW, w, gifH, h);
   }
 
   // build a blob of memory to have pointers to rows of pixels
   // AND store the decoded gif pixels (1 byte per pixel) as welll
   rows = static_cast<GifRowType*>(malloc((gifH * sizeof(GifRowType)) + (gifW * gifH * sizeof(GifPixelType))));
-  if(!rows)
+  if(DALI_UNLIKELY(!rows))
   {
-    goto on_error;
+    PRINT_ERROR_AND_EXIT("GIF Loader: malloc failed! gifW : %d, w : %d, gifH : %d, h : %d\n", gifW, w, gifH, h);
   }
 
   // fill in the pointers at the start
@@ -661,9 +910,9 @@ bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin,
     {
       for(yy = intoffset[i]; yy < gifH; yy += intjump[i])
       {
-        if(DGifGetLine(gif, rows[yy], gifW) != GIF_OK)
+        if(DALI_UNLIKELY(DGifGetLine(gif, rows[yy], gifW) != GIF_OK))
         {
-          goto on_error;
+          PRINT_ERROR_AND_EXIT("GIF Loader: Decode failed at line %d! gifW : %d, gifH : %d, rows[yy] : %d, i : %d, intoffset[i] : %d, intjump[i] : %d\n", yy, gifW, gifH, rows[yy], i, intoffset[i], intjump[i]);
         }
       }
     }
@@ -673,9 +922,9 @@ bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin,
   {
     for(yy = 0; yy < gifH; yy++)
     {
-      if(DGifGetLine(gif, rows[yy], gifW) != GIF_OK)
+      if(DALI_UNLIKELY(DGifGetLine(gif, rows[yy], gifW) != GIF_OK))
       {
-        goto on_error;
+        PRINT_ERROR_AND_EXIT("GIF Loader: Decode failed at line %d! gifW : %d, gifH : %d, rows[yy] : %d\n", yy, gifW, gifH, rows[yy]);
       }
     }
   }
@@ -684,76 +933,174 @@ bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin,
   if(gif->Image.ColorMap)
   {
     colorMap = gif->Image.ColorMap;
+    // if w * h is big enough, generate local cached color.
+    if(colorMap->ColorCount * LOCAL_CACHED_COLOR_GENERATE_THRESHOLD < w * h)
+    {
+      gifCachedColor.localCachedColor.resize(colorMap->ColorCount);
+      for(i = 0; i < colorMap->ColorCount; ++i)
+      {
+        gifCachedColor.localCachedColor[i] = PixelLookup(colorMap, i);
+      }
+
+      cachedColorPtr = gifCachedColor.localCachedColor.data();
+    }
   }
   else
   {
-    colorMap = gif->SColorMap;
+    colorMap       = gif->SColorMap;
+    cachedColorPtr = gifCachedColor.globalCachedColor.data();
   }
 
+  // HARD-CODING optimize
   // if we need to deal with transparent pixels at all...
   if(transparent >= 0)
   {
-    // if we are told to FILL (overwrite with transparency kept)
-    if(fill)
+    HandleTransparentPixels(fill, cachedColorPtr, xx, yy, x, y, w, h, xin, yin, rowpix, transparent, fillColor, pix, p, data, rows, colorMap);
+  }
+  else
+  {
+    HandleNonTransparentPixels(cachedColorPtr, xx, yy, x, y, w, h, xin, yin, rowpix, transparent, fillColor, pix, p, data, rows, colorMap);
+  }
+  ret = true;
+
+  if(rows)
+  {
+    free(rows);
+  }
+  return ret;
+}
+
+/// Walk through gif records in file to figure out info while reading the header
+void WalkThroughGifRecordsWhileReadingHeader(
+  const GifAccessor&     gifAccessor,
+  GifRecordType&         rec,
+  int&                   imageNumber,
+  FrameInfo*&            frameInfo,
+  bool&                  full,
+  const ImageProperties& prop,
+  GifAnimationData&      animated,
+  int&                   loopCount,
+  bool&                  success)
+{
+  do
+  {
+    if(DGifGetRecordType(gifAccessor.gif, &rec) == GIF_ERROR)
     {
-      for(yy = 0; yy < h; yy++)
+      // if we have a gif that ends part way through a sequence
+      // (or animation) consider it valid and just break - no error
+      if(imageNumber <= 1)
       {
-        p = data + ((y + yy) * rowpix) + x;
-        for(xx = 0; xx < w; xx++)
-        {
-          pix = rows[yin + yy][xin + xx];
-          if(pix != transparent)
-          {
-            *p = PixelLookup(colorMap, pix);
-          }
-          else
-          {
-            *p = 0;
-          }
-          p++;
-        }
+        success = true;
+      }
+      else
+      {
+        success = false;
+        DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
       }
+      break;
     }
-    // paste on top with transparent pixels untouched
-    else
+
+    // get image description section
+    if(rec == IMAGE_DESC_RECORD_TYPE)
     {
-      for(yy = 0; yy < h; yy++)
+      int          img_code;
+      GifByteType* img;
+
+      // get image desc
+      if(DALI_UNLIKELY(DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR))
+      {
+        success = false;
+        DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
+        break;
+      }
+      // skip decoding and just walk image to next
+      if(DALI_UNLIKELY(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR))
+      {
+        success = false;
+        DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
+        break;
+      }
+      // skip till next...
+      while(img)
+      {
+        img = NULL;
+        DGifGetCodeNext(gifAccessor.gif, &img);
+      }
+      // store geometry in the last frame info data
+      if(frameInfo)
+      {
+        StoreFrameInfo(gifAccessor.gif, frameInfo);
+        CheckTransparency(full, frameInfo, prop.w, prop.h);
+      }
+      // or if we dont have a frameInfo entry - create one even for stills
+      else
       {
-        p = data + ((y + yy) * rowpix) + x;
-        for(xx = 0; xx < w; xx++)
+        // allocate and save frame with field data
+        frameInfo = NewFrame(animated, -1, 0, 0, imageNumber + 1);
+        if(DALI_UNLIKELY(!frameInfo))
         {
-          pix = rows[yin + yy][xin + xx];
-          if(pix != transparent)
-          {
-            *p = PixelLookup(colorMap, pix);
-          }
-          p++;
+          success = false;
+          DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
+          break;
         }
+        // store geometry info from gif image
+        StoreFrameInfo(gifAccessor.gif, frameInfo);
+        // check for transparency/alpha
+        CheckTransparency(full, frameInfo, prop.w, prop.h);
       }
+      imageNumber++;
     }
-  }
-  else
-  {
-    // walk pixels without worring about transparency at all
-    for(yy = 0; yy < h; yy++)
+    // we have an extension code block - for animated gifs for sure
+    else if(rec == EXTENSION_RECORD_TYPE)
     {
-      p = data + ((y + yy) * rowpix) + x;
-      for(xx = 0; xx < w; xx++)
+      int          ext_code;
+      GifByteType* ext = NULL;
+
+      // get the first extension entry
+      DGifGetExtension(gifAccessor.gif, &ext_code, &ext);
+      while(ext)
       {
-        pix = rows[yin + yy][xin + xx];
-        *p  = PixelLookup(colorMap, pix);
-        p++;
+        // graphic control extension - for animated gif data
+        // and transparent index + flag
+        if(ext_code == 0xf9)
+        {
+          // create frame and store it in image
+          int transparencyIndex = (ext[1] & 1) ? ext[4] : -1;
+          int disposeMode       = (ext[1] >> 2) & 0x7;
+          int delay             = (int(ext[3]) << 8) | int(ext[2]);
+          frameInfo             = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
+          if(DALI_UNLIKELY(!frameInfo))
+          {
+            success = false;
+            DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
+            break;
+          }
+        }
+        // netscape extension indicating loop count.
+        else if(ext_code == 0xff) /* application extension */
+        {
+          if(!strncmp(reinterpret_cast<char*>(&ext[1]), "NETSCAPE2.0", 11) ||
+             !strncmp(reinterpret_cast<char*>(&ext[1]), "ANIMEXTS1.0", 11))
+          {
+            ext = NULL;
+            DGifGetExtensionNext(gifAccessor.gif, &ext);
+            if(ext[1] == 0x01)
+            {
+              loopCount = (int(ext[3]) << 8) | int(ext[2]);
+              if(loopCount > 0)
+              {
+                loopCount++;
+              }
+            }
+          }
+        }
+
+        // and continue onto the next extension entry
+        ext = NULL;
+        DGifGetExtensionNext(gifAccessor.gif, &ext);
       }
     }
-  }
-  ret = true;
-
-on_error:
-  if(rows)
-  {
-    free(rows);
-  }
-  return ret;
+  } while(rec != TERMINATE_RECORD_TYPE && success);
 }
 
 /**
@@ -761,16 +1108,15 @@ on_error:
  *
  * @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
  * @param[out] prop A ImageProperties structure for storing information about GIF data.
- * @param[out] error Error code
  * @return The true or false whether reading was successful or not.
  */
 bool ReadHeader(LoaderInfo&      loaderInfo,
-                ImageProperties& prop, //output struct
-                int*             error)
+                ImageProperties& prop)
 {
-  GifAnimationData&     animated = loaderInfo.animated;
-  LoaderInfo::FileData& fileData = loaderInfo.fileData;
-  bool                  success  = false;
+  GifAnimationData&     animated    = loaderInfo.animated;
+  GifCachedColorData&   cachedColor = loaderInfo.cachedColor;
+  LoaderInfo::FileData& fileData    = loaderInfo.fileData;
+  bool                  success     = false;
   LoaderInfo::FileInfo  fileInfo;
   GifRecordType         rec;
 
@@ -782,7 +1128,7 @@ bool ReadHeader(LoaderInfo&      loaderInfo,
   bool       full        = true;
 
   success = fileData.LoadFile();
-  if(!success || !fileData.globalMap)
+  if(DALI_UNLIKELY(!success || !fileData.globalMap))
   {
     success = false;
     DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
@@ -801,7 +1147,7 @@ bool ReadHeader(LoaderInfo&      loaderInfo,
       prop.h = gifAccessor.gif->SHeight;
 
       // if size is invalid - abort here
-      if((prop.w < 1) || (prop.h < 1) || (prop.w > IMG_MAX_SIZE) || (prop.h > IMG_MAX_SIZE) || IMG_TOO_BIG(prop.w, prop.h))
+      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)))
       {
         if(IMG_TOO_BIG(prop.w, prop.h))
         {
@@ -816,153 +1162,266 @@ bool ReadHeader(LoaderInfo&      loaderInfo,
       }
       else
       {
-        // walk through gif records in file to figure out info
         success = true;
-        do
+        WalkThroughGifRecordsWhileReadingHeader(gifAccessor, rec, imageNumber, frameInfo, full, prop, animated, loopCount, success);
+
+        if(success)
         {
-          if(DGifGetRecordType(gifAccessor.gif, &rec) == GIF_ERROR)
+          // if the gif main says we have more than one image or our image counting
+          // says so, then this image is animated - indicate this
+          if((gifAccessor.gif->ImageCount > 1) || (imageNumber > 1))
           {
-            // if we have a gif that ends part way through a sequence
-            // (or animation) consider it valid and just break - no error
-            if(imageNumber <= 1)
-            {
-              success = true;
-            }
-            else
-            {
-              success = false;
-              DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
-            }
-            break;
+            animated.animated  = 1;
+            animated.loopCount = loopCount;
           }
+          animated.frameCount = std::min(gifAccessor.gif->ImageCount, imageNumber);
 
-          // get image description section
-          if(rec == IMAGE_DESC_RECORD_TYPE)
+          if(!full)
           {
-            int          img_code;
-            GifByteType* img;
+            prop.alpha = 1;
+          }
 
-            // get image desc
-            if(DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR)
-            {
-              success = false;
-              DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
-              break;
-            }
-            // skip decoding and just walk image to next
-            if(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR)
-            {
-              success = false;
-              DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
-              break;
-            }
-            // skip till next...
-            while(img)
-            {
-              img = NULL;
-              DGifGetCodeNext(gifAccessor.gif, &img);
-            }
-            // store geometry in the last frame info data
-            if(frameInfo)
-            {
-              StoreFrameInfo(gifAccessor.gif, frameInfo);
-              CheckTransparency(full, frameInfo, prop.w, prop.h);
-            }
-            // or if we dont have a frameInfo entry - create one even for stills
-            else
+          animated.currentFrame = 1;
+
+          // cache global color map
+          ColorMapObject* colorMap = gifAccessor.gif->SColorMap;
+          if(colorMap)
+          {
+            cachedColor.globalCachedColor.resize(colorMap->ColorCount);
+            for(int i = 0; i < colorMap->ColorCount; ++i)
             {
-              // allocate and save frame with field data
-              frameInfo = NewFrame(animated, -1, 0, 0, imageNumber + 1);
-              if(!frameInfo)
-              {
-                success = false;
-                DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
-                break;
-              }
-              // store geometry info from gif image
-              StoreFrameInfo(gifAccessor.gif, frameInfo);
-              // check for transparency/alpha
-              CheckTransparency(full, frameInfo, prop.w, prop.h);
+              cachedColor.globalCachedColor[i] = PixelLookup(colorMap, i);
             }
-            imageNumber++;
           }
-          // we have an extension code block - for animated gifs for sure
-          else if(rec == EXTENSION_RECORD_TYPE)
+        }
+      }
+    }
+    else
+    {
+      success = false;
+      DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
+    }
+  }
+  return success;
+}
+
+/// Walk through gif records in file to figure out info
+bool WalkThroughGifRecords(
+  GifRecordType&         rec,
+  LoaderInfo&            loaderInfo,
+  GifAnimationData&      animated,
+  int&                   imageNumber,
+  const int&             index,
+  FrameInfo*&            frameInfo,
+  const ImageProperties& prop,
+  ImageFrame*&           lastPreservedFrame,
+  unsigned char*&        pixels)
+{
+  do
+  {
+    if(DALI_UNLIKELY(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR))
+    {
+      DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
+      return false;
+    }
+
+    if(rec == EXTENSION_RECORD_TYPE)
+    {
+      int          ext_code;
+      GifByteType* ext = NULL;
+      DGifGetExtension(loaderInfo.gifAccessor->gif, &ext_code, &ext);
+
+      while(ext)
+      {
+        ext = NULL;
+        DGifGetExtensionNext(loaderInfo.gifAccessor->gif, &ext);
+      }
+    }
+    // get image description section
+    else if(rec == IMAGE_DESC_RECORD_TYPE)
+    {
+      int          xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
+      int          img_code;
+      GifByteType* img;
+      ImageFrame*  previousFrame = NULL;
+      ImageFrame*  thisFrame     = NULL;
+
+      // get image desc
+      if(DALI_UNLIKELY(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR))
+      {
+        DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
+        return false;
+      }
+
+      // get the previous frame entry AND the current one to fill in
+      previousFrame = FindFrame(animated, imageNumber - 1);
+      thisFrame     = FindFrame(animated, imageNumber);
+
+      // if we have a frame AND we're animated AND we have no data...
+      if((thisFrame) && (!thisFrame->data) && (animated.animated))
+      {
+        bool first = false;
+
+        // allocate it
+        thisFrame->data = new uint32_t[prop.w * prop.h];
+
+        if(DALI_UNLIKELY(!thisFrame->data))
+        {
+          DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
+          return false;
+        }
+
+        // Lazy fill background color feature.
+        // DecodeImage function draw range is EQUAL with previous FillImage range,
+        // We don't need to fill background that time.
+        // It will try to reduce the number of FillImage API call
+        // Note : We might check overlapping. But that operation looks expensive
+        // So, just optimize only if EQUAL case.
+        bool     updateBackgroundColorLazy = false;
+        uint32_t backgroundColor           = 0u;
+        int      prevX                     = 0;
+        int      prevY                     = 0;
+        int      prevW                     = 0;
+        int      prevH                     = 0;
+
+        // if we have no prior frame OR prior frame data... empty
+        if((!previousFrame) || (!previousFrame->data))
+        {
+          first                     = true;
+          frameInfo                 = &(thisFrame->info);
+          updateBackgroundColorLazy = true;
+          backgroundColor           = 0u;
+          prevX                     = 0;
+          prevY                     = 0;
+          prevW                     = prop.w;
+          prevH                     = prop.h;
+        }
+        // we have a prior frame to copy data from...
+        else
+        {
+          frameInfo = &(previousFrame->info);
+
+          // fix coords of sub image in case it goes out...
+          ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
+
+          // if dispose mode is not restore - then copy pre frame
+          if(frameInfo->dispose != DISPOSE_PREVIOUS)
           {
-            int          ext_code;
-            GifByteType* ext = NULL;
+            memcpy(thisFrame->data, previousFrame->data, prop.w * prop.h * sizeof(uint32_t));
+          }
 
-            // get the first extension entry
-            DGifGetExtension(gifAccessor.gif, &ext_code, &ext);
-            while(ext)
+          // if dispose mode is "background" then fill with bg
+          if(frameInfo->dispose == DISPOSE_BACKGROUND)
+          {
+            updateBackgroundColorLazy = true;
+            backgroundColor           = GetBackgroundColor(loaderInfo.gifAccessor->gif, frameInfo);
+            prevX                     = x;
+            prevY                     = y;
+            prevW                     = w;
+            prevH                     = h;
+          }
+          else if(frameInfo->dispose == DISPOSE_PREVIOUS) // GIF_DISPOSE_RESTORE
+          {
+            int prevIndex = 2;
+            do
             {
-              // graphic control extension - for animated gif data
-              // and transparent index + flag
-              if(ext_code == 0xf9)
+              // Find last preserved frame.
+              lastPreservedFrame = FindFrame(animated, imageNumber - prevIndex);
+              if(DALI_UNLIKELY(!lastPreservedFrame))
               {
-                // create frame and store it in image
-                int transparencyIndex = (ext[1] & 1) ? ext[4] : -1;
-                int disposeMode       = (ext[1] >> 2) & 0x7;
-                int delay             = (int(ext[3]) << 8) | int(ext[2]);
-                frameInfo             = NewFrame(animated, transparencyIndex, disposeMode, delay, imageNumber + 1);
-                if(!frameInfo)
-                {
-                  success = false;
-                  DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
-                  break;
-                }
-              }
-              // netscape extension indicating loop count.
-              else if(ext_code == 0xff) /* application extension */
-              {
-                if(!strncmp(reinterpret_cast<char*>(&ext[1]), "NETSCAPE2.0", 11) ||
-                   !strncmp(reinterpret_cast<char*>(&ext[1]), "ANIMEXTS1.0", 11))
-                {
-                  ext = NULL;
-                  DGifGetExtensionNext(gifAccessor.gif, &ext);
-                  if(ext[1] == 0x01)
-                  {
-                    loopCount = (int(ext[3]) << 8) | int(ext[2]);
-                    if(loopCount > 0)
-                    {
-                      loopCount++;
-                    }
-                  }
-                }
+                DALI_LOG_ERROR("LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND");
+                return false;
               }
+              prevIndex++;
+            } while(lastPreservedFrame && lastPreservedFrame->info.dispose == DISPOSE_PREVIOUS);
 
-              // and continue onto the next extension entry
-              ext = NULL;
-              DGifGetExtensionNext(gifAccessor.gif, &ext);
+            if(lastPreservedFrame)
+            {
+              memcpy(thisFrame->data, lastPreservedFrame->data, prop.w * prop.h * sizeof(uint32_t));
             }
           }
-        } while(rec != TERMINATE_RECORD_TYPE && success);
+        }
+        // now draw this frame on top
+        frameInfo = &(thisFrame->info);
+        ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
 
-        if(success)
+        if(updateBackgroundColorLazy)
         {
-          // if the gif main says we have more than one image or our image counting
-          // says so, then this image is animated - indicate this
-          if((gifAccessor.gif->ImageCount > 1) || (imageNumber > 1))
+          // If this frame's x,y,w,h is not equal with previous x,y,w,h, FillImage. else, don't fill
+          if(prevX != x || prevY != y || prevW != w || prevH != h)
           {
-            animated.animated  = 1;
-            animated.loopCount = loopCount;
+            FillImage(thisFrame->data, prop.w, backgroundColor, prevX, prevY, prevW, prevH);
+            // Don't send background color information to DecodeImage function.
+            updateBackgroundColorLazy = false;
           }
-          animated.frameCount = std::min(gifAccessor.gif->ImageCount, imageNumber);
+        }
+        if(DALI_UNLIKELY(!DecodeImage(loaderInfo.gifAccessor->gif, loaderInfo.cachedColor, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first || updateBackgroundColorLazy, backgroundColor)))
+        {
+          DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
+          return false;
+        }
 
-          if(!full)
+        // mark as loaded and done
+        thisFrame->loaded = true;
+
+        FlushFrames(animated, prop.w, prop.h, thisFrame, previousFrame, lastPreservedFrame);
+      }
+      // if we have a frame BUT the image is not animated. different
+      // path
+      else if((thisFrame) && (!thisFrame->data) && (!animated.animated))
+      {
+        // if we don't have the data decoded yet - decode it
+        if((!thisFrame->loaded) || (!thisFrame->data))
+        {
+          // use frame info but we WONT allocate frame pixels
+          frameInfo = &(thisFrame->info);
+          ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
+
+          // clear out all pixels only if x,y,w,h is not whole image.
+          if(x != 0 || y != 0 || w != static_cast<int>(prop.w) || h != static_cast<int>(prop.h))
           {
-            prop.alpha = 1;
+            const std::uint32_t backgroundColor = GetBackgroundColor(loaderInfo.gifAccessor->gif, frameInfo);
+            FillImage(reinterpret_cast<uint32_t*>(pixels), prop.w, backgroundColor, 0, 0, prop.w, prop.h);
           }
 
-          animated.currentFrame = 1;
+          // and decode the gif with overwriting
+          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)))
+          {
+            DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
+            return false;
+          }
 
-          // no errors in header scan etc. so set err and return value
-          *error = 0;
+          // mark as loaded and done
+          thisFrame->loaded = true;
         }
+        // flush mem we don't need (at expense of decode cpu)
+      }
+      else
+      {
+        // skip decoding and just walk image to next
+        if(DALI_UNLIKELY(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR))
+        {
+          DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
+          return false;
+        }
+
+        while(img)
+        {
+          img = NULL;
+          DGifGetCodeNext(loaderInfo.gifAccessor->gif, &img);
+        }
+      }
+
+      imageNumber++;
+      // if we found the image we wanted - get out of here
+      if(imageNumber > index)
+      {
+        break;
       }
     }
-  }
-  return success;
+  } while(rec != TERMINATE_RECORD_TYPE);
+
+  return true;
 }
 
 /**
@@ -990,7 +1449,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
   index = animated.currentFrame;
 
   // if index is invalid for animated image - error out
-  if((animated.animated) && ((index <= 0) || (index > animated.frameCount)))
+  if(DALI_UNLIKELY((animated.animated) && ((index <= 0) || (index > animated.frameCount))))
   {
     DALI_LOG_ERROR("LOAD_ERROR_GENERIC");
     return false;
@@ -998,7 +1457,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
 
   // find the given frame index
   frame = FindFrame(animated, index);
-  if(!frame)
+  if(DALI_UNLIKELY(!frame))
   {
     DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
     return false;
@@ -1023,13 +1482,13 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
       loaderInfo.fileInfo.map      = fileData.globalMap;
       loaderInfo.fileInfo.length   = fileData.length;
       loaderInfo.fileInfo.position = 0;
-      if(!loaderInfo.fileInfo.map)
+      if(DALI_UNLIKELY(!loaderInfo.fileInfo.map))
       {
         DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
         return false;
       }
       std::unique_ptr<GifAccessor> gifAccessor = std::make_unique<GifAccessor>(loaderInfo.fileInfo);
-      if(!gifAccessor->gif)
+      if(DALI_UNLIKELY(!gifAccessor->gif))
       {
         DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
         return false;
@@ -1041,172 +1500,10 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
     // our current position is the previous frame we decoded from the file
     imageNumber = loaderInfo.imageNumber;
 
-    // walk through gif records in file to figure out info
-    do
+    if(!WalkThroughGifRecords(rec, loaderInfo, animated, imageNumber, index, frameInfo, prop, lastPreservedFrame, pixels))
     {
-      if(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR)
-      {
-        DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
-        return false;
-      }
-
-      if(rec == EXTENSION_RECORD_TYPE)
-      {
-        int          ext_code;
-        GifByteType* ext = NULL;
-        DGifGetExtension(loaderInfo.gifAccessor->gif, &ext_code, &ext);
-
-        while(ext)
-        {
-          ext = NULL;
-          DGifGetExtensionNext(loaderInfo.gifAccessor->gif, &ext);
-        }
-      }
-      // get image description section
-      else if(rec == IMAGE_DESC_RECORD_TYPE)
-      {
-        int          xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
-        int          img_code;
-        GifByteType* img;
-        ImageFrame*  previousFrame = NULL;
-        ImageFrame*  thisFrame     = NULL;
-
-        // get image desc
-        if(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR)
-        {
-          DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
-          return false;
-        }
-
-        // get the previous frame entry AND the current one to fill in
-        previousFrame = FindFrame(animated, imageNumber - 1);
-        thisFrame     = FindFrame(animated, imageNumber);
-
-        // if we have a frame AND we're animated AND we have no data...
-        if((thisFrame) && (!thisFrame->data) && (animated.animated))
-        {
-          bool first = false;
-
-          // allocate it
-          thisFrame->data = new uint32_t[prop.w * prop.h];
-
-          if(!thisFrame->data)
-          {
-            DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
-            return false;
-          }
-
-          // if we have no prior frame OR prior frame data... empty
-          if((!previousFrame) || (!previousFrame->data))
-          {
-            first     = true;
-            frameInfo = &(thisFrame->info);
-            memset(thisFrame->data, 0, prop.w * prop.h * sizeof(uint32_t));
-          }
-          // we have a prior frame to copy data from...
-          else
-          {
-            frameInfo = &(previousFrame->info);
-
-            // fix coords of sub image in case it goes out...
-            ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
-
-            // if dispose mode is not restore - then copy pre frame
-            if(frameInfo->dispose != DISPOSE_PREVIOUS)
-            {
-              memcpy(thisFrame->data, previousFrame->data, prop.w * prop.h * sizeof(uint32_t));
-            }
-
-            // if dispose mode is "background" then fill with bg
-            if(frameInfo->dispose == DISPOSE_BACKGROUND)
-            {
-              FillFrame(thisFrame->data, prop.w, loaderInfo.gifAccessor->gif, frameInfo, x, y, w, h);
-            }
-            else if(frameInfo->dispose == DISPOSE_PREVIOUS) // GIF_DISPOSE_RESTORE
-            {
-              int prevIndex = 2;
-              do
-              {
-                // Find last preserved frame.
-                lastPreservedFrame = FindFrame(animated, imageNumber - prevIndex);
-                if(!lastPreservedFrame)
-                {
-                  DALI_LOG_ERROR("LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND");
-                  return false;
-                }
-                prevIndex++;
-              } while(lastPreservedFrame && lastPreservedFrame->info.dispose == DISPOSE_PREVIOUS);
-
-              if(lastPreservedFrame)
-              {
-                memcpy(thisFrame->data, lastPreservedFrame->data, prop.w * prop.h * sizeof(uint32_t));
-              }
-            }
-          }
-          // now draw this frame on top
-          frameInfo = &(thisFrame->info);
-          ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
-          if(!DecodeImage(loaderInfo.gifAccessor->gif, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first))
-          {
-            DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
-            return false;
-          }
-
-          // mark as loaded and done
-          thisFrame->loaded = true;
-
-          FlushFrames(animated, prop.w, prop.h, thisFrame, previousFrame, lastPreservedFrame);
-        }
-        // if we have a frame BUT the image is not animated. different
-        // path
-        else if((thisFrame) && (!thisFrame->data) && (!animated.animated))
-        {
-          // if we don't have the data decoded yet - decode it
-          if((!thisFrame->loaded) || (!thisFrame->data))
-          {
-            // use frame info but we WONT allocate frame pixels
-            frameInfo = &(thisFrame->info);
-            ClipCoordinates(prop.w, prop.h, &xin, &yin, frameInfo->x, frameInfo->y, frameInfo->w, frameInfo->h, &x, &y, &w, &h);
-
-            // clear out all pixels
-            FillFrame(reinterpret_cast<uint32_t*>(pixels), prop.w, loaderInfo.gifAccessor->gif, frameInfo, 0, 0, prop.w, prop.h);
-
-            // and decode the gif with overwriting
-            if(!DecodeImage(loaderInfo.gifAccessor->gif, reinterpret_cast<uint32_t*>(pixels), prop.w, xin, yin, frameInfo->transparent, x, y, w, h, true))
-            {
-              DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
-              return false;
-            }
-
-            // mark as loaded and done
-            thisFrame->loaded = true;
-          }
-          // flush mem we don't need (at expense of decode cpu)
-        }
-        else
-        {
-          // skip decoding and just walk image to next
-          if(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR)
-          {
-            DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
-            return false;
-          }
-
-          while(img)
-          {
-            img = NULL;
-            DGifGetCodeNext(loaderInfo.gifAccessor->gif, &img);
-          }
-        }
-
-        imageNumber++;
-        // if we found the image we wanted - get out of here
-        if(imageNumber > index)
-        {
-          break;
-        }
-      }
-    } while(rec != TERMINATE_RECORD_TYPE);
+      return false;
+    }
 
     // if we are at the end of the animation or not animated, close file
     loaderInfo.imageNumber = imageNumber;
@@ -1238,14 +1535,29 @@ struct GifLoading::Impl
 public:
   Impl(const std::string& url, bool isLocalResource)
   : mUrl(url),
-    mLoadSucceeded(true)
+    mLoadSucceeded(false),
+    mMutex()
   {
-    loaderInfo.gifAccessor = nullptr;
-    int error;
+    loaderInfo.gifAccessor              = nullptr;
     loaderInfo.fileData.fileName        = mUrl.c_str();
     loaderInfo.fileData.isLocalResource = isLocalResource;
+  }
 
-    mLoadSucceeded = ReadHeader(loaderInfo, imageProperties, &error);
+  bool LoadGifInformation()
+  {
+    // Block to do not load this file again.
+    Mutex::ScopedLock lock(mMutex);
+    if(DALI_LIKELY(mLoadSucceeded))
+    {
+      return mLoadSucceeded;
+    }
+
+    mLoadSucceeded = ReadHeader(loaderInfo, imageProperties);
+    if(!mLoadSucceeded)
+    {
+      DALI_LOG_ERROR("ReadHeader is failed [%s]\n", mUrl.c_str());
+    }
+    return mLoadSucceeded;
   }
 
   // Moveable but not copyable
@@ -1258,6 +1570,7 @@ public:
   LoaderInfo      loaderInfo;
   ImageProperties imageProperties;
   bool            mLoadSucceeded;
+  Mutex           mMutex;
 };
 
 AnimatedImageLoadingPtr GifLoading::New(const std::string& url, bool isLocalResource)
@@ -1275,49 +1588,20 @@ GifLoading::~GifLoading()
   delete mImpl;
 }
 
-bool GifLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
-{
-  int  error;
-  bool ret = false;
-  if(!mImpl->mLoadSucceeded)
-  {
-    return false;
-  }
-
-  const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof(uint32_t);
-
-  DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count);
-
-  for(int i = 0; i < count; ++i)
-  {
-    auto pixelBuffer = new unsigned char[bufferSize];
-
-    mImpl->loaderInfo.animated.currentFrame = 1 + ((frameStartIndex + i) % mImpl->loaderInfo.animated.frameCount);
-
-    if(ReadNextFrame(mImpl->loaderInfo, mImpl->imageProperties, pixelBuffer, &error))
-    {
-      if(pixelBuffer)
-      {
-        pixelData.push_back(Dali::PixelData::New(pixelBuffer, bufferSize, mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY));
-        ret = true;
-      }
-    }
-  }
-
-  return ret;
-}
-
-Dali::Devel::PixelBuffer GifLoading::LoadFrame(uint32_t frameIndex)
+Dali::Devel::PixelBuffer GifLoading::LoadFrame(uint32_t frameIndex, ImageDimensions desiredSize)
 {
   int                      error;
   Dali::Devel::PixelBuffer pixelBuffer;
-  if(!mImpl->mLoadSucceeded)
+
+  DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
+
+  // If Gif file is still not loaded, Load the information.
+  if(DALI_UNLIKELY(!mImpl->LoadGifInformation()))
   {
     return pixelBuffer;
   }
 
-  DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
-
+  Mutex::ScopedLock lock(mImpl->mMutex);
   pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888);
 
   mImpl->loaderInfo.animated.currentFrame = 1 + (frameIndex % mImpl->loaderInfo.animated.frameCount);
@@ -1332,17 +1616,35 @@ Dali::Devel::PixelBuffer GifLoading::LoadFrame(uint32_t frameIndex)
 
 ImageDimensions GifLoading::GetImageSize() const
 {
+  if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+  {
+    mImpl->LoadGifInformation();
+  }
   return ImageDimensions(mImpl->imageProperties.w, mImpl->imageProperties.h);
 }
 
 uint32_t GifLoading::GetImageCount() const
 {
+  if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+  {
+    mImpl->LoadGifInformation();
+  }
   return mImpl->loaderInfo.animated.frameCount;
 }
 
 uint32_t GifLoading::GetFrameInterval(uint32_t frameIndex) const
 {
-  return mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
+  if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+  {
+    mImpl->LoadGifInformation();
+  }
+
+  uint32_t interval = 0u;
+  if(DALI_LIKELY(mImpl->mLoadSucceeded))
+  {
+    interval = mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
+  }
+  return interval;
 }
 
 std::string GifLoading::GetUrl() const