[Tizen] GPU Alpha Masking for Animated Image Visual 97/267997/15
authorseungho <sbsh.baek@samsung.com>
Tue, 14 Dec 2021 08:20:09 +0000 (17:20 +0900)
committerseungho <sbsh.baek@samsung.com>
Wed, 29 Dec 2021 04:29:34 +0000 (13:29 +0900)
Change-Id: I1780deec4964ae76656fc83a23781870b07b180c
Signed-off-by: seungho <sbsh.baek@samsung.com>
dali-toolkit/devel-api/visuals/image-visual-properties-devel.h
dali-toolkit/internal/graphics/shaders/image-visual-shader.frag
dali-toolkit/internal/graphics/shaders/image-visual-shader.vert
dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp
dali-toolkit/internal/visuals/image-visual-shader-factory.cpp
dali-toolkit/internal/visuals/image-visual-shader-factory.h
dali-toolkit/internal/visuals/texture-manager-impl.cpp
dali-toolkit/internal/visuals/texture-manager-impl.h
dali-toolkit/internal/visuals/visual-factory-cache.h
dali-toolkit/internal/visuals/visual-string-constants.cpp
dali-toolkit/internal/visuals/visual-string-constants.h

index 413b556..0cfb9f6 100644 (file)
@@ -145,7 +145,16 @@ enum Type
    * @details Name "redrawInScalingDown", type Property::BOOLEAN.
    * @note It is used in the AnimatedVectorImageVisual. The default is true.
    */
-  REDRAW_IN_SCALING_DOWN
+  REDRAW_IN_SCALING_DOWN = ORIENTATION_CORRECTION + 11,
+
+  /**
+   * @brief Whether to apply mask in loading time or not.
+   * @details Name "preappliedMask", type Property::BOOLEAN.
+   * If it is true, mask image is applied on the texture in loading time with CPU.
+   * If it is false, mask image is applied in runtime in shader.
+   * @note It is used in the ImageVisual and AnimatedImageVisual. The default is true.
+   */
+  PREAPPLIED_MASK = ORIENTATION_CORRECTION + 12
 };
 
 } //namespace Property
index 703c649..c2324e7 100644 (file)
@@ -4,6 +4,9 @@
 #ifndef IS_REQUIRED_BORDERLINE
 #define IS_REQUIRED_BORDERLINE 0
 #endif
+#ifndef IS_REQUIRED_ALPHA_MASKING
+#define IS_REQUIRED_ALPHA_MASKING 0
+#endif
 #ifndef ATLAS_DEFAULT_WARP
 #define ATLAS_DEFAULT_WARP 0
 #endif
@@ -22,6 +25,12 @@ INPUT mediump vec4 vCornerRadius;
 #endif
 
 uniform sampler2D sTexture;
+
+#if IS_REQUIRED_ALPHA_MASKING
+uniform sampler2D sMaskTexture;
+INPUT mediump vec2 vMaskTexCoord;
+#endif
+
 #if ATLAS_DEFAULT_WARP
 uniform mediump vec4 uAtlasRect;
 #elif ATLAS_CUSTOM_WARP
@@ -228,6 +237,12 @@ void main()
 
   lowp vec4 textureColor = TEXTURE( sTexture, texCoord ) * vec4( mixColor, 1.0 ) * uColor;
 
+#if IS_REQUIRED_ALPHA_MASKING
+  mediump float maskAlpha = TEXTURE(sMaskTexture, vMaskTexCoord).a;
+  textureColor.a *= maskAlpha;
+  textureColor.rgb *= mix(1.0, maskAlpha, preMultipliedAlpha);
+#endif
+
 #if IS_REQUIRED_ROUNDED_CORNER || IS_REQUIRED_BORDERLINE
   // skip most potential calculate for performance
   if(abs(vPosition.x) < vOptRectSize.x && abs(vPosition.y) < vOptRectSize.y)
index 21d4084..de8290b 100644 (file)
@@ -4,6 +4,9 @@
 #ifndef IS_REQUIRED_BORDERLINE
 #define IS_REQUIRED_BORDERLINE 0
 #endif
+#ifndef IS_REQUIRED_ALPHA_MASKING
+#define IS_REQUIRED_ALPHA_MASKING 0
+#endif
 
 INPUT mediump vec2 aPosition;
 OUTPUT mediump vec2 vTexCoord;
