[dali_2.3.27] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-image / animated-image-visual.cpp
index d5dcdaa..d94e0b5 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2022 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 
 // CLASS HEADER
  */
 
 // CLASS HEADER
-#include "animated-image-visual.h"
+#include <dali-toolkit/internal/visuals/animated-image/animated-image-visual.h>
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/image-loading.h>
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/debug.h>
+#include <dali/public-api/rendering/decorated-visual-renderer.h>
 #include <memory>
 
 // INTERNAL INCLUDES
 #include <memory>
 
 // INTERNAL INCLUDES
@@ -31,6 +33,7 @@
 #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/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/image-visual-shader-feature-builder.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-base-data-impl.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
@@ -46,7 +49,27 @@ namespace Internal
 {
 namespace
 {
 {
 namespace
 {
-const int CUSTOM_PROPERTY_COUNT(3); // ltr, wrap, pixel area,
+const int CUSTOM_PROPERTY_COUNT(5); // ltr, wrap, pixel area, crop to mask, mask texture ratio
+
+// fitting modes
+DALI_ENUM_TO_STRING_TABLE_BEGIN(FITTING_MODE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, SHRINK_TO_FIT)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, SCALE_TO_FILL)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, FIT_WIDTH)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, FIT_HEIGHT)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, DEFAULT)
+DALI_ENUM_TO_STRING_TABLE_END(FITTING_MODE)
+
+// sampling modes
+DALI_ENUM_TO_STRING_TABLE_BEGIN(SAMPLING_MODE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, NEAREST)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, LINEAR)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX_THEN_NEAREST)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX_THEN_LINEAR)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, NO_FILTER)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, DONT_CARE)
+DALI_ENUM_TO_STRING_TABLE_END(SAMPLING_MODE)
 
 // stop behavior
 DALI_ENUM_TO_STRING_TABLE_BEGIN(STOP_BEHAVIOR)
 
 // stop behavior
 DALI_ENUM_TO_STRING_TABLE_BEGIN(STOP_BEHAVIOR)
@@ -83,6 +106,8 @@ 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;
 
 static constexpr auto     LOOP_FOREVER = -1;
 static constexpr auto     FIRST_LOOP   = 0u;
 
+constexpr uint32_t TEXTURE_COUNT_FOR_GPU_ALPHA_MASK = 2u;
+
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
 #endif
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
 #endif
@@ -121,7 +146,7 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "
 
 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));
+  AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory, ImageDimensions()));
   visual->InitializeAnimatedImage(imageUrl);
   visual->SetProperties(properties);
 
   visual->InitializeAnimatedImage(imageUrl);
   visual->SetProperties(properties);
 
@@ -132,7 +157,7 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache
 
 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, ImageDimensions()));
   visual->mImageUrls = new ImageCache::UrlList();
   visual->mImageUrls->reserve(imageUrls.Count());
 
   visual->mImageUrls = new ImageCache::UrlList();
   visual->mImageUrls->reserve(imageUrls.Count());
 
@@ -151,9 +176,9 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache
   return visual;
 }
 
   return visual;
 }
 
-AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl)
+AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, ImageDimensions size)
 {
 {
-  AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory));
+  AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory, size));
   visual->InitializeAnimatedImage(imageUrl);
 
   visual->Initialize();
   visual->InitializeAnimatedImage(imageUrl);
 
   visual->Initialize();
@@ -163,8 +188,24 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache
 
 void AnimatedImageVisual::InitializeAnimatedImage(const VisualUrl& imageUrl)
 {
 
 void AnimatedImageVisual::InitializeAnimatedImage(const VisualUrl& imageUrl)
 {
-  mImageUrl = imageUrl;
+  mImageUrl             = imageUrl;
   mAnimatedImageLoading = AnimatedImageLoading::New(imageUrl.GetUrl(), imageUrl.IsLocalResource());
   mAnimatedImageLoading = AnimatedImageLoading::New(imageUrl.GetUrl(), imageUrl.IsLocalResource());
+
+  // If we fail to load the animated image, we will try to load as a normal image.
+  if(!mAnimatedImageLoading)
+  {
+    mImageUrls = new ImageCache::UrlList();
+    mImageUrls->reserve(SINGLE_IMAGE_COUNT);
+
+    for(unsigned int i = 0; i < SINGLE_IMAGE_COUNT; ++i)
+    {
+      ImageCache::UrlStore urlStore;
+      urlStore.mTextureId = TextureManager::INVALID_TEXTURE_ID;
+      urlStore.mUrl       = imageUrl;
+      mImageUrls->push_back(urlStore);
+    }
+    mFrameCount = SINGLE_IMAGE_COUNT;
+  }
 }
 
 void AnimatedImageVisual::CreateImageCache()
 }
 
 void AnimatedImageVisual::CreateImageCache()
