Minor optimization on image loading
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / gif-loading.cpp
index 6b0d709..a3f34f0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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>
@@ -60,6 +61,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 = 16; ///< 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 +133,16 @@ struct GifAnimationData
   bool                    animated;
 };
 
+struct GifCachedColorData
+{
+  GifCachedColorData() = default;
+
+  // precalculated colormap table
+  std::vector<std::uint32_t> globalCachedColor{};
+  std::vector<std::uint32_t> localCachedColor{};
+  ColorMapObject*            localCachedColorMap{nullptr}; // Weak-pointer of ColorMapObject. should be nullptr if image changed
+};
+
 // Forward declaration
 struct GifAccessor;
 
@@ -186,6 +199,7 @@ struct LoaderInfo
 
   FileData                     fileData;
   GifAnimationData             animated;
+  GifCachedColorData           cachedColor;
   std::unique_ptr<GifAccessor> gifAccessor{nullptr};
   int                          imageNumber{0};
   FileInfo                     fileInfo;
@@ -246,7 +260,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,23 +294,23 @@ 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);
@@ -316,17 +330,17 @@ 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);
@@ -354,12 +368,12 @@ bool LoaderInfo::FileData::LoadRemoteFile()
  * @param[in] index Frame index to be searched in GIF
  * @return A pointer to the ImageFrame.
  */
-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);
 }
@@ -387,25 +401,47 @@ 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++)
+  // Boost time if stride == width and x == 0. We can assume that all pointer is continuous.
+  if(x == 0 && stride == width)
   {
-    pixelPosition = data + ((y + yAxis) * row) + x;
-    for(xAxis = 0; xAxis < width; xAxis++)
+    pixelPosition = data + (y * stride);
+    // Clear as white or transparent
+    // Special case. we can use memset.
+    if(val == 0x00 || val == 0xffffffffu)
+    {
+      const std::int8_t setupVal = val & 0xff;
+      memset(pixelPosition, setupVal, width * height * sizeof(std::uint32_t));
+    }
+    else
     {
-      *pixelPosition = val;
-      pixelPosition++;
+      for(int byteCount = 0; byteCount < width * height; ++byteCount)
+      {
+        *pixelPosition = val;
+        ++pixelPosition;
+      }
+    }
+  }
+  else
+  {
+    for(int yAxis = 0; yAxis < height; ++yAxis)
+    {
+      pixelPosition = data + ((y + yAxis) * stride) + x;
+      for(int xAxis = 0; xAxis < width; ++xAxis)
+      {
+        *pixelPosition = val;
+        ++pixelPosition;
+      }
     }
   }
 }
@@ -414,7 +450,7 @@ void FillImage(uint32_t* data, int row, uint32_t val, int x, int y, int width, i
  * @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] stride A int containing the number of stride 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
@@ -422,7 +458,7 @@ void FillImage(uint32_t* data, int row, uint32_t val, int x, int y, int width, i
  * @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)
+void FillFrame(uint32_t* data, int stride, GifFileType* gif, FrameInfo* frameInfo, int x, int y, int w, int h)
 {
   // solid color fill for pre frame region
   if(frameInfo->transparent < 0)
@@ -441,12 +477,12 @@ void FillFrame(uint32_t* data, int row, GifFileType* gif, FrameInfo* frameInfo,
     }
     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);
+    FillImage(data, stride, 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);
+    FillImage(data, stride, 0, x, y, w, h);
   }
 }
 
@@ -616,7 +652,7 @@ FrameInfo* NewFrame(GifAnimationData& animated, int transparent, int dispose, in
  * @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)
 {
   int             intoffset[] = {0, 4, 2, 1};
   int             intjump[]   = {8, 8, 4, 2};
@@ -626,6 +662,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,7 +672,7 @@ 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");
@@ -643,7 +682,7 @@ bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin,
   // 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;
   }
@@ -661,7 +700,7 @@ 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;
         }
@@ -673,7 +712,7 @@ 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;
       }