@@ -34,6 +37,13 @@ uniform mediump float borderlineOffset;
 uniform mediump vec4 cornerRadius;
 uniform mediump float cornerRadiusPolicy;
 #endif
+
+#if IS_REQUIRED_ALPHA_MASKING
+OUTPUT  mediump vec2  vMaskTexCoord;
+uniform lowp    float cropToMask;
+uniform mediump vec2  maskTextureRatio;
+#endif
+
 uniform mediump vec2 extraSize;
 
 vec4 ComputeVertexPosition()
@@ -68,7 +78,19 @@ vec4 ComputeVertexPosition()
   mediump vec2 vPosition = aPosition * visualSize;
 #endif
 
-  vTexCoord = pixelArea.xy + pixelArea.zw * (vPosition.xy / max(vec2(1.0), visualSize) + vec2(0.5));
+
+vec4 finalPixelArea = pixelArea;
+#if IS_REQUIRED_ALPHA_MASKING
+  finalPixelArea = mix(pixelArea,
+                       vec4(
+                            vec2(0.5) + (pixelArea.xy - vec2(0.5)) * maskTextureRatio,
+                            pixelArea.zw * maskTextureRatio
+                       ),
+                       cropToMask);
+  vMaskTexCoord = pixelArea.xy + pixelArea.zw * (vPosition.xy / max(vec2(1.0), visualSize) + vec2(0.5));
+#endif
+  vTexCoord = finalPixelArea.xy + finalPixelArea.zw * (vPosition.xy / max(vec2(1.0), visualSize) + vec2(0.5));
+
   return vec4(vPosition + anchorPoint * visualSize + (visualOffset + origin) * uSize.xy, 0.0, 1.0);
 }
 
index e830602..5025862 100644 (file)
@@ -81,6 +81,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;
 
+const char* const MASK_TEXTURE_RATIO_NAME("maskTextureRatio");
+
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
 #endif
@@ -244,23 +246,24 @@ AnimatedImageVisual::~AnimatedImageVisual()
 
 void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize)
 {
+  naturalSize = Vector2::ZERO;
   if(mImageSize.GetWidth() == 0 && mImageSize.GetHeight() == 0)
   {
-    if(mImageUrl.IsValid())
+    if(mImpl->mRenderer)
     {
-      if(mAnimatedImageLoading.HasLoadingSucceeded())
+      auto textureSet = mImpl->mRenderer.GetTextures();
+      if(textureSet)
       {
-        mImageSize = mAnimatedImageLoading.GetImageSize();
-      }
-      else
-      {
-        mImageSize = Dali::GetClosestImageSize(mImageUrl.GetUrl());
+        auto texture = textureSet.GetTexture(0);
+        if(texture)
+        {
+          SetImageSize(textureSet);
+          naturalSize.x = texture.GetWidth();
+          naturalSize.y = texture.GetHeight();
+          return;
+        }
       }
     }
-    else if(mImageUrls && mImageUrls->size() > 0)
-    {
-      mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl);
-    }
 
     if(mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid() &&
        mMaskingData->mCropToMask)
@@ -268,11 +271,28 @@ void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize)
       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())
+    {
+      if(mAnimatedImageLoading.HasLoadingSucceeded())
+      {
+        mImageSize = mAnimatedImageLoading.GetImageSize();
+      }
+      else
+      {
+        mImageSize = Dali::GetClosestImageSize(mImageUrl.GetUrl());
+      }
+    }
+    else if(mImageUrls && mImageUrls->size() > 0)
+    {
+      mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl);
+    }
   }
 
   naturalSize.width  = mImageSize.GetWidth();
@@ -321,6 +341,7 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const
     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::PREAPPLIED_MASK, mMaskingData->mPreappliedMasking);
   }
 
   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
@@ -450,6 +471,10 @@ void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
       {
         DoSetProperty(Toolkit::ImageVisual::Property::CROP_TO_MASK, keyValue.second);
       }