@@ -175,7 +216,7 @@ void AnimatedImageVisual::CreateImageCache()
 
   if(mAnimatedImageLoading)
   {
 
   if(mAnimatedImageLoading)
   {
-    mImageCache = new RollingAnimatedImageCache(textureManager, mAnimatedImageLoading, *this, mCacheSize, mBatchSize, IsSynchronousLoadingRequired());
+    mImageCache = new RollingAnimatedImageCache(textureManager, mDesiredSize, mFittingMode, mSamplingMode, mAnimatedImageLoading, mMaskingData, *this, mCacheSize, mBatchSize, mWrapModeU, mWrapModeV, IsSynchronousLoadingRequired(), IsPreMultipliedAlphaEnabled());
   }
   else if(mImageUrls)
   {
   }
   else if(mImageUrls)
   {
@@ -186,22 +227,22 @@ void AnimatedImageVisual::CreateImageCache()
     uint16_t cacheSize = std::max(std::min(std::max(batchSize, mCacheSize), numUrls), MINIMUM_CACHESIZE);
     if(cacheSize < numUrls)
     {
     uint16_t cacheSize = std::max(std::min(std::max(batchSize, mCacheSize), numUrls), MINIMUM_CACHESIZE);
     if(cacheSize < numUrls)
     {
-      mImageCache = new RollingImageCache(textureManager, *mImageUrls, *this, cacheSize, batchSize, mFrameDelay);
+      mImageCache = new RollingImageCache(textureManager, mDesiredSize, mFittingMode, mSamplingMode, *mImageUrls, mMaskingData, *this, cacheSize, batchSize, mFrameDelay, IsPreMultipliedAlphaEnabled());
     }
     else
     {
     }
     else
     {
-      mImageCache = new FixedImageCache(textureManager, *mImageUrls, *this, batchSize, mFrameDelay);
+      mImageCache = new FixedImageCache(textureManager, mDesiredSize, mFittingMode, mSamplingMode, *mImageUrls, mMaskingData, *this, batchSize, mFrameDelay, IsPreMultipliedAlphaEnabled());
     }
   }
 
     }
   }
 
-  if(!mImageCache)
+  if(DALI_UNLIKELY(!mImageCache))
   {
     DALI_LOG_ERROR("mImageCache is null\n");
   }
 }
 
   {
     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, ImageDimensions desiredSize)
+: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::ANIMATED_IMAGE),
   mFrameDelayTimer(),
   mPlacementActor(),
   mImageVisualShaderFactory(shaderFactory),
   mFrameDelayTimer(),
   mPlacementActor(),
   mImageVisualShaderFactory(shaderFactory),
@@ -219,15 +260,20 @@ AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, Image
   mCurrentLoopIndex(FIRST_LOOP),
   mLoadPolicy(Toolkit::ImageVisual::LoadPolicy::ATTACHED),
   mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED),
   mCurrentLoopIndex(FIRST_LOOP),
   mLoadPolicy(Toolkit::ImageVisual::LoadPolicy::ATTACHED),
   mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED),
+  mMaskingData(),
+  mDesiredSize(desiredSize),
   mFrameCount(0),
   mImageSize(),
   mActionStatus(DevelAnimatedImageVisual::Action::PLAY),
   mWrapModeU(WrapMode::DEFAULT),
   mWrapModeV(WrapMode::DEFAULT),
   mFrameCount(0),
   mImageSize(),
   mActionStatus(DevelAnimatedImageVisual::Action::PLAY),
   mWrapModeU(WrapMode::DEFAULT),
   mWrapModeV(WrapMode::DEFAULT),
