CPU Alpha Masking for Animated Image Visual
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-image / animated-image-visual.cpp
old mode 100755 (executable)
new mode 100644 (file)
index db1be08..b704d34
@@ -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.
 #include <memory>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
-#include <dali-toolkit/public-api/visuals/visual-properties.h>
-#include <dali-toolkit/internal/visuals/visual-factory-impl.h>
-#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
-#include <dali-toolkit/internal/visuals/visual-string-constants.h>
-#include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
-#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
-#include <dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h>
-#include <dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h>
-#include <dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h>
 #include <dali-toolkit/devel-api/image-loader/image-atlas.h>
 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
+#include <dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h>
+#include <dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h>
+#include <dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h>
+#include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
+#include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
+#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
+#include <dali-toolkit/internal/visuals/visual-factory-impl.h>
+#include <dali-toolkit/internal/visuals/visual-string-constants.h>
+#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
+#include <dali-toolkit/public-api/visuals/visual-properties.h>
 
 namespace Dali
 {
-
 namespace Toolkit
 {
-
 namespace Internal
 {
-
 namespace
 {
+const int CUSTOM_PROPERTY_COUNT(3); // ltr, wrap, pixel area,
+
 // stop behavior
-DALI_ENUM_TO_STRING_TABLE_BEGIN( STOP_BEHAVIOR )
-DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::DevelImageVisual::StopBehavior, CURRENT_FRAME )
-DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::DevelImageVisual::StopBehavior, FIRST_FRAME )
-DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::DevelImageVisual::StopBehavior, LAST_FRAME )
-DALI_ENUM_TO_STRING_TABLE_END( STOP_BEHAVIOR )
+DALI_ENUM_TO_STRING_TABLE_BEGIN(STOP_BEHAVIOR)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelImageVisual::StopBehavior, CURRENT_FRAME)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelImageVisual::StopBehavior, FIRST_FRAME)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelImageVisual::StopBehavior, LAST_FRAME)
+DALI_ENUM_TO_STRING_TABLE_END(STOP_BEHAVIOR)
 
 // wrap modes
-DALI_ENUM_TO_STRING_TABLE_BEGIN( WRAP_MODE )
-DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, DEFAULT )
-DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, CLAMP_TO_EDGE )
-DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, REPEAT )
-DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, MIRRORED_REPEAT )
-DALI_ENUM_TO_STRING_TABLE_END( WRAP_MODE )
-
-const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
-constexpr auto LOOP_FOREVER = -1;
+DALI_ENUM_TO_STRING_TABLE_BEGIN(WRAP_MODE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, DEFAULT)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, CLAMP_TO_EDGE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, REPEAT)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, MIRRORED_REPEAT)
+DALI_ENUM_TO_STRING_TABLE_END(WRAP_MODE)
+
+// load policies
+DALI_ENUM_TO_STRING_TABLE_BEGIN(LOAD_POLICY)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, IMMEDIATE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, ATTACHED)
+DALI_ENUM_TO_STRING_TABLE_END(LOAD_POLICY)
+
+// release policies
+DALI_ENUM_TO_STRING_TABLE_BEGIN(RELEASE_POLICY)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DETACHED)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DESTROYED)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, NEVER)
+DALI_ENUM_TO_STRING_TABLE_END(RELEASE_POLICY)
+
+static constexpr uint32_t SINGLE_IMAGE_COUNT = 1u;
+static constexpr uint32_t FIRST_FRAME_INDEX  = 0u;
+static constexpr uint16_t MINIMUM_CACHESIZE  = 1;
+static constexpr Vector4  FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
+static constexpr auto     LOOP_FOREVER = -1;
+static constexpr auto     FIRST_LOOP   = 0u;
 
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
 #endif
-}
-
+} // namespace
 
 /**
  * Multi-image  Flow of execution
  *
  *   | New
  *   |   DoSetProperties()
- *   |   LoadFirstBatch()
- *   |     new cache
- *   |       cache->LoadBatch()
+ *   |   OnInitialize()
+ *   |     CreateImageCache()
  *   |
  *   | DoSetOnScene()
  *   |   PrepareTextureSet()
  *   |     cache->FirstFrame()
- *   |   CreateRenderer()    (Doesn't become ready until first frame loads)
- *   |   StartFirstFrame()
  *   |
  *   | FrameReady(textureSet)
- *   |   start first frame:
+ *   |   StartFirstFrame:
  *   |     actor.AddRenderer
  *   |     start timer
  *   |   mRenderer.SetTextures(textureSet)
@@ -99,8 +111,7 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "
  *   |     if front frame is ready,
  *   |       mRenderer.SetTextures( front frame's texture )
  *   |     else
- *   |       mWaitingForTexture=true
- *   |     cache->LoadBatch()
+ *   |       Waiting for frame ready.
  *   |
  *   | FrameReady(textureSet)
  *   |   mRenderer.SetTextures(textureSet)
@@ -108,169 +119,225 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "
  *  Time
  */
 
-AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, const Property::Map& properties )
+AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, const Property::Map& properties)
 {
-  AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache, shaderFactory ) );
-  visual->InitializeAnimatedImage( imageUrl );
-  visual->SetProperties( properties );
-
-  if( visual->mFrameCount > 0 )
-  {
-    visual->LoadFirstBatch();
-  }
+  AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory));
+  visual->InitializeAnimatedImage(imageUrl);
+  visual->SetProperties(properties);
 
   visual->Initialize();
 
   return visual;
 }
 
-AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const Property::Array& imageUrls, const Property::Map& properties )
+AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const Property::Array& imageUrls, const Property::Map& properties)
 {
-  AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache, shaderFactory ) );
+  AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory));
   visual->mImageUrls = new ImageCache::UrlList();
-  visual->mImageUrls->reserve( imageUrls.Count() );
+  visual->mImageUrls->reserve(imageUrls.Count());
 
-  for( unsigned int i=0; i < imageUrls.Count(); ++i)
+  for(unsigned int i = 0; i < imageUrls.Count(); ++i)
   {
     ImageCache::UrlStore urlStore;
     urlStore.mTextureId = TextureManager::INVALID_TEXTURE_ID;
-    urlStore.mUrl = imageUrls[i].Get<std::string>();
-    visual->mImageUrls->push_back( urlStore );
+    urlStore.mUrl       = imageUrls[i].Get<std::string>();
+    visual->mImageUrls->push_back(urlStore);
   }
   visual->mFrameCount = imageUrls.Count();
-  visual->SetProperties( properties );
-
-  if( visual->mFrameCount > 0 )
-  {
-    visual->LoadFirstBatch();
-  }
+  visual->SetProperties(properties);
 
   visual->Initialize();
 
   return visual;
 }
 
-AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl )
+AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl)
 {
-  AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache, shaderFactory ) );
-  visual->InitializeAnimatedImage( imageUrl );
-
-  if( visual->mFrameCount > 0 )
-  {
-    visual->LoadFirstBatch();
-  }
+  AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory));
+  visual->InitializeAnimatedImage(imageUrl);
 
   visual->Initialize();
 
   return visual;
 }
 
-void AnimatedImageVisual::InitializeAnimatedImage( const VisualUrl& imageUrl )
+void AnimatedImageVisual::InitializeAnimatedImage(const VisualUrl& imageUrl)
 {
   mImageUrl = imageUrl;
-  mAnimatedImageLoading = AnimatedImageLoading::New( imageUrl.GetUrl(), imageUrl.IsLocalResource() );
-  mFrameCount = mAnimatedImageLoading.GetImageCount();
+  mAnimatedImageLoading = AnimatedImageLoading::New(imageUrl.GetUrl(), imageUrl.IsLocalResource());
+}
+
+void AnimatedImageVisual::CreateImageCache()
+{
+  DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::CreateImageCache()  batchSize:%d  cacheSize:%d\n", mBatchSize, mCacheSize);
+
+  TextureManager& textureManager = mFactoryCache.GetTextureManager();
+
+  if(mAnimatedImageLoading)
+  {
+    mImageCache = new RollingAnimatedImageCache(textureManager, mAnimatedImageLoading, mMaskingData, *this, mCacheSize, mBatchSize, IsSynchronousLoadingRequired());
+  }
+  else if(mImageUrls)
+  {
+    // Ensure the batch size and cache size are no bigger than the number of URLs,
+    // and that the cache is at least as big as the batch size.
+    uint16_t numUrls   = mImageUrls->size();
+    uint16_t batchSize = std::max(std::min(mBatchSize, numUrls), MINIMUM_CACHESIZE);
+    uint16_t cacheSize = std::max(std::min(std::max(batchSize, mCacheSize), numUrls), MINIMUM_CACHESIZE);
+    if(cacheSize < numUrls)
+    {
+      mImageCache = new RollingImageCache(textureManager, *mImageUrls, mMaskingData, *this, cacheSize, batchSize, mFrameDelay);
+    }
+    else
+    {
+      mImageCache = new FixedImageCache(textureManager, *mImageUrls, mMaskingData, *this, batchSize, mFrameDelay);
+    }
+  }
+
+  if(!mImageCache)
+  {
+    DALI_LOG_ERROR("mImageCache is null\n");
+  }
 }
 
-AnimatedImageVisual::AnimatedImageVisual( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory )
-: Visual::Base( factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::ANIMATED_IMAGE ),
+AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory)
+: Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::ANIMATED_IMAGE),
   mFrameDelayTimer(),
   mPlacementActor(),
-  mImageVisualShaderFactory( shaderFactory ),
-  mPixelArea( FULL_TEXTURE_RECT ),
+  mImageVisualShaderFactory(shaderFactory),
+  mPixelArea(FULL_TEXTURE_RECT),
   mImageUrl(),
   mAnimatedImageLoading(),
-  mFrameIndexForJumpTo( 0 ),
-  mImageUrls( NULL ),
-  mImageCache( NULL ),
-  mCacheSize( 2 ),
-  mBatchSize( 2 ),
-  mFrameDelay( 100 ),
-  mLoopCount( LOOP_FOREVER ),
-  mCurrentLoopIndex( 0 ),
-  mUrlIndex( 0 ),
-  mFrameCount( 0 ),
+  mFrameIndexForJumpTo(0),
+  mCurrentFrameIndex(FIRST_FRAME_INDEX),
+  mImageUrls(NULL),
+  mImageCache(NULL),
+  mCacheSize(2),
+  mBatchSize(2),
+  mFrameDelay(100),
+  mLoopCount(LOOP_FOREVER),
+  mCurrentLoopIndex(FIRST_LOOP),
+  mLoadPolicy(Toolkit::ImageVisual::LoadPolicy::ATTACHED),
+  mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED),
+  mMaskingData(),
+  mFrameCount(0),
   mImageSize(),