@@ -684,65 +723,149 @@ bool DecodeImage(GifFileType* gif, uint32_t* data, int rowpix, int xin, int yin,
   if(gif->Image.ColorMap)
   {
     colorMap = gif->Image.ColorMap;
+    // use local cached color map without re-calculate cache.
+    if(gifCachedColor.localCachedColorMap == colorMap)
+    {
+      cachedColorPtr = gifCachedColor.localCachedColor.data();
+    }
+    // else if w * h is big enough, generate local cached color.
+    else 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);
+      }
+      gifCachedColor.localCachedColorMap = colorMap;
+
+      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)
     {
-      for(yy = 0; yy < h; yy++)
+      // if we use cachedColor, use it
+      if(cachedColorPtr)
       {
-        p = data + ((y + yy) * rowpix) + x;
-        for(xx = 0; xx < w; xx++)
+        for(yy = 0; yy < h; yy++)
         {
-          pix = rows[yin + yy][xin + xx];
-          if(pix != transparent)
+          p = data + ((y + yy) * rowpix) + x;
+          for(xx = 0; xx < w; xx++)
           {
-            *p = PixelLookup(colorMap, pix);
+            pix = rows[yin + yy][xin + xx];
+            if(pix != transparent)
+            {
+              *p = cachedColorPtr[pix];
+            }
+            else
+            {
+              *p = 0;
+            }
+            p++;
           }
-          else
+        }
+      }
+      // 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++)
           {
-            *p = 0;
+            pix = rows[yin + yy][xin + xx];
+            if(pix != transparent)
+            {
+              *p = PixelLookup(colorMap, pix);
+            }
+            else
+            {
+              *p = 0;
+            }
+            p++;
           }
-          p++;
         }
       }
     }
     // paste on top with transparent pixels untouched
     else
     {
-      for(yy = 0; yy < h; yy++)
+      // if we use cachedColor, use it
+      if(cachedColorPtr)
       {
-        p = data + ((y + yy) * rowpix) + x;
-        for(xx = 0; xx < w; xx++)
+        for(yy = 0; yy < h; yy++)
         {
-          pix = rows[yin + yy][xin + xx];
-          if(pix != transparent)
+          p = data + ((y + yy) * rowpix) + x;
+          for(xx = 0; xx < w; xx++)
           {
-            *p = PixelLookup(colorMap, pix);
+            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++;
           }
-          p++;
         }
       }
     }
   }
   else
   {
-    // walk pixels without worring about transparency at all
-    for(yy = 0; yy < h; yy++)
+    // if we use cachedColor, use it
+    if(cachedColorPtr)
     {
-      p = data + ((y + yy) * rowpix) + x;
-      for(xx = 0; xx < w; xx++)
+      // walk pixels without worring about transparency at all
+      for(yy = 0; yy < h; yy++)
       {
-        pix = rows[yin + yy][xin + xx];
-        *p  = PixelLookup(colorMap, pix);
-        p++;
+        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++;
+        }
       }
     }
   }