+  mFittingMode(FittingMode::VISUAL_FITTING),
+  mSamplingMode(SamplingMode::BOX_THEN_LINEAR),
   mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
   mStartFirstFrame(false),
   mIsJumpTo(false)
 {
   mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
   mStartFirstFrame(false),
   mIsJumpTo(false)
 {
+  EnablePreMultipliedAlpha(mFactoryCache.GetPreMultiplyOnLoad());
 }
 
 AnimatedImageVisual::~AnimatedImageVisual()
 }
 
 AnimatedImageVisual::~AnimatedImageVisual()
@@ -236,7 +282,10 @@ AnimatedImageVisual::~AnimatedImageVisual()
   // 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)
   {
   // 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();
+    if(DALI_LIKELY(mImageCache))
+    {
+      mImageCache->ClearCache();
+    }
   }
   delete mImageCache;
   delete mImageUrls;
   }
   delete mImageCache;
   delete mImageUrls;
@@ -244,15 +293,56 @@ AnimatedImageVisual::~AnimatedImageVisual()
 
 void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize)
 {
 
 void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize)
 {
+  if(mDesiredSize.GetWidth() > 0 && mDesiredSize.GetHeight() > 0)
+  {
+    if(mImpl->mRenderer)
+    {
+      auto textureSet = mImpl->mRenderer.GetTextures();
+      if(textureSet && textureSet.GetTextureCount())
+      {
+        auto texture = textureSet.GetTexture(0);
+        if(texture)
+        {
+          Dali::Vector2 textureSize;
+          textureSize.x = texture.GetWidth();
+          textureSize.y = texture.GetHeight();
+          if(textureSize != Vector2::ZERO)
+          {
+            naturalSize = textureSize;
+            return;
+          }
+        }
+      }
+    }
+
+    naturalSize.x = mDesiredSize.GetWidth();
+    naturalSize.y = mDesiredSize.GetHeight();
+    return;
+  }
+
+  naturalSize = Vector2::ZERO;
   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() && mAnimatedImageLoading)
     {
       mImageSize = mAnimatedImageLoading.GetImageSize();
     }
     else if(mImageUrls && mImageUrls->size() > 0)
     {
     {
       mImageSize = mAnimatedImageLoading.GetImageSize();
     }
     else if(mImageUrls && mImageUrls->size() > 0)
     {
-      mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl);
+      mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl.GetUrl());
     }
   }
 
     }
   }
 
@@ -278,7 +368,7 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const
     Property::Array urls;
     for(unsigned int i = 0; i < mImageUrls->size(); ++i)
     {
     Property::Array urls;
     for(unsigned int i = 0; i < mImageUrls->size(); ++i)
     {
-      urls.Add((*mImageUrls)[i].mUrl);
+      urls.Add((*mImageUrls)[i].mUrl.GetUrl());
     }
     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);
@@ -293,18 +383,49 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const
   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::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);
+
+  // This returns -1 until the loading is finished.
+  auto frameCount = int32_t(mFrameCount);
+  if(mImageCache && frameCount == 0)
+  {
+    frameCount = mImageCache->GetTotalFrameCount();
+
+    if(frameCount <= int32_t(SINGLE_IMAGE_COUNT) && mAnimatedImageLoading && mAnimatedImageLoading.HasLoadingSucceeded())
+    {
+      frameCount = int32_t(mAnimatedImageLoading.GetImageCount());
+    }
+    else
+    {
+      frameCount = -1;
+    }
+  }
+
+  map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, static_cast<int>(frameCount));
 
   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::DevelImageVisual::Property::MASKING_TYPE, mMaskingData->mPreappliedMasking ? DevelImageVisual::MaskingType::MASKING_ON_LOADING : DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
+  }
+
   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
   map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
   map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
+  map.Insert(Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode);
+  map.Insert(Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode);
+  map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
+  map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
 }
 
 void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
 {
 }
 
 void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
 {
-  // Do nothing
+  map.Clear();
+  map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE);
+  map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
+  map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
 }
 
 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)