-  mWrapModeU( WrapMode::DEFAULT ),
-  mWrapModeV( WrapMode::DEFAULT ),
-  mActionStatus( DevelAnimatedImageVisual::Action::PLAY ),
-  mStopBehavior( DevelImageVisual::StopBehavior::CURRENT_FRAME ),
+  mActionStatus(DevelAnimatedImageVisual::Action::PLAY),
+  mWrapModeU(WrapMode::DEFAULT),
+  mWrapModeV(WrapMode::DEFAULT),
+  mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
   mStartFirstFrame(false),
-  mIsJumpTo( false )
-{}
+  mIsJumpTo(false)
+{
+}
 
 AnimatedImageVisual::~AnimatedImageVisual()
 {
+  // AnimatedImageVisual destroyed so remove texture unless ReleasePolicy is set to never release
+  // If this is animated image, clear cache. Else if this is single frame image, this is affected be release policy.
+  if(mFrameCount > SINGLE_IMAGE_COUNT || mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER)
+  {
+    mImageCache->ClearCache();
+  }
   delete mImageCache;
   delete mImageUrls;
 }
 
-void AnimatedImageVisual::GetNaturalSize( Vector2& naturalSize )
+void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize)
 {
-  if( mImageSize.GetWidth() == 0 &&  mImageSize.GetHeight() == 0)
+  if(mImageSize.GetWidth() == 0 && mImageSize.GetHeight() == 0)
   {
-    if( mImageUrl.IsValid() )
+    if(mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid() &&
+       mMaskingData->mCropToMask)
+    {
+      ImageDimensions dimensions = Dali::GetClosestImageSize(mMaskingData->mAlphaMaskUrl.GetUrl());
+      if(dimensions != ImageDimensions(0, 0))
+      {
+        mImageSize = dimensions;
+        naturalSize.x = dimensions.GetWidth();
+        naturalSize.y = dimensions.GetHeight();
+        return;
+      }
+    }
+
+    if(mImageUrl.IsValid())
     {
       mImageSize = mAnimatedImageLoading.GetImageSize();
     }
-    else if( mImageUrls && mImageUrls->size() > 0 )
+    else if(mImageUrls && mImageUrls->size() > 0)
     {
-      mImageSize = Dali::GetClosestImageSize( (*mImageUrls)[0].mUrl );
+      mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl);
     }
   }
 
-  naturalSize.width = mImageSize.GetWidth();
+  naturalSize.width  = mImageSize.GetWidth();
   naturalSize.height = mImageSize.GetHeight();
 }
 
-void AnimatedImageVisual::DoCreatePropertyMap( Property::Map& map ) const
+void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const
 {
   map.Clear();
 
   bool sync = IsSynchronousLoadingRequired();
-  map.Insert( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync );
+  map.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync);
 
-  map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE );
+  map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE);
 
-  if( mImageUrl.IsValid() )
+  if(mImageUrl.IsValid())
   {
-    map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
+    map.Insert(Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl());
   }
-  if( mImageUrls != NULL && ! mImageUrls->empty() )
+  if(mImageUrls != NULL && !mImageUrls->empty())
   {
     Property::Array urls;
-    for( unsigned int i=0; i<mImageUrls->size(); ++i)
+    for(unsigned int i = 0; i < mImageUrls->size(); ++i)
     {
-      urls.Add( (*mImageUrls)[i].mUrl );
+      urls.Add((*mImageUrls)[i].mUrl);
     }
-    Property::Value value( const_cast<Property::Array&>(urls) );
-    map.Insert( Toolkit::ImageVisual::Property::URL, value );
+    Property::Value value(const_cast<Property::Array&>(urls));
+    map.Insert(Toolkit::ImageVisual::Property::URL, value);
   }
 
-  map.Insert( Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea );
-  map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU );
-  map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV );
+  map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea);
+  map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU);
+  map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV);
 
-  map.Insert( Toolkit::ImageVisual::Property::BATCH_SIZE, static_cast<int>(mBatchSize) );
-  map.Insert( Toolkit::ImageVisual::Property::CACHE_SIZE, static_cast<int>(mCacheSize) );
-  map.Insert( Toolkit::ImageVisual::Property::FRAME_DELAY, static_cast<int>(mFrameDelay) );
-  map.Insert( Toolkit::DevelImageVisual::Property::LOOP_COUNT, static_cast<int>(mLoopCount) );
-  map.Insert( Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetCurrentFrameIndex()) : -1 );
-  map.Insert( Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetTotalFrameCount()) : -1 );
+  map.Insert(Toolkit::ImageVisual::Property::BATCH_SIZE, static_cast<int>(mBatchSize));
+  map.Insert(Toolkit::ImageVisual::Property::CACHE_SIZE, static_cast<int>(mCacheSize));
+  map.Insert(Toolkit::ImageVisual::Property::FRAME_DELAY, static_cast<int>(mFrameDelay));
+  map.Insert(Toolkit::DevelImageVisual::Property::LOOP_COUNT, static_cast<int>(mLoopCount));
+  map.Insert(Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetCurrentFrameIndex()) : -1);
+  map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>((mAnimatedImageLoading) ? mAnimatedImageLoading.GetImageCount() : 
+                                                                                                                                     mImageCache->GetTotalFrameCount()) : -1);
 