+      else if(keyValue.first == PREAPPLIED_MASK_NAME)
+      {
+        DoSetProperty(Toolkit::DevelImageVisual::Property::PREAPPLIED_MASK, keyValue.second);
+      }
       else if(keyValue.first == LOAD_POLICY_NAME)
       {
         DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
@@ -624,6 +649,17 @@ void AnimatedImageVisual::DoSetProperty(Property::Index        index,
       break;
     }
 
+    case Toolkit::DevelImageVisual::Property::PREAPPLIED_MASK:
+    {
+      bool preappliedMask = true;
+      if(value.Get(preappliedMask))
+      {
+        AllocateMaskData();
+        mMaskingData->mPreappliedMasking = preappliedMask;
+      }
+      break;
+    }
+
     case Toolkit::ImageVisual::Property::RELEASE_POLICY:
     {
       int releasePolicy = 0;
@@ -730,6 +766,11 @@ void AnimatedImageVisual::OnInitialize()
   {
     mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
   }
+
+  if(mMaskingData)
+  {
+    mImpl->mRenderer.RegisterProperty(CROP_TO_MASK_NAME, static_cast<float>(mMaskingData->mCropToMask));
+  }
 }
 
 void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval)
@@ -792,6 +833,22 @@ void AnimatedImageVisual::SetImageSize(TextureSet& textureSet)
       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);
+      }
+    }
   }
 }
 
@@ -923,15 +980,16 @@ TextureSet AnimatedImageVisual::SetLoadingFailed()
 
 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   requiredAlphaMasking = (mMaskingData) ? !mMaskingData->mPreappliedMasking : false;
   Shader shader;
   shader = mImageVisualShaderFactory.GetShader(
     mFactoryCache,
     ImageVisualShaderFeature::FeatureBuilder()
-    .ApplyDefaultTextureWrapMode(defaultWrapMode)
-    .EnableRoundedCorner(IsRoundedCornerRequired())
-    .EnableBorderline(IsBorderlineRequired())
-  );
+      .ApplyDefaultTextureWrapMode(defaultWrapMode)
+      .EnableRoundedCorner(IsRoundedCornerRequired())
+      .EnableBorderline(IsBorderlineRequired())
+      .EnableAlphaMasking(requiredAlphaMasking));
   return shader;
 }
 
index f5753fa..8e7e304 100644 (file)
@@ -43,6 +43,28 @@ static std::string gFragmentShaderNoAtlas;
 
 const int NATIVE_SHADER_TYPE_OFFSET = VisualFactoryCache::ShaderType::NATIVE_IMAGE_SHADER - VisualFactoryCache::ShaderType::IMAGE_SHADER;
 
+// enum of required list when we select shader
+enum class ImageVisualRequireFlag : uint32_t
+{
+  DEFAULT         = 0,
+  ROUNDED_CORNER  = 1 << 0,
+  BORDERLINE      = 1 << 1,
+  ALPHA_MASKING = 1 << 2,
+};
+
+static constexpr auto SHADER_TYPE_COUNT = 8u;
+VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[SHADER_TYPE_COUNT] =
+{
+  VisualFactoryCache::IMAGE_SHADER,
+  VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER,
+  VisualFactoryCache::IMAGE_SHADER_BORDERLINE,
+  VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE,
+  VisualFactoryCache::IMAGE_SHADER_MASKING,
+  VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_MASKING,
+  VisualFactoryCache::IMAGE_SHADER_BORDERLINE_MASKING,
+  VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING
+};
+
 } // unnamed namespace
 
 namespace ImageVisualShaderFeature
@@ -72,6 +94,11 @@ FeatureBuilder& FeatureBuilder::SetTextureForFragmentShaderCheck(const Dali::Tex
   mTexture = texture;
   return *this;
 }
+FeatureBuilder& FeatureBuilder::EnableAlphaMasking(bool enableAlphaMasking)
+{
+  mAlphaMasking = (enableAlphaMasking ? AlphaMasking::ENABLED : AlphaMasking::DISABLED);
+  return *this;
+}
 } // namespace ImageVisualShaderFeature
 
 ImageVisualShaderFactory::ImageVisualShaderFactory()
@@ -92,9 +119,10 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, con
   const auto& defaultTextureWrapping = featureBuilder.mDefaultTextureWrapMode;
   const auto& roundedCorner          = featureBuilder.mRoundedCorner;
   const auto& borderline             = featureBuilder.mBorderline;
+  const auto& alphaMasking           = featureBuilder.mAlphaMasking;
   const auto& changeFragmentShader   = (featureBuilder.mTexture && DevelTexture::IsNative(featureBuilder.mTexture))