@@ -337,7 +458,7 @@ void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const
     {
       // STOP reset functionality will actually be done in a future change
       // Stop will be executed on next timer tick
     {
       // STOP reset functionality will actually be done in a future change
       // Stop will be executed on next timer tick
-      mActionStatus = DevelAnimatedImageVisual::Action::STOP;
+      mActionStatus     = DevelAnimatedImageVisual::Action::STOP;
       mCurrentLoopIndex = FIRST_LOOP;
       if(IsOnScene())
       {
       mCurrentLoopIndex = FIRST_LOOP;
       if(IsOnScene())
       {
@@ -413,6 +534,22 @@ void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
       {
         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 == MASKING_TYPE_NAME)
+      {
+        DoSetProperty(Toolkit::DevelImageVisual::Property::MASKING_TYPE, keyValue.second);
+      }
       else if(keyValue.first == LOAD_POLICY_NAME)
       {
         DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
       else if(keyValue.first == LOAD_POLICY_NAME)
       {
         DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
@@ -425,6 +562,22 @@ void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
       {
         DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second);
       }
       {
         DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second);
       }
+      else if(keyValue.first == IMAGE_FITTING_MODE)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::FITTING_MODE, keyValue.second);
+      }
+      else if(keyValue.first == IMAGE_SAMPLING_MODE)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::SAMPLING_MODE, keyValue.second);
+      }
+      else if(keyValue.first == IMAGE_DESIRED_WIDTH)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_WIDTH, keyValue.second);
+      }
+      else if(keyValue.first == IMAGE_DESIRED_HEIGHT)
+      {
+        DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, keyValue.second);
+      }
     }
   }
   // Load image immediately if LOAD_POLICY requires it
     }
   }
   // Load image immediately if LOAD_POLICY requires it
@@ -511,7 +664,7 @@ void AnimatedImageVisual::DoSetProperty(Property::Index        index,
       if(value.Get(frameDelay))
       {
         mFrameDelay = frameDelay;
       if(value.Get(frameDelay))
       {
         mFrameDelay = frameDelay;
-        if(mImageCache)
+        if(DALI_LIKELY(mImageCache))
         {
           mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
         }
         {
           mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
         }
@@ -545,11 +698,55 @@ void AnimatedImageVisual::DoSetProperty(Property::Index        index,
       value.Get(sync);
       if(sync)
       {
       value.Get(sync);
       if(sync)
       {
-        mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
+        mImpl->mFlags |= Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
       }
       else
       {
       }
       else
       {
-        mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
+        mImpl->mFlags &= ~Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
+      }
+      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::DevelImageVisual::Property::MASKING_TYPE:
+    {
+      int maskingType = 0;
+      if(value.Get(maskingType))
+      {
+        AllocateMaskData();
+        mMaskingData->mPreappliedMasking = Toolkit::DevelImageVisual::MaskingType::Type(maskingType) == Toolkit::DevelImageVisual::MaskingType::MASKING_ON_LOADING ? true : false;
       }
       break;
     }
       }
       break;
     }
@@ -569,14 +766,67 @@ void AnimatedImageVisual::DoSetProperty(Property::Index        index,
       mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy);
       break;
     }
       mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy);
       break;
     }