-  map.Insert( Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior );
+  map.Insert(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior);
+
+  if(mMaskingData != nullptr)
+  {
+    map.Insert(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl());
+    map.Insert(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor);
+    map.Insert(Toolkit::ImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask);
+  }
+
+  map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
+  map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
 }
 
-void AnimatedImageVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
+void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
 {
   // Do nothing
 }
 
-void AnimatedImageVisual::OnDoAction( const Dali::Property::Index actionId, const Dali::Property::Value& attributes )
+void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
 {
-  // Check if action is valid for this visual type and perform action if possible
+  // Make not set any action when the resource status is already failed.
+  if(mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED)
+  {
+    return;
+  }
 
-  switch ( actionId )
+  // Check if action is valid for this visual type and perform action if possible
+  switch(actionId)
   {
     case DevelAnimatedImageVisual::Action::PAUSE:
     {
@@ -280,7 +347,7 @@ void AnimatedImageVisual::OnDoAction( const Dali::Property::Index actionId, cons
     }
     case DevelAnimatedImageVisual::Action::PLAY:
     {
-      if( mFrameDelayTimer && IsOnScene() && mActionStatus != DevelAnimatedImageVisual::Action::PLAY )
+      if(mFrameDelayTimer && IsOnScene() && mActionStatus != DevelAnimatedImageVisual::Action::PLAY)
       {
         mFrameDelayTimer.Start();
       }
@@ -292,7 +359,8 @@ void AnimatedImageVisual::OnDoAction( const Dali::Property::Index actionId, cons
       // STOP reset functionality will actually be done in a future change
       // Stop will be executed on next timer tick
       mActionStatus = DevelAnimatedImageVisual::Action::STOP;
-      if( IsOnScene() )
+      mCurrentLoopIndex = FIRST_LOOP;
+      if(IsOnScene())
       {
         DisplayNextFrame();
       }
@@ -301,17 +369,17 @@ void AnimatedImageVisual::OnDoAction( const Dali::Property::Index actionId, cons
     case DevelAnimatedImageVisual::Action::JUMP_TO:
     {
       int32_t frameNumber;
-      if( attributes.Get( frameNumber ) )
+      if(attributes.Get(frameNumber))
       {
-        if( frameNumber < 0 || frameNumber >= static_cast<int32_t>( mFrameCount ) )
+        if(frameNumber < 0 || frameNumber >= static_cast<int32_t>(mFrameCount))
         {
-          DALI_LOG_ERROR( "Invalid frame index used.\n" );
+          DALI_LOG_ERROR("Invalid frame index used.\n");
         }
         else
         {
-          mIsJumpTo = true;
+          mIsJumpTo            = true;
           mFrameIndexForJumpTo = frameNumber;
-          if( IsOnScene() )
+          if(IsOnScene())
           {
             DisplayNextFrame();
           }
@@ -322,69 +390,97 @@ void AnimatedImageVisual::OnDoAction( const Dali::Property::Index actionId, cons
   }
 }
 
-void AnimatedImageVisual::DoSetProperties( const Property::Map& propertyMap )
+void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
 {
   // url[s] already passed in from constructor
-
-  for( Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter )
+  for(Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter)
   {
-    KeyValuePair keyValue = propertyMap.GetKeyValue( iter );
-    if( keyValue.first.type == Property::Key::INDEX )
+    KeyValuePair keyValue = propertyMap.GetKeyValue(iter);
+    if(keyValue.first.type == Property::Key::INDEX)
     {
-      DoSetProperty( keyValue.first.indexKey, keyValue.second );
+      DoSetProperty(keyValue.first.indexKey, keyValue.second);
     }
     else
     {
-      if( keyValue.first == PIXEL_AREA_UNIFORM_NAME )
+      if(keyValue.first == PIXEL_AREA_UNIFORM_NAME)
       {
-        DoSetProperty( Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second );
+        DoSetProperty(Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second);
       }
-      else if( keyValue.first == IMAGE_WRAP_MODE_U )
+      else if(keyValue.first == IMAGE_WRAP_MODE_U)
       {
-        DoSetProperty( Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second );
+        DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second);
       }
-      else if( keyValue.first == IMAGE_WRAP_MODE_V )
+      else if(keyValue.first == IMAGE_WRAP_MODE_V)
       {
-        DoSetProperty( Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second );
+        DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second);
       }
-      else if( keyValue.first == BATCH_SIZE_NAME )
+      else if(keyValue.first == BATCH_SIZE_NAME)
       {
-        DoSetProperty( Toolkit::ImageVisual::Property::BATCH_SIZE, keyValue.second );
+        DoSetProperty(Toolkit::ImageVisual::Property::BATCH_SIZE, keyValue.second);
       }
-      else if( keyValue.first == CACHE_SIZE_NAME )
+      else if(keyValue.first == CACHE_SIZE_NAME)
       {
-        DoSetProperty( Toolkit::ImageVisual::Property::CACHE_SIZE, keyValue.second );
+        DoSetProperty(Toolkit::ImageVisual::Property::CACHE_SIZE, keyValue.second);
       }
-      else if( keyValue.first == FRAME_DELAY_NAME )
+      else if(keyValue.first == FRAME_DELAY_NAME)
       {
-        DoSetProperty( Toolkit::ImageVisual::Property::FRAME_DELAY, keyValue.second );
+        DoSetProperty(Toolkit::ImageVisual::Property::FRAME_DELAY, keyValue.second);
       }
-      else if( keyValue.first == LOOP_COUNT_NAME )
+      else if(keyValue.first == LOOP_COUNT_NAME)
       {
-        DoSetProperty( Toolkit::DevelImageVisual::Property::LOOP_COUNT, keyValue.second );
+        DoSetProperty(Toolkit::DevelImageVisual::Property::LOOP_COUNT, keyValue.second);
       }
-      else if( keyValue.first == STOP_BEHAVIOR_NAME )
+      else if(keyValue.first == STOP_BEHAVIOR_NAME)
       {
-         DoSetProperty( Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, keyValue.second );
+        DoSetProperty(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, keyValue.second);
+      }
+      else if(keyValue.first == ALPHA_MASK_URL)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, keyValue.second);
+      }
+      else if(keyValue.first == MASK_CONTENT_SCALE_NAME)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, keyValue.second);
+      }
+      else if(keyValue.first == CROP_TO_MASK_NAME)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::CROP_TO_MASK, keyValue.second);
+      }
+      else if(keyValue.first == LOAD_POLICY_NAME)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
+      }
+      else if(keyValue.first == RELEASE_POLICY_NAME)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::RELEASE_POLICY, keyValue.second);
+      }
+      else if(keyValue.first == SYNCHRONOUS_LOADING)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second);
       }
     }
   }