@@ -768,9 +891,10 @@ bool ReadHeader(LoaderInfo&      loaderInfo,
                 ImageProperties& prop, //output struct
                 int*             error)
 {
-  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 +906,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 +925,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))
         {
@@ -843,14 +967,14 @@ bool ReadHeader(LoaderInfo&      loaderInfo,
             GifByteType* img;
 
             // get image desc
-            if(DGifGetImageDesc(gifAccessor.gif) == GIF_ERROR)
+            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(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR)
+            if(DALI_UNLIKELY(DGifGetCode(gifAccessor.gif, &img_code, &img) == GIF_ERROR))
             {
               success = false;
               DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
@@ -873,7 +997,7 @@ bool ReadHeader(LoaderInfo&      loaderInfo,
             {
               // allocate and save frame with field data
               frameInfo = NewFrame(animated, -1, 0, 0, imageNumber + 1);
-              if(!frameInfo)
+              if(DALI_UNLIKELY(!frameInfo))
               {
                 success = false;
                 DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
@@ -905,7 +1029,7 @@ bool ReadHeader(LoaderInfo&      loaderInfo,
                 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)
+                if(DALI_UNLIKELY(!frameInfo))
                 {
                   success = false;
                   DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
@@ -956,6 +1080,18 @@ bool ReadHeader(LoaderInfo&      loaderInfo,
 
           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)
+            {
+              cachedColor.globalCachedColor[i] = PixelLookup(colorMap, i);
+            }
+          }
+          cachedColor.localCachedColorMap = nullptr;
+
           // no errors in header scan etc. so set err and return value
           *error = 0;
         }
@@ -990,7 +1126,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 +1134,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 +1159,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;
@@ -1044,7 +1180,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
     // walk through gif records in file to figure out info
     do
     {
-      if(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR)
+      if(DALI_UNLIKELY(DGifGetRecordType(loaderInfo.gifAccessor->gif, &rec) == GIF_ERROR))
       {
         DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
         return false;
@@ -1072,7 +1208,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
         ImageFrame*  thisFrame     = NULL;
 
         // get image desc
-        if(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR)
+        if(DALI_UNLIKELY(DGifGetImageDesc(loaderInfo.gifAccessor->gif) == GIF_ERROR))
         {
           DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
           return false;
@@ -1090,7 +1226,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
           // allocate it
           thisFrame->data = new uint32_t[prop.w * prop.h];
 
-          if(!thisFrame->data)
+          if(DALI_UNLIKELY(!thisFrame->data))
           {
             DALI_LOG_ERROR("LOAD_ERROR_RESOURCE_ALLOCATION_FAILED");
             return false;
@@ -1129,7 +1265,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
               {
                 // Find last preserved frame.
                 lastPreservedFrame = FindFrame(animated, imageNumber - prevIndex);
-                if(!lastPreservedFrame)
+                if(DALI_UNLIKELY(!lastPreservedFrame))
                 {
                   DALI_LOG_ERROR("LOAD_ERROR_LAST_PRESERVED_FRAME_NOT_FOUND");
                   return false;
@@ -1146,7 +1282,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
           // 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))
+          if(DALI_UNLIKELY(!DecodeImage(loaderInfo.gifAccessor->gif, loaderInfo.cachedColor, thisFrame->data, prop.w, xin, yin, frameInfo->transparent, x, y, w, h, first)))
           {
             DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
             return false;
@@ -1172,7 +1308,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
             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))
+            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)))
             {
               DALI_LOG_ERROR("LOAD_ERROR_CORRUPT_FILE\n");
               return false;
@@ -1186,7 +1322,7 @@ bool ReadNextFrame(LoaderInfo& loaderInfo, ImageProperties& prop, //  use for w
         else
         {
           // skip decoding and just walk image to next
-          if(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR)
+          if(DALI_UNLIKELY(DGifGetCode(loaderInfo.gifAccessor->gif, &img_code, &img) == GIF_ERROR))
           {
             DALI_LOG_ERROR("LOAD_ERROR_UNKNOWN_FORMAT\n");
             return false;
@@ -1238,7 +1374,8 @@ struct GifLoading::Impl
 public:
   Impl(const std::string& url, bool isLocalResource)
   : mUrl(url),
-    mLoadSucceeded(true)
+    mLoadSucceeded(true),
+    mMutex()
   {
     loaderInfo.gifAccessor = nullptr;
     int error;
@@ -1258,6 +1395,7 @@ public:
   LoaderInfo      loaderInfo;
   ImageProperties imageProperties;
   bool            mLoadSucceeded;
+  Mutex           mMutex;
 };
 
 AnimatedImageLoadingPtr GifLoading::New(const std::string& url, bool isLocalResource)
@@ -1279,7 +1417,9 @@ bool GifLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vecto
 {
   int  error;
   bool ret = false;
-  if(!mImpl->mLoadSucceeded)
+
+  Mutex::ScopedLock lock(mImpl->mMutex);
+  if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
   {
     return false;
   }
@@ -1316,6 +1456,7 @@ Dali::Devel::PixelBuffer GifLoading::LoadFrame(uint32_t frameIndex)
     return pixelBuffer;
   }
 
+  Mutex::ScopedLock lock(mImpl->mMutex);
   DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
 
   pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888);