+
+    case Toolkit::ImageVisual::Property::FITTING_MODE:
+    {
+      int fittingMode = 0;
+      Scripting::GetEnumerationProperty(value, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, fittingMode);
+      mFittingMode = Dali::FittingMode::Type(fittingMode);
+      break;
+    }
+
+    case Toolkit::ImageVisual::Property::SAMPLING_MODE:
+    {
+      int samplingMode = 0;
+      Scripting::GetEnumerationProperty(value, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, samplingMode);
+      mSamplingMode = Dali::SamplingMode::Type(samplingMode);
+      break;
+    }
+
+    case Toolkit::ImageVisual::Property::DESIRED_WIDTH:
+    {
+      float desiredWidth = 0.0f;
+      if(value.Get(desiredWidth))
+      {
+        mDesiredSize.SetWidth(desiredWidth);
+      }
+      else
+      {
+        DALI_LOG_ERROR("AnimatedImageVisual: desiredWidth property has incorrect type\n");
+      }
+      break;
+    }
+
+    case Toolkit::ImageVisual::Property::DESIRED_HEIGHT:
+    {
+      float desiredHeight = 0.0f;
+      if(value.Get(desiredHeight))
+      {
+        mDesiredSize.SetHeight(desiredHeight);
+      }
+      else
+      {
+        DALI_LOG_ERROR("AnimatedImageVisual: desiredHeight property has incorrect type\n");
+      }
+      break;
+    }
   }
 }
 
 void AnimatedImageVisual::DoSetOnScene(Actor& actor)
 {
   }
 }
 
 void AnimatedImageVisual::DoSetOnScene(Actor& actor)
 {
-  mStartFirstFrame      = true;
-  mPlacementActor       = actor;
+  mStartFirstFrame = true;
+  mPlacementActor  = actor;
   PrepareTextureSet();
   PrepareTextureSet();
+
+  actor.InheritedVisibilityChangedSignal().Connect(this, &AnimatedImageVisual::OnControlInheritedVisibilityChanged);
+
+  Window window = DevelWindow::Get(actor);
+  if(window)
+  {
+    mPlacementWindow = window;
+    DevelWindow::VisibilityChangedSignal(window).Connect(this, &AnimatedImageVisual::OnWindowVisibilityChanged);
+  }
 }
 
 void AnimatedImageVisual::DoSetOffScene(Actor& actor)
 }
 
 void AnimatedImageVisual::DoSetOffScene(Actor& actor)
@@ -592,7 +842,10 @@ void AnimatedImageVisual::DoSetOffScene(Actor& actor)
   actor.RemoveRenderer(mImpl->mRenderer);
   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
   {
   actor.RemoveRenderer(mImpl->mRenderer);
   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
   {
-    mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
+    if(DALI_LIKELY(mImageCache))
+    {
+      mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
+    }
     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
 
     TextureSet textureSet = TextureSet::New();
     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
 
     TextureSet textureSet = TextureSet::New();
@@ -600,9 +853,18 @@ void AnimatedImageVisual::DoSetOffScene(Actor& actor)
   }
 
   mPlacementActor.Reset();
   }
 
   mPlacementActor.Reset();
-  mStartFirstFrame = false;
+  mStartFirstFrame   = false;
   mCurrentFrameIndex = FIRST_FRAME_INDEX;
   mCurrentFrameIndex = FIRST_FRAME_INDEX;
-  mCurrentLoopIndex = FIRST_LOOP;
+  mCurrentLoopIndex  = FIRST_LOOP;
+
+  actor.InheritedVisibilityChangedSignal().Disconnect(this, &AnimatedImageVisual::OnControlInheritedVisibilityChanged);
+
+  Window window = mPlacementWindow.GetHandle();
+  if(window)
+  {
+    DevelWindow::VisibilityChangedSignal(window).Disconnect(this, &AnimatedImageVisual::OnWindowVisibilityChanged);
+    mPlacementWindow.Reset();
+  }
 }
 
 void AnimatedImageVisual::OnSetTransform()
 }
 
 void AnimatedImageVisual::OnSetTransform()
@@ -624,14 +886,16 @@ void AnimatedImageVisual::UpdateShader()
 
 Shader AnimatedImageVisual::GenerateShader() const
 {
 
 Shader AnimatedImageVisual::GenerateShader() const
 {
-  bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
+  bool   defaultWrapMode                 = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
+  bool   requiredAlphaMaskingOnRendering = (mMaskingData && !mMaskingData->mMaskImageLoadingFailed) ? !mMaskingData->mPreappliedMasking : false;
   Shader shader;
   shader = mImageVisualShaderFactory.GetShader(
     mFactoryCache,
   Shader shader;
   shader = mImageVisualShaderFactory.GetShader(
     mFactoryCache,
-    ImageVisualShaderFeature::FeatureBuilder()
+    ImageVisualShaderFeatureBuilder()
       .ApplyDefaultTextureWrapMode(defaultWrapMode)
       .EnableRoundedCorner(IsRoundedCornerRequired())
       .ApplyDefaultTextureWrapMode(defaultWrapMode)
       .EnableRoundedCorner(IsRoundedCornerRequired())
-      .EnableBorderline(IsBorderlineRequired()));
+      .EnableBorderline(IsBorderlineRequired())
+      .EnableAlphaMaskingOnRendering(requiredAlphaMaskingOnRendering));
   return shader;
 }
 
   return shader;
 }
 