+  // Load image immediately if LOAD_POLICY requires it
+  if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE)
+  {
+    PrepareTextureSet();
+  }
 }
 
-void AnimatedImageVisual::DoSetProperty( Property::Index index,
-                                         const Property::Value& value )
+void AnimatedImageVisual::DoSetProperty(Property::Index        index,
+                                        const Property::Value& value)
 {
   switch(index)
   {
     case Toolkit::ImageVisual::Property::PIXEL_AREA:
     {
-      value.Get( mPixelArea );
+      value.Get(mPixelArea);
       break;
     }
     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
     {
       int wrapMode = 0;
-      if(Scripting::GetEnumerationProperty( value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode ))
+      if(Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode))
       {
         mWrapModeU = Dali::WrapMode::Type(wrapMode);
       }
@@ -397,7 +493,7 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
     {
       int wrapMode = 0;
-      if(Scripting::GetEnumerationProperty( value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode ))
+      if(Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode))
       {
         mWrapModeV = Dali::WrapMode::Type(wrapMode);
       }
@@ -411,11 +507,11 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
     case Toolkit::ImageVisual::Property::BATCH_SIZE:
     {
       int batchSize;
-      if( value.Get( batchSize ) )
+      if(value.Get(batchSize))
       {
-        if( batchSize < 2 )
+        if(batchSize < 2)
         {
-          DALI_LOG_ERROR( "The minimum value of batch size is 2." );
+          DALI_LOG_ERROR("The minimum value of batch size is 2.");
         }
         else
         {
@@ -428,11 +524,11 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
     case Toolkit::ImageVisual::Property::CACHE_SIZE:
     {
       int cacheSize;
-      if( value.Get( cacheSize ) )
+      if(value.Get(cacheSize))
       {
-        if( cacheSize < 2 )
+        if(cacheSize < 2)
         {
-          DALI_LOG_ERROR( "The minimum value of cache size is 2." );
+          DALI_LOG_ERROR("The minimum value of cache size is 2.");
         }
         else
         {
@@ -445,9 +541,13 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
     case Toolkit::ImageVisual::Property::FRAME_DELAY:
     {
       int frameDelay;
-      if( value.Get( frameDelay ) )
+      if(value.Get(frameDelay))
       {
         mFrameDelay = frameDelay;
+        if(mImageCache)
+        {
+          mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
+        }
       }
       break;
     }
@@ -455,7 +555,7 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
     case Toolkit::DevelImageVisual::Property::LOOP_COUNT:
     {
       int loopCount;
-      if( value.Get( loopCount ) )
+      if(value.Get(loopCount))
       {
         mLoopCount = loopCount;
       }
@@ -465,9 +565,9 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
     case Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR:
     {
       int32_t stopBehavior = mStopBehavior;
-      if( Scripting::GetEnumerationProperty( value, STOP_BEHAVIOR_TABLE, STOP_BEHAVIOR_TABLE_COUNT, stopBehavior ) )
+      if(Scripting::GetEnumerationProperty(value, STOP_BEHAVIOR_TABLE, STOP_BEHAVIOR_TABLE_COUNT, stopBehavior))
       {
-        mStopBehavior = DevelImageVisual::StopBehavior::Type( stopBehavior );
+        mStopBehavior = DevelImageVisual::StopBehavior::Type(stopBehavior);
       }
       break;
     }
@@ -475,8 +575,8 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
     {
       bool sync = false;
-      value.Get( sync );
-      if( sync )
+      value.Get(sync);
+      if(sync)
       {
         mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
       }
@@ -486,239 +586,263 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
       }
       break;
     }
+
+    case Toolkit::ImageVisual::Property::ALPHA_MASK_URL:
+    {
+      std::string alphaUrl = "";
+      if(value.Get(alphaUrl))
+      {
+        AllocateMaskData();
+        mMaskingData->mAlphaMaskUrl = alphaUrl;
+      }
+      break;
+    }
+
+    case Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE:
+    {
+      float scale = 1.0f;
+      if(value.Get(scale))
+      {
+        AllocateMaskData();
+        mMaskingData->mContentScaleFactor = scale;
+      }
+      break;
+    }
+
+    case Toolkit::ImageVisual::Property::CROP_TO_MASK:
+    {
+      bool crop = false;
+      if(value.Get(crop))
+      {
+        AllocateMaskData();
+        mMaskingData->mCropToMask = crop;
+      }
+      break;
+    }
+
+    case Toolkit::ImageVisual::Property::RELEASE_POLICY:
+    {
+      int releasePolicy = 0;
+      Scripting::GetEnumerationProperty(value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy);
+      mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type(releasePolicy);
+      break;
+    }
+
+    case Toolkit::ImageVisual::Property::LOAD_POLICY:
+    {
+      int loadPolicy = 0;
+      Scripting::GetEnumerationProperty(value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy);
+      mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy);
+      break;
+    }
   }
 }
 
-void AnimatedImageVisual::DoSetOnScene( Actor& actor )
+void AnimatedImageVisual::DoSetOnScene(Actor& actor)
 {
-  mPlacementActor = actor;
-  TextureSet textureSet = PrepareTextureSet();
-
-  if( textureSet ) // if the image loading is successful
-  {
-    StartFirstFrame( textureSet );
-  }
-  else
-  {
-    mStartFirstFrame = true;
-  }
+  mStartFirstFrame      = true;
+  mPlacementActor       = actor;
+  PrepareTextureSet();
 }
 
-void AnimatedImageVisual::DoSetOffScene( Actor& actor )
+void AnimatedImageVisual::DoSetOffScene(Actor& actor)
 {
-  DALI_ASSERT_DEBUG( (bool)mImpl->mRenderer && "There should always be a renderer whilst on stage");
+  DALI_ASSERT_DEBUG((bool)mImpl->mRenderer && "There should always be a renderer whilst on stage");
 
-  if( mFrameDelayTimer )
+  if(mFrameDelayTimer)
   {
     mFrameDelayTimer.Stop();
     mFrameDelayTimer.Reset();
   }
 
-  actor.RemoveRenderer( mImpl->mRenderer );
+  actor.RemoveRenderer(mImpl->mRenderer);
+  if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
+  {
+    mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
+    mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
+
+    TextureSet textureSet = TextureSet::New();
+    mImpl->mRenderer.SetTextures(textureSet);
+  }
+
   mPlacementActor.Reset();
   mStartFirstFrame = false;
+  mCurrentFrameIndex = FIRST_FRAME_INDEX;
+  mCurrentLoopIndex = FIRST_LOOP;
 }
 
 void AnimatedImageVisual::OnSetTransform()
 {
-  if( mImpl->mRenderer )
+  if(mImpl->mRenderer)
   {
-    mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
+    mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
   }
 }
 
-void AnimatedImageVisual::OnInitialize()
+void AnimatedImageVisual::UpdateShader()
 {
-  bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
-  bool atlasing = false;
-  Shader shader = mImageVisualShaderFactory.GetShader( mFactoryCache, atlasing, defaultWrapMode, IsRoundedCornerRequired() );
-
-  Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
-
-  mImpl->mRenderer = Renderer::New( geometry, shader );
-
-  // Register transform properties
-  mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
-
-  if( !defaultWrapMode ) // custom wrap mode
+  if(mImpl->mRenderer)
   {
-    Vector2 wrapMode(mWrapModeU-WrapMode::CLAMP_TO_EDGE, mWrapModeV-WrapMode::CLAMP_TO_EDGE);
-    wrapMode.Clamp( Vector2::ZERO, Vector2( 2.f, 2.f ) );
-    mImpl->mRenderer.RegisterProperty( WRAP_MODE_UNIFORM_NAME, wrapMode );
+    Shader shader = GenerateShader();
+    mImpl->mRenderer.SetShader(shader);
   }
+}
 
-  if( mPixelArea != FULL_TEXTURE_RECT )
-  {
-    mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
-  }
+Shader AnimatedImageVisual::GenerateShader() const
+{
+  bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
+  Shader shader;
+  shader = mImageVisualShaderFactory.GetShader(
+    mFactoryCache,
+    ImageVisualShaderFeature::FeatureBuilder()
+      .ApplyDefaultTextureWrapMode(defaultWrapMode)
+      .EnableRoundedCorner(IsRoundedCornerRequired())
+      .EnableBorderline(IsBorderlineRequired()));
+  return shader;
 }
 
-void AnimatedImageVisual::LoadFirstBatch()
+void AnimatedImageVisual::OnInitialize()
 {
-  // Ensure the batch size and cache size are no bigger than the number of URLs,
-  // and that the cache is at least as big as the batch size.
-  uint16_t numUrls = 0;
-  uint16_t batchSize = 1;
-  uint16_t cacheSize = 1;
+  CreateImageCache();
 
-  if( mImageUrls )
-  {
-    numUrls = mImageUrls->size();
-  }
-  else
-  {
-    numUrls = mFrameCount;
-  }
+  bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
+  Shader shader          = GenerateShader();
 
-  batchSize = std::min( mBatchSize, numUrls );
-  cacheSize = std::min( std::max( batchSize, mCacheSize ), numUrls );
+  Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
 
-  DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::LoadFirstBatch()  batchSize:%d  cacheSize:%d\n", batchSize, cacheSize);
+  mImpl->mRenderer = VisualRenderer::New(geometry, shader);
+  mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
 
-  mUrlIndex = 0;
-  TextureManager& textureManager = mFactoryCache.GetTextureManager();
+  // Register transform properties
+  mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
 
-  if( mAnimatedImageLoading )
-  {
-    mImageCache = new RollingAnimatedImageCache( textureManager, mAnimatedImageLoading, mFrameCount, *this, cacheSize, batchSize, IsSynchronousLoadingRequired() );
-  }
-  else if( mImageUrls )
+  if(!defaultWrapMode) // custom wrap mode
   {
-    if( batchSize > 0 && cacheSize > 0 )
-    {
-      if( cacheSize < numUrls )
-      {
-        mImageCache = new RollingImageCache( textureManager, *mImageUrls, *this, cacheSize, batchSize );
-      }
-      else
-      {
-        mImageCache = new FixedImageCache( textureManager, *mImageUrls, *this, batchSize );
-      }
-    }
-    else
-    {
-      mImageCache = new RollingImageCache( textureManager, *mImageUrls, *this, 1, 1 );
-    }
+    Vector2 wrapMode(mWrapModeU - WrapMode::CLAMP_TO_EDGE, mWrapModeV - WrapMode::CLAMP_TO_EDGE);
+    wrapMode.Clamp(Vector2::ZERO, Vector2(2.f, 2.f));
+    mImpl->mRenderer.RegisterProperty(WRAP_MODE_UNIFORM_NAME, wrapMode);
   }
 
-  if (!mImageCache)
+  if(mPixelArea != FULL_TEXTURE_RECT)
   {
-    DALI_LOG_ERROR("mImageCache is null\n");
+    mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
   }
 }
 
-void AnimatedImageVisual::StartFirstFrame( TextureSet& textureSet )
+void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval)
 {
-  DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::StartFirstFrame()\n");
+  DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::StartFirstFrame()\n");
 
   mStartFirstFrame = false;
   if(mImpl->mRenderer)
   {
-    mImpl->mRenderer.SetTextures( textureSet );
+    mImpl->mRenderer.SetTextures(textureSet);
 
     Actor actor = mPlacementActor.GetHandle();
-    if( actor )
+    if(actor)
     {
-      actor.AddRenderer( mImpl->mRenderer );
+      actor.AddRenderer(mImpl->mRenderer);
       mPlacementActor.Reset();
     }
   }
 
-  if( mFrameCount > 1 )
+  if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED)
   {
-    int frameDelay = mImageCache->GetFrameInterval( 0 );
-    if( frameDelay == 0u )
+    if(mFrameCount > SINGLE_IMAGE_COUNT)
     {
-      frameDelay = mFrameDelay; // from URL array
+      mFrameDelayTimer = Timer::New(firstInterval);
+      mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame);
+      mFrameDelayTimer.Start();
     }
-    mFrameDelayTimer = Timer::New( frameDelay );
-    mFrameDelayTimer.TickSignal().Connect( this, &AnimatedImageVisual::DisplayNextFrame );
-    mFrameDelayTimer.Start();
+
+    DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::READY)\n");
+    ResourceReady(Toolkit::Visual::ResourceStatus::READY);
   }
-  DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"ResourceReady(ResourceStatus::READY)\n");
-  ResourceReady( Toolkit::Visual::ResourceStatus::READY );
+
+  mCurrentFrameIndex = FIRST_FRAME_INDEX;
 }
 