-                                       ? ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE
-                                       : ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE;
+                                         ? ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE
+                                         : ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE;
 
   if(atlasing == ImageVisualShaderFeature::TextureAtlas::ENABLED)
   {
@@ -109,24 +137,20 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, con
   }
   else
   {
+    uint32_t shaderTypeFlag = static_cast<uint32_t>(ImageVisualRequireFlag::DEFAULT);
     if(roundedCorner == ImageVisualShaderFeature::RoundedCorner::ENABLED)
     {
-      if(borderline == ImageVisualShaderFeature::Borderline::ENABLED)
-      {
-        shaderType = VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE;
-      }
-      else
-      {
-        shaderType = VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER;
-      }
+      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::ROUNDED_CORNER);
     }
-    else
+    if(borderline == ImageVisualShaderFeature::Borderline::ENABLED)
     {
-      if(borderline == ImageVisualShaderFeature::Borderline::ENABLED)
-      {
-        shaderType = VisualFactoryCache::IMAGE_SHADER_BORDERLINE;
-      }
+      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::BORDERLINE);
+    }
+    if(alphaMasking == ImageVisualShaderFeature::AlphaMasking::ENABLED)
+    {
+      shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::ALPHA_MASKING);
     }
+    shaderType = SHADER_TYPE_TABLE[shaderTypeFlag];
   }
 
   if(changeFragmentShader == ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE &&
@@ -164,6 +188,11 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, con
         vertexShaderPrefixList   += "#define IS_REQUIRED_BORDERLINE 1\n";
         fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE 1\n";
       }
+      if(alphaMasking == ImageVisualShaderFeature::AlphaMasking::ENABLED)
+      {
+        vertexShaderPrefixList   += "#define IS_REQUIRED_ALPHA_MASKING 1\n";
+        fragmentShaderPrefixList += "#define IS_REQUIRED_ALPHA_MASKING 1\n";
+      }
     }
 
     std::string vertexShader   = std::string(Dali::Shader::GetVertexShaderPrefix()   + vertexShaderPrefixList   + SHADER_IMAGE_VISUAL_SHADER_VERT.data());
index 1459614..1e1974d 100644 (file)
@@ -29,7 +29,6 @@ namespace Toolkit
 {
 namespace Internal
 {
-
 /**
  * ImageVisualShaderFeature contains feature lists what image visual shader need to know.
  */
@@ -96,6 +95,18 @@ enum Type
 };
 } // namespace ChangeFragmentShader
 
+namespace AlphaMasking
+{
+/**
+ * @brief Whether use runtime alpha masking in shader, or not
+ */
+enum Type
+{
+  DISABLED = 0, ///< Image visual doesn't use runtime alpha masking
+  ENABLED       ///< Image visual uses runtime alpha masking
+};
+} // namespace AlphaMasking
+
 /**
  * @brief Collection of current image visual feature. Only use for ImageVisualShaderFactory::GetShader()
  */
@@ -106,6 +117,7 @@ struct FeatureBuilder
     mDefaultTextureWrapMode(DefaultTextureWrapMode::APPLY),
     mRoundedCorner(RoundedCorner::DISABLED),
     mBorderline(Borderline::DISABLED),
+    mAlphaMasking(AlphaMasking::DISABLED),
     mTexture()
   {
   }
@@ -115,15 +127,17 @@ struct FeatureBuilder
   FeatureBuilder& EnableRoundedCorner(bool enableRoundedCorner);
   FeatureBuilder& EnableBorderline(bool enableBorderline);
   FeatureBuilder& SetTextureForFragmentShaderCheck(const Dali::Texture& texture);
+  FeatureBuilder& EnableAlphaMasking(bool enableAlphaMasking);
 
   TextureAtlas::Type           mTextureAtlas : 2;           ///< Whether use texture with atlas, or not. default as TextureAtlas::DISABLED
   DefaultTextureWrapMode::Type mDefaultTextureWrapMode : 2; ///< Whether apply to texture wraping in default, or not. default as DefaultTextureWrapMode::APPLY
   RoundedCorner::Type          mRoundedCorner : 2;          ///< Whether use rounded corner, or not. default as RoundedCorner::DISABLED
   Borderline::Type             mBorderline : 2;             ///< Whether use borderline, or not. default as Borderline::DISABLED