@@ -644,7 +908,7 @@ void AnimatedImageVisual::OnInitialize()
 
   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
 
 
   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
 
-  mImpl->mRenderer = VisualRenderer::New(geometry, shader);
+  mImpl->mRenderer = DecoratedVisualRenderer::New(geometry, shader);
   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
 
   // Register transform properties
   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
 
   // Register transform properties
@@ -661,6 +925,17 @@ void AnimatedImageVisual::OnInitialize()
   {
     mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
   }
   {
     mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
   }
+
+  if(mMaskingData)
+  {
+    mImpl->mRenderer.RegisterProperty(CROP_TO_MASK_NAME, static_cast<float>(mMaskingData->mCropToMask));
+  }
+
+  // Enable PreMultipliedAlpha if it need premultiplied
+  auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
+                             ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
+                             : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
 }
 
 void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval)
 }
 
 void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval)
@@ -671,6 +946,7 @@ void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t first
   if(mImpl->mRenderer)
   {
     mImpl->mRenderer.SetTextures(textureSet);
   if(mImpl->mRenderer)
   {
     mImpl->mRenderer.SetTextures(textureSet);
+    CheckMaskTexture();
 
     Actor actor = mPlacementActor.GetHandle();
     if(actor)
 
     Actor actor = mPlacementActor.GetHandle();
     if(actor)
@@ -699,10 +975,15 @@ void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t first
 void AnimatedImageVisual::PrepareTextureSet()
 {
   TextureSet textureSet;
 void AnimatedImageVisual::PrepareTextureSet()
 {
   TextureSet textureSet;
-  if(mImageCache)
+  if(DALI_LIKELY(mImageCache))
   {
     textureSet = mImageCache->FirstFrame();
   }
   {
     textureSet = mImageCache->FirstFrame();
   }
+  else
+  {
+    // preMultiplied should be false because broken image don't premultiply alpha on load
+    FrameReady(TextureSet(), 0, false);
+  }
 
   // Check whether synchronous loading is true or false for the first frame.
   if(textureSet)
 
   // Check whether synchronous loading is true or false for the first frame.
   if(textureSet)
@@ -721,11 +1002,29 @@ void AnimatedImageVisual::SetImageSize(TextureSet& textureSet)
       mImageSize.SetWidth(texture.GetWidth());
       mImageSize.SetHeight(texture.GetHeight());
     }
       mImageSize.SetWidth(texture.GetWidth());
       mImageSize.SetHeight(texture.GetHeight());
     }
+
+    if(textureSet.GetTextureCount() > 1u && mMaskingData && mMaskingData->mCropToMask)
+    {
+      Texture maskTexture = textureSet.GetTexture(1);
+      if(maskTexture)
+      {
+        mImageSize.SetWidth(std::min(static_cast<uint32_t>(mImageSize.GetWidth() * mMaskingData->mContentScaleFactor), maskTexture.GetWidth()));
+        mImageSize.SetHeight(std::min(static_cast<uint32_t>(mImageSize.GetHeight() * mMaskingData->mContentScaleFactor), maskTexture.GetHeight()));
+
+        float   textureWidth  = std::max(static_cast<float>(texture.GetWidth() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
+        float   textureHeight = std::max(static_cast<float>(texture.GetHeight() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
+        Vector2 textureRatio(std::min(static_cast<float>(maskTexture.GetWidth()), textureWidth) / textureWidth,
+                             std::min(static_cast<float>(maskTexture.GetHeight()), textureHeight) / textureHeight);
+        mImpl->mRenderer.RegisterProperty(MASK_TEXTURE_RATIO_NAME, textureRatio);
+      }
+    }
   }
 }
 
   }
 }
 
-void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval)
+void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval, bool preMultiplied)
 {
 {
+  EnablePreMultipliedAlpha(preMultiplied);
+
   // When image visual requested to load new frame to mImageCache and it is failed.
   if(!mImageCache || !textureSet)
   {
   // When image visual requested to load new frame to mImageCache and it is failed.
   if(!mImageCache || !textureSet)
   {
@@ -735,7 +1034,10 @@ void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval)
 
   if(mStartFirstFrame)
   {
 
   if(mStartFirstFrame)
   {
-    mFrameCount = mImageCache->GetTotalFrameCount();
+    if(DALI_LIKELY(mImageCache))
+    {
+      mFrameCount = mImageCache->GetTotalFrameCount();
+    }
     StartFirstFrame(textureSet, interval);
   }
   else
     StartFirstFrame(textureSet, interval);
   }
   else
@@ -747,6 +1049,7 @@ void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval)
         mFrameDelayTimer.SetInterval(interval);
       }
       mImpl->mRenderer.SetTextures(textureSet);
         mFrameDelayTimer.SetInterval(interval);
       }
       mImpl->mRenderer.SetTextures(textureSet);