-TextureSet AnimatedImageVisual::PrepareTextureSet()
+void AnimatedImageVisual::PrepareTextureSet()
 {
   TextureSet textureSet;
-  if (mImageCache)
+  if(mImageCache)
   {
     textureSet = mImageCache->FirstFrame();
   }
 
-  if( textureSet )
+  // Check whether synchronous loading is true or false for the first frame.
+  if(textureSet)
   {
-    SetImageSize( textureSet );
+    SetImageSize(textureSet);
   }
-
-  return textureSet;
 }
 
-void AnimatedImageVisual::SetImageSize( TextureSet& textureSet )
+void AnimatedImageVisual::SetImageSize(TextureSet& textureSet)
 {
-  if( textureSet )
+  if(textureSet)
   {
-    Texture texture = textureSet.GetTexture( 0 );
-    if( texture )
+    Texture texture = textureSet.GetTexture(0);
+    if(texture)
     {
-      mImageSize.SetWidth( texture.GetWidth() );
-      mImageSize.SetHeight( texture.GetHeight() );
+      mImageSize.SetWidth(texture.GetWidth());
+      mImageSize.SetHeight(texture.GetHeight());
     }
   }
 }
 
-void AnimatedImageVisual::FrameReady( TextureSet textureSet )
+void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval)
 {
-  if(textureSet)
+  // When image visual requested to load new frame to mImageCache and it is failed.
+  if(!mImageCache || !textureSet)
   {
-    SetImageSize(textureSet);
+    textureSet = SetLoadingFailed();
+  }
+  SetImageSize(textureSet);
 
-    if(mStartFirstFrame)
-    {
-      StartFirstFrame(textureSet);
-    }
-    else
+  if(mStartFirstFrame)
+  {
+    mFrameCount = mImageCache->GetTotalFrameCount();
+    StartFirstFrame(textureSet, interval);
+  }
+  else
+  {
+    if(mImpl->mRenderer)
     {
-      if(mImpl->mRenderer)
+      if(mFrameDelayTimer && interval > 0u)
       {
-        mImpl->mRenderer.SetTextures(textureSet);
+        mFrameDelayTimer.SetInterval(interval);
       }
+      mImpl->mRenderer.SetTextures(textureSet);
     }
   }
-  else
-  {
-    DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n" );
-    ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
-  }
 }
 
 bool AnimatedImageVisual::DisplayNextFrame()
 {
-  bool continueTimer = false;
+  TextureSet textureSet;
+  bool       continueTimer = false;
 
   if(mImageCache)
   {
-    bool nextFrame = false;
     uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
 
-    if( mIsJumpTo )
+    if(mIsJumpTo)
     {
-      mIsJumpTo = false;
+      mIsJumpTo  = false;
       frameIndex = mFrameIndexForJumpTo;
     }
-    else if( mActionStatus == DevelAnimatedImageVisual::Action::PAUSE )
+    else if(mActionStatus == DevelAnimatedImageVisual::Action::PAUSE)
     {
       return false;
     }
-    else if( mActionStatus == DevelAnimatedImageVisual::Action::STOP )
+    else if(mActionStatus == DevelAnimatedImageVisual::Action::STOP)
     {
-      frameIndex = 0;
-      if( mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME )
+      mCurrentLoopIndex = FIRST_LOOP;
+      if(mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME)
       {
-        frameIndex = 0;
+        frameIndex = FIRST_FRAME_INDEX;
       }
-      else if( mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME )
+      else if(mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME)
       {
         frameIndex = mFrameCount - 1;
       }
@@ -729,13 +853,12 @@ bool AnimatedImageVisual::DisplayNextFrame()
     }
     else
     {
-      if( mFrameCount > 1 )
+      if(mFrameCount > SINGLE_IMAGE_COUNT)
       {
-        nextFrame = true;
         frameIndex++;
-        if( frameIndex >= mFrameCount )
+        if(frameIndex >= mFrameCount)
         {
-          frameIndex %= mFrameCount;
+          frameIndex = FIRST_FRAME_INDEX;
           ++mCurrentLoopIndex;
         }
 
@@ -746,44 +869,61 @@ bool AnimatedImageVisual::DisplayNextFrame()
           return DisplayNextFrame();
         }
       }
-
-      unsigned int delay = mImageCache->GetFrameInterval( frameIndex );
-      if( delay > 0u )
-      {
-        if( mFrameDelayTimer.GetInterval() != delay )
-        {
-          mFrameDelayTimer.SetInterval( delay );
-        }
-      }
     }
 
-    DALI_LOG_INFO( gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
+    DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
 
-    TextureSet textureSet;
-    if(nextFrame)
-    {
-      textureSet = mImageCache->NextFrame();
-    }
-    else
-    {
-      textureSet = mImageCache->Frame( frameIndex );
-    }
+    textureSet = mImageCache->Frame(frameIndex);
 
-    if( textureSet )
+    if(textureSet)
     {
-      SetImageSize( textureSet );
-      if( mImpl->mRenderer )
+      SetImageSize(textureSet);
+      if(mImpl->mRenderer)
       {
-        mImpl->mRenderer.SetTextures( textureSet );
+        mImpl->mRenderer.SetTextures(textureSet);
       }
+      mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
     }
 
-    continueTimer = ( mActionStatus == DevelAnimatedImageVisual::Action::PLAY ) ? true : false;
+    mCurrentFrameIndex = frameIndex;
+    continueTimer      = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY && textureSet) ? true : false;
   }
 
   return continueTimer;
 }
 
+TextureSet AnimatedImageVisual::SetLoadingFailed()
+{
+  DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n");
+  ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
+
+  Actor   actor     = mPlacementActor.GetHandle();
+  Vector2 imageSize = Vector2::ZERO;
+  if(actor)
+  {
+    imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
+  }
+  mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
+  TextureSet textureSet = mImpl->mRenderer.GetTextures();
+
+  if(mFrameDelayTimer)
+  {
+    mFrameDelayTimer.Stop();
+    mFrameDelayTimer.Reset();
+  }
+
+  SetImageSize(textureSet);
+
+  return textureSet;
+}
+
+void AnimatedImageVisual::AllocateMaskData()
+{
+  if(!mMaskingData)
+  {
+    mMaskingData.reset(new TextureManager::MaskingData());
+  }
+}
 
 } // namespace Internal