+  AlphaMasking::Type           mAlphaMasking : 2;           ///< Whether use runtime alpha masking, or not. default as AlphaMasking::DISABLED
   Dali::Texture                mTexture;                    ///< Texture to check whether we need to change fragment shader or not
 };
 
-} // namespace ImageVisualShaderFactoryFeature
+} // namespace ImageVisualShaderFeature
 
 /**
  * ImageVisualShaderFactory is an object that provides and shares shaders between image visuals
@@ -131,7 +145,6 @@ struct FeatureBuilder
 class ImageVisualShaderFactory
 {
 public:
-
   /**
    * @brief Constructor
    */
@@ -174,7 +187,6 @@ protected:
   ImageVisualShaderFactory& operator=(const ImageVisualShaderFactory& rhs);
 
 private:
-
   /**
    * @brief Cached information whether native image should change fragment shader.
    * Default it is ChangeFragmentShader::UNDECIDED.
index e694c57..9171032 100644 (file)
@@ -117,7 +117,8 @@ TextureManager::MaskingData::MaskingData()
 : mAlphaMaskUrl(),
   mAlphaMaskId(INVALID_TEXTURE_ID),
   mContentScaleFactor(1.0f),
-  mCropToMask(true)
+  mCropToMask(true),
+  mPreappliedMasking(true)
 {
 }
 
@@ -169,12 +170,22 @@ TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading a
     }
     else
     {
+      Texture maskTexture;
       if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
       {
         Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true);
         if(maskPixelBuffer)
         {
-          pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask);
+          if(!maskInfo->mPreappliedMasking)
+          {
+            PixelData maskPixelData = Devel::PixelBuffer::Convert(maskPixelBuffer); // takes ownership of buffer
+            Texture   maskTexture   = Texture::New(Dali::TextureType::TEXTURE_2D, maskPixelData.GetPixelFormat(), maskPixelData.GetWidth(), maskPixelData.GetHeight());
+            maskTexture.Upload(maskPixelData);
+          }
+          else
+          {
+            pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask);
+          }
         }
       }
 
@@ -185,6 +196,10 @@ TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading a
         texture.Upload(pixelData);
         textureSet = TextureSet::New();
         textureSet.SetTexture(0u, texture);
+        if(maskTexture)
+        {
+          textureSet.SetTexture(1u, maskTexture);
+        }
       }
     }
   }
@@ -195,7 +210,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading a
     bool      cropToMask         = false;
     if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
     {
-      maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl);
+      maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, maskInfo->mPreappliedMasking ? StorageType::KEEP_PIXEL_BUFFER : StorageType::KEEP_TEXTURE);
       alphaMaskId            = maskInfo->mAlphaMaskId;
       contentScaleFactor     = maskInfo->mContentScaleFactor;
       cropToMask             = maskInfo->mCropToMask;
@@ -206,11 +221,11 @@ TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading a
     textureId        = RequestLoadInternal(animatedImageLoading.GetUrl(),
                                     alphaMaskId,
                                     contentScaleFactor,
+                                    cropToMask,
                                     ImageDimensions(),
                                     FittingMode::SCALE_TO_FILL,
                                     SamplingMode::BOX_THEN_LINEAR,
                                     TextureManager::NO_ATLAS,
-                                    cropToMask,
                                     StorageType::UPLOAD_TO_TEXTURE,
                                     textureObserver,
                                     true,
@@ -275,7 +290,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer(
   }
   else
   {
-    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false);
+    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, false, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false);
   }
 
   return pixelBuffer;
@@ -396,7 +411,7 @@ TextureSet TextureManager::LoadTexture(const VisualUrl&                url,
       }
       else
       {
-        maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl);
+        maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, maskInfo->mPreappliedMasking ? StorageType::KEEP_PIXEL_BUFFER : StorageType::KEEP_TEXTURE);
         textureId              = RequestLoad(url,
                                 maskInfo->mAlphaMaskId,
                                 maskInfo->mContentScaleFactor,
@@ -454,7 +469,7 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureManager::ReloadPolicy    reloadPolicy,
   TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
 {
-  return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, true);
+  return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, false, desiredSize, fittingMode, samplingMode, useAtlas, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, true);
 }
 
 TextureManager::TextureId TextureManager::RequestLoad(
@@ -471,25 +486,25 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureManager::ReloadPolicy    reloadPolicy,
   TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
 {
-  return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, true);
+  return RequestLoadInternal(url, maskTextureId, contentScale, cropToMask, desiredSize, fittingMode, samplingMode, useAtlas, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, true);
 }
 
-TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl)
+TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl, StorageType storageType)
 {
   // Use the normal load procedure to get the alpha mask.
   auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, true);
+  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, false, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, storageType, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, true);
 }
 
 TextureManager::TextureId TextureManager::RequestLoadInternal(
   const VisualUrl&                url,
   TextureId                       maskTextureId,
   float                           contentScale,
+  bool                            cropToMask,
   const ImageDimensions           desiredSize,
   FittingMode::Type               fittingMode,
   Dali::SamplingMode::Type        samplingMode,
   UseAtlas                        useAtlas,
-  bool                            cropToMask,
   StorageType                     storageType,
   TextureUploadObserver*          observer,
   bool                            orientationCorrection,
@@ -507,7 +522,8 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
     textureHash = GenerateHash(url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId);
 
     // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
-    cacheIndex = FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad, (animatedImageLoading) ? true : false);
+    cacheIndex = FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas,
+                                   storageType, maskTextureId, preMultiplyOnLoad, (animatedImageLoading) ? true : false);
   }
 
   TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
@@ -1151,8 +1167,36 @@ void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixe
           }
           else if(maskLoadState == LoadState::LOAD_FINISHED)
           {
-            // Send New Task to Thread
-            ApplyMask(textureInfo, textureInfo.maskTextureId);
+            int32_t maskCacheIndex = GetCacheIndexFromId(textureInfo.maskTextureId);
+            if(maskCacheIndex != INVALID_CACHE_INDEX)
+            {
+              TextureInfo& maskTextureInfo(mTextureInfoContainer[maskCacheIndex]);
+              if(maskTextureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER)
+              {
+                // Send New Task to Thread
+                ApplyMask(textureInfo, textureInfo.maskTextureId);
+              }
+            }
+          }
+          else if(maskLoadState == LoadState::UPLOADED)
+          {
+            int32_t maskCacheIndex = GetCacheIndexFromId(textureInfo.maskTextureId);
+            if(maskCacheIndex != INVALID_CACHE_INDEX)
+            {
+              TextureInfo& maskTextureInfo(mTextureInfoContainer[maskCacheIndex]);
+              if(maskTextureInfo.storageType == StorageType::KEEP_TEXTURE)
+              {
+                // Upload image texture. textureInfo.loadState will be UPLOADED.
+                UploadTexture(textureInfo.pixelBuffer, textureInfo);
+                if(maskTextureInfo.textureSet.GetTextureCount() > 0u)
+                {
+                  Texture maskTexture = maskTextureInfo.textureSet.GetTexture(0u);
+                  textureInfo.textureSet.SetTexture(1u, maskTexture);
+                }
+                // notify mask texture set.
+                NotifyObservers(textureInfo, true);
+              }
+            }
           }
         }
       }
@@ -1171,7 +1215,7 @@ void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixe
       {
         NotifyObservers(textureInfo, true);
       }
-      else
+      else  // for the StorageType::KEEP_PIXEL_BUFFER and StorageType::KEEP_TEXTURE
       {
         // Check if there was another texture waiting for this load to complete
         // (e.g. if this was an image mask, and its load is on a different thread)
@@ -1189,10 +1233,16 @@ void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixe
 
 void TextureManager::CheckForWaitingTexture(TextureInfo& maskTextureInfo)
 {
+  if(maskTextureInfo.loadState == LoadState::LOAD_FINISHED &&
+     maskTextureInfo.storageType == StorageType::KEEP_TEXTURE)
+  {
+    // Upload mask texture. textureInfo.loadState will be UPLOADED.
+    UploadTexture(maskTextureInfo.pixelBuffer, maskTextureInfo);
+  }
+
   // Search the cache, checking if any texture has this texture id as a
   // maskTextureId:
   const unsigned int size = mTextureInfoContainer.size();
-
   for(unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex)
   {
     if(mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
@@ -1202,11 +1252,30 @@ void TextureManager::CheckForWaitingTexture(TextureInfo& maskTextureInfo)
 
       if(maskTextureInfo.loadState == LoadState::LOAD_FINISHED)
       {
-        // Send New Task to Thread
-        ApplyMask(textureInfo, maskTextureInfo.textureId);
+        if(maskTextureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER)
+        {
+          // Send New Task to Thread
+          ApplyMask(textureInfo, maskTextureInfo.textureId);
+        }
+      }
+      else if (maskTextureInfo.loadState == LoadState::UPLOADED)
+      {
+        if(maskTextureInfo.storageType == StorageType::KEEP_TEXTURE)
+        {
+          // Upload image texture. textureInfo.loadState will be UPLOADED.
+          UploadTexture(textureInfo.pixelBuffer, textureInfo);
+          if(maskTextureInfo.textureSet.GetTextureCount() > 0u)
+          {
+            Texture maskTexture = maskTextureInfo.textureSet.GetTexture(0u);
+            textureInfo.textureSet.SetTexture(1u, maskTexture);
+          }
+          // notify mask texture set.
+          NotifyObservers(textureInfo, true);
+        }
       }
       else
       {
+        // for the load fail.
         textureInfo.pixelBuffer.Reset();
         textureInfo.loadState = LoadState::LOAD_FAILED;
         NotifyObservers(textureInfo, false);
@@ -1432,6 +1501,7 @@ int TextureManager::FindCachedTexture(
   const FittingMode::Type           fittingMode,
   const Dali::SamplingMode::Type    samplingMode,
   const bool                        useAtlas,
+  StorageType                       storageType,
   TextureId                         maskTextureId,
   TextureManager::MultiplyOnLoad    preMultiplyOnLoad,
   bool                              isAnimatedImage)
@@ -1453,6 +1523,7 @@ int TextureManager::FindCachedTexture(
          (maskTextureId == textureInfo.maskTextureId) &&
          (size == textureInfo.desiredSize) &&
          (isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
+         (storageType == textureInfo.storageType) &&
          ((size.GetWidth() == 0 && size.GetHeight() == 0) ||
           (fittingMode == textureInfo.fittingMode &&
            samplingMode == textureInfo.samplingMode)))
index 0da2ba2..823fc0d 100644 (file)
@@ -77,6 +77,7 @@ public:
   {
     KEEP_PIXEL_BUFFER,
     RETURN_PIXEL_BUFFER,
+    KEEP_TEXTURE,
     UPLOAD_TO_TEXTURE
   };
 
@@ -133,6 +134,7 @@ public:
     TextureManager::TextureId mAlphaMaskId;
     float                     mContentScaleFactor;
     bool                      mCropToMask;
+    bool                      mPreappliedMasking;
   };
   using MaskingDataPointer = std::unique_ptr<MaskingData>;
 
@@ -360,7 +362,7 @@ public:
    * Requests a masking image to be loaded. This mask is not uploaded to GL,
    * instead, it is stored in CPU memory, and can be used for CPU blending.
    */