+      CheckMaskTexture();
     }
   }
 }
     }
   }
 }
@@ -756,7 +1059,7 @@ bool AnimatedImageVisual::DisplayNextFrame()
   TextureSet textureSet;
   bool       continueTimer = false;
 
   TextureSet textureSet;
   bool       continueTimer = false;
 
-  if(mImageCache)
+  if(DALI_LIKELY(mImageCache))
   {
     uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
 
   {
     uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
 
@@ -815,6 +1118,7 @@ bool AnimatedImageVisual::DisplayNextFrame()
       if(mImpl->mRenderer)
       {
         mImpl->mRenderer.SetTextures(textureSet);
       if(mImpl->mRenderer)
       {
         mImpl->mRenderer.SetTextures(textureSet);
+        CheckMaskTexture();
       }
       mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
     }
       }
       mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
     }
@@ -837,8 +1141,13 @@ TextureSet AnimatedImageVisual::SetLoadingFailed()
   {
     imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
   }
   {
     imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
   }
-  mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
-  TextureSet textureSet = mImpl->mRenderer.GetTextures();
+
+  TextureSet textureSet;
+  if(DALI_LIKELY(mImpl->mRenderer))
+  {
+    mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
+    textureSet = mImpl->mRenderer.GetTextures();
+  }
 
   if(mFrameDelayTimer)
   {
 
   if(mFrameDelayTimer)
   {
@@ -851,6 +1160,52 @@ TextureSet AnimatedImageVisual::SetLoadingFailed()
   return textureSet;
 }
 
   return textureSet;
 }
 
+void AnimatedImageVisual::AllocateMaskData()
+{
+  if(!mMaskingData)
+  {
+    mMaskingData.reset(new TextureManager::MaskingData());
+  }
+}
+
+void AnimatedImageVisual::CheckMaskTexture()
+{
+  if(mMaskingData && !mMaskingData->mPreappliedMasking)
+  {
+    bool       maskLoadFailed = true;
+    TextureSet textures       = mImpl->mRenderer.GetTextures();
+    if(textures && textures.GetTextureCount() >= TEXTURE_COUNT_FOR_GPU_ALPHA_MASK)
+    {
+      maskLoadFailed = false;
+    }
+    if(mMaskingData->mMaskImageLoadingFailed != maskLoadFailed)
+    {
+      mMaskingData->mMaskImageLoadingFailed = maskLoadFailed;
+      UpdateShader();
+    }
+  }
+}
+
+void AnimatedImageVisual::OnControlInheritedVisibilityChanged(Actor actor, bool visible)
+{
+  if(!visible && mActionStatus != DevelAnimatedImageVisual::Action::STOP)
+  {
+    mActionStatus = DevelAnimatedImageVisual::Action::STOP;
+    DisplayNextFrame();
+    DALI_LOG_INFO(gAnimImgLogFilter, Debug::Verbose, "AnimatedImageVisual::OnControlInheritedVisibilityChanged: invisibile. Pause animation [%p]\n", this);
+  }
+}
+
+void AnimatedImageVisual::OnWindowVisibilityChanged(Window window, bool visible)
+{
+  if(!visible && mActionStatus != DevelAnimatedImageVisual::Action::STOP)
+  {
+    mActionStatus = DevelAnimatedImageVisual::Action::STOP;
+    DisplayNextFrame();
+    DALI_LOG_INFO(gAnimImgLogFilter, Debug::Verbose, "AnimatedImageVisual::OnWindowVisibilityChanged: invisibile. Pause animation [%p]\n", this);
+  }
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
 } // namespace Internal
 
 } // namespace Toolkit