-  TextureId RequestMaskLoad(const VisualUrl& maskUrl);
+  TextureId RequestMaskLoad(const VisualUrl& maskUrl, StorageType storageType);
 
   /**
    * @brief Remove a Texture from the TextureManager.
@@ -480,14 +482,14 @@ private:
    * @param[in] maskTextureId         The texture id of an image to use as a mask. If no mask is required, then set
    *                                  to INVALID_TEXTURE_ID
    * @param[in] contentScale          The scaling factor to apply to the content when masking
+   * @param[in] cropToMask            Whether to crop the target after masking, or scale the mask to the image before
+   *                                  masking.
    * @param[in] desiredSize           The size the image is likely to appear at. This can be set to 0,0 for automatic
    * @param[in] fittingMode           The FittingMode to use
    * @param[in] samplingMode          The SamplingMode to use
    * @param[in] useAtlasing           Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be
    *                                  loaded, and marked successful, but "useAtlasing" will be set to false in the
    *                                  "UploadCompleted" callback from the TextureManagerUploadObserver.
-   * @param[in] cropToMask            Whether to crop the target after masking, or scale the mask to the image before
-   *                                  masking.
    * @param[in] storageType,          Whether the pixel data is stored in the cache or uploaded to the GPU
    * @param[in] observer              The client object should inherit from this and provide the "UploadCompleted"
    *                                  virtual.
@@ -504,11 +506,11 @@ private:
     const VisualUrl&             url,
     TextureId                    maskTextureId,
     float                        contentScale,
+    bool                         cropToMask,
     const ImageDimensions        desiredSize,
     FittingMode::Type            fittingMode,
     Dali::SamplingMode::Type     samplingMode,
     UseAtlas                     useAtlas,
-    bool                         cropToMask,
     StorageType                  storageType,
     TextureUploadObserver*       observer,
     bool                         orientationCorrection,
@@ -516,7 +518,7 @@ private:
     MultiplyOnLoad&              preMultiplyOnLoad,
     Dali::AnimatedImageLoading   animatedImageLoading,
     uint32_t                     frameIndex,
-    bool useCache);
+    bool                         useCache);
 
   /**
    * @brief Get the current state of a texture
@@ -793,6 +795,7 @@ private:
    * @param[in] fittingMode       The FittingMode to use
    * @param[in] samplingMode      The SamplingMode to use
    * @param[in] useAtlas          True if atlased
+   * @param[in] storageType,      Whether the pixel data is stored in the cache or uploaded to the GPU
    * @param[in] maskTextureId     Optional texture ID to use to mask this image
    * @param[in] preMultiplyOnLoad If the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha.
    * @param[in] isAnimatedImage   True if the texture is from animated image.
@@ -805,6 +808,7 @@ private:
     const FittingMode::Type           fittingMode,
     const Dali::SamplingMode::Type    samplingMode,
     const bool                        useAtlas,
+    StorageType                       storageType,
     TextureId                         maskTextureId,
     MultiplyOnLoad                    preMultiplyOnLoad,
     bool                              isAnimatedImage);
index deae0b0..3dbbb6f 100644 (file)
@@ -84,12 +84,20 @@ public:
     IMAGE_SHADER_ROUNDED_CORNER,
     IMAGE_SHADER_BORDERLINE,
     IMAGE_SHADER_ROUNDED_BORDERLINE,
+    IMAGE_SHADER_MASKING,
+    IMAGE_SHADER_ROUNDED_CORNER_MASKING,
+    IMAGE_SHADER_BORDERLINE_MASKING,
+    IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING,
     IMAGE_SHADER_ATLAS_DEFAULT_WRAP,
     IMAGE_SHADER_ATLAS_CUSTOM_WRAP,
     NATIVE_IMAGE_SHADER,
     NATIVE_IMAGE_SHADER_ROUNDED_CORNER,
     NATIVE_IMAGE_SHADER_BORDERLINE,
     NATIVE_IMAGE_SHADER_ROUNDED_BORDERLINE,
+    NATIVE_IMAGE_SHADER_MASKING,
+    NATIVE_IMAGE_SHADER_ROUNDED_CORNER_MASKING,
+    NATIVE_IMAGE_SHADER_BORDERLINE_MASKING,
+    NATIVE_IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING,
     NINE_PATCH_SHADER,
     NINE_PATCH_MASK_SHADER,
     TEXT_SHADER_MULTI_COLOR_TEXT,
index 53bcd1c..579ad4a 100644 (file)
@@ -122,6 +122,7 @@ const char* const IMAGE_DESIRED_WIDTH("desiredWidth");
 const char* const IMAGE_DESIRED_HEIGHT("desiredHeight");
 const char* const ALPHA_MASK_URL("alphaMaskUrl");
 const char* const REDRAW_IN_SCALING_DOWN_NAME("redrawInScalingDown");
+const char* const PREAPPLIED_MASK_NAME("preappliedMask");
 
 // Text visual
 const char* const TEXT_PROPERTY("text");
index 470ded9..da65661 100644 (file)
@@ -106,6 +106,7 @@ extern const char* const IMAGE_DESIRED_WIDTH;
 extern const char* const IMAGE_DESIRED_HEIGHT;
 extern const char* const ALPHA_MASK_URL;
 extern const char* const REDRAW_IN_SCALING_DOWN_NAME;
+extern const char* const PREAPPLIED_MASK_NAME;
 
 // Text visual
 extern const char* const TEXT_PROPERTY;