Enable masking for Scene3D::SceneView 23/301423/10
authorseungho baek <sbsh.baek@samsung.com>
Wed, 25 Oct 2023 05:45:17 +0000 (14:45 +0900)
committerseungho baek <sbsh.baek@samsung.com>
Fri, 1 Dec 2023 06:47:08 +0000 (15:47 +0900)
Change-Id: Ia725f8a19a96d6020e1247cc49b9568a5f562215
Signed-off-by: seungho baek <sbsh.baek@samsung.com>
automated-tests/src/dali-scene3d/utc-Dali-SceneView.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp
dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp
dali-scene3d/internal/controls/scene-view/scene-view-impl.h
dali-scene3d/public-api/controls/scene-view/scene-view.h
dali-toolkit/internal/graphics/shaders/image-visual-shader.frag
dali-toolkit/internal/visuals/image-visual-shader-factory.cpp
dali-toolkit/internal/visuals/image-visual-shader-feature-builder.cpp
dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h
dali-toolkit/internal/visuals/image/image-visual.cpp

index ca87d38..aaf05f6 100644 (file)
@@ -1033,3 +1033,39 @@ int UtcDaliSceneViewColorMode(void)
 
   END_TEST;
 }
+
+namespace
+{
+const char* TEST_MASK_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/mask.png";
+
+static constexpr std::string_view Y_FLIP_MASK_TEXTURE = "uYFlipMaskTexture";
+} // namespace
+
+int UtcDaliSceneViewMasking(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  application.GetScene().Add(view);
+
+  DALI_TEST_EQUALS(view.GetProperty<std::string>(Dali::Scene3D::SceneView::Property::ALPHA_MASK_URL), "", TEST_LOCATION);
+  DALI_TEST_EQUALS(view.GetProperty<bool>(Dali::Scene3D::SceneView::Property::CROP_TO_MASK), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(view.GetProperty<float>(Dali::Scene3D::SceneView::Property::MASK_CONTENT_SCALE), 1.0f, TEST_LOCATION);
+
+  auto yFlipMaskTextureIndex = view.GetPropertyIndex(Y_FLIP_MASK_TEXTURE.data());
+  DALI_TEST_EQUALS(yFlipMaskTextureIndex, Property::INVALID_INDEX, TEST_LOCATION);
+
+  view.UseFramebuffer(true);
+  view.SetProperty(Dali::Scene3D::SceneView::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME);
+  view.SetProperty(Dali::Scene3D::SceneView::Property::CROP_TO_MASK, false);
+  view.SetProperty(Dali::Scene3D::SceneView::Property::MASK_CONTENT_SCALE, 0.5f);
+
+  DALI_TEST_EQUALS(view.GetProperty<std::string>(Dali::Scene3D::SceneView::Property::ALPHA_MASK_URL), TEST_MASK_IMAGE_FILE_NAME, TEST_LOCATION);
+  DALI_TEST_EQUALS(view.GetProperty<bool>(Dali::Scene3D::SceneView::Property::CROP_TO_MASK), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(view.GetProperty<float>(Dali::Scene3D::SceneView::Property::MASK_CONTENT_SCALE), 0.5f, TEST_LOCATION);
+
+  yFlipMaskTextureIndex = view.GetPropertyIndex(std::string(Y_FLIP_MASK_TEXTURE));
+  DALI_TEST_EQUALS(view.GetProperty<float>(yFlipMaskTextureIndex), 1.0f, TEST_LOCATION);
+
+  END_TEST;
+}
index 7b07ffc..f63b5cf 100644 (file)
@@ -2175,6 +2175,7 @@ int UtcDaliImageVisualAlphaMask02(void)
   propertyMap.Insert(ImageVisual::Property::URL, TEST_LARGE_IMAGE_FILE_NAME);
   propertyMap.Insert(ImageVisual::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME);
   propertyMap.Insert(DevelImageVisual::Property::MASKING_TYPE, DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
+  propertyMap.Insert(DevelImageVisual::Property::CROP_TO_MASK, false);
 
   Visual::Base visual = factory.CreateVisual(propertyMap);
   DALI_TEST_CHECK(visual);
@@ -2220,6 +2221,10 @@ int UtcDaliImageVisualAlphaMask02(void)
   dummyImpl.UnregisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1);
   DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
 
+  auto index = renderer.GetPropertyIndex("maskTextureRatio");
+  DALI_TEST_NOT_EQUALS(index, Property::INVALID_INDEX, 0.1f, TEST_LOCATION);
+  DALI_TEST_EQUALS(renderer.GetProperty<Vector2>(index), Vector2::ONE, TEST_LOCATION);
+
   END_TEST;
 }
 
index 75c9b51..7e59d6a 100644 (file)
@@ -60,6 +60,10 @@ BaseHandle Create()
 
 // Setup properties, signals and actions using the type-registry.
 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::SceneView, Toolkit::Control, Create);
+
+DALI_PROPERTY_REGISTRATION(Scene3D, SceneView, "AlphaMaskUrl", STRING, ALPHA_MASK_URL)
+DALI_PROPERTY_REGISTRATION(Scene3D, SceneView, "MaskContentScale", FLOAT, MASK_CONTENT_SCALE)
+DALI_PROPERTY_REGISTRATION(Scene3D, SceneView, "CropToMask", BOOLEAN, CROP_TO_MASK)
 DALI_TYPE_REGISTRATION_END()
 
 Property::Index    RENDERING_BUFFER        = Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX + 1;
@@ -68,6 +72,8 @@ constexpr int32_t  INVALID_INDEX           = -1;
 constexpr uint32_t MAXIMUM_SIZE_SHADOW_MAP = 2048;
 
 static constexpr std::string_view SKYBOX_INTENSITY_STRING = "uIntensity";
+static constexpr std::string_view Y_FLIP_MASK_TEXTURE = "uYFlipMaskTexture";
+static constexpr float FLIP_MASK_TEXTURE = 1.0f;
 
 Dali::Actor CreateSkybox()
 {
@@ -789,6 +795,113 @@ void SceneView::UpdateShadowUniform(Scene3D::Light light)
   mShaderManager->UpdateShadowUniform(light);
 }
 
+void SceneView::SetAlphaMaskUrl(std::string& alphaMaskUrl)
+{
+  if(mAlphaMaskUrl != alphaMaskUrl)
+  {
+    mAlphaMaskUrl = alphaMaskUrl;
+    mMaskingPropertyChanged = true;
+    UpdateRenderTask();
+  }
+}
+
+std::string SceneView::GetAlphaMaskUrl()
+{
+  return mAlphaMaskUrl;
+}
+
+void SceneView::SetMaskContentScaleFactor(float maskContentScaleFactor)
+{
+  if(mMaskContentScaleFactor != maskContentScaleFactor)
+  {
+    mMaskContentScaleFactor = maskContentScaleFactor;
+    mMaskingPropertyChanged = true;
+    UpdateRenderTask();
+  }
+}
+
+float SceneView::GetMaskContentScaleFactor()
+{
+  return mMaskContentScaleFactor;
+}
+
+void SceneView::EnableCropToMask(bool enableCropToMask)
+{
+  if(mCropToMask != enableCropToMask)
+  {
+    mCropToMask = enableCropToMask;
+    mMaskingPropertyChanged = true;
+    UpdateRenderTask();
+  }
+}
+
+bool SceneView::IsEnabledCropToMask()
+{
+  return mCropToMask;
+}
+
+void SceneView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
+{
+  Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(Dali::BaseHandle(object));
+
+  if(sceneView)
+  {
+    SceneView& sceneViewImpl(GetImpl(sceneView));
+
+    switch(index)
+    {
+      case Scene3D::SceneView::Property::ALPHA_MASK_URL:
+      {
+        std::string alphaMaskUrl = value.Get<std::string>();
+        sceneViewImpl.SetAlphaMaskUrl(alphaMaskUrl);
+        break;
+      }
+      case Scene3D::SceneView::Property::MASK_CONTENT_SCALE:
+      {
+        sceneViewImpl.SetMaskContentScaleFactor(value.Get<float>());
+        break;
+      }
+      case Scene3D::SceneView::Property::CROP_TO_MASK:
+      {
+        sceneViewImpl.EnableCropToMask(value.Get<bool>());
+        break;
+      }
+    }
+  }
+}
+
+Property::Value SceneView::GetProperty(BaseObject* object, Property::Index index)
+{
+  Property::Value value;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(Dali::BaseHandle(object));
+
+  if(sceneView)
+  {
+    SceneView& sceneViewImpl(GetImpl(sceneView));
+
+    switch(index)
+    {
+      case Scene3D::SceneView::Property::ALPHA_MASK_URL:
+      {
+        value = sceneViewImpl.GetAlphaMaskUrl();
+        break;
+      }
+      case Scene3D::SceneView::Property::MASK_CONTENT_SCALE:
+      {
+        value = sceneViewImpl.GetMaskContentScaleFactor();
+        break;
+      }
+      case Scene3D::SceneView::Property::CROP_TO_MASK:
+      {
+        value = sceneViewImpl.IsEnabledCropToMask();
+        break;
+      }
+    }
+  }
+  return value;
+}
+
 ///////////////////////////////////////////////////////////
 //
 // Private methods
@@ -978,7 +1091,8 @@ void SceneView::UpdateRenderTask()
       Dali::FrameBuffer currentFrameBuffer = mRenderTask.GetFrameBuffer();
       if(!currentFrameBuffer ||
          !Dali::Equals(currentFrameBuffer.GetColorTexture().GetWidth(), size.width) ||
-         !Dali::Equals(currentFrameBuffer.GetColorTexture().GetHeight(), size.height))
+         !Dali::Equals(currentFrameBuffer.GetColorTexture().GetHeight(), size.height) ||
+         mMaskingPropertyChanged)
       {
         mRootLayer.SetProperty(Dali::Actor::Property::COLOR_MODE, ColorMode::USE_OWN_COLOR);
         mRenderTask.ResetViewportGuideActor();
@@ -996,8 +1110,18 @@ void SceneView::UpdateRenderTask()
         imagePropertyMap.Insert(Toolkit::ImageVisual::Property::URL, imageUrl.GetUrl());
         // To flip rendered scene without CameraActor::SetInvertYAxis() to avoid backface culling.
         imagePropertyMap.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, Vector4(0.0f, 1.0f, 1.0f, -1.0f));
-        mVisual = Toolkit::VisualFactory::Get().CreateVisual(imagePropertyMap);
+        if(!mAlphaMaskUrl.empty())
+        {
+          imagePropertyMap.Insert(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mAlphaMaskUrl);
+          imagePropertyMap.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+          imagePropertyMap.Insert(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskContentScaleFactor);
+          imagePropertyMap.Insert(Toolkit::ImageVisual::Property::CROP_TO_MASK, mCropToMask);
+          imagePropertyMap.Insert(Toolkit::DevelImageVisual::Property::MASKING_TYPE, Toolkit::DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
+          Self().RegisterProperty(Y_FLIP_MASK_TEXTURE, FLIP_MASK_TEXTURE);
+        }
+        mMaskingPropertyChanged = false;
 
+        mVisual = Toolkit::VisualFactory::Get().CreateVisual(imagePropertyMap);
         Toolkit::DevelControl::RegisterVisual(*this, RENDERING_BUFFER, mVisual);
 
         mRenderTask.SetFrameBuffer(mFrameBuffer);
index 79c3705..ce9641a 100644 (file)
@@ -227,11 +227,65 @@ public:
   Dali::Scene3D::Loader::ShaderManagerPtr GetShaderManager() const;
 
   /**
-   * @brief Update shader uniforms about shadow.
+   * @brief Updates shader uniforms about shadow.
    * @param[in] light Light that makes shadow.
    */
   void UpdateShadowUniform(Scene3D::Light light);
 
+  /**
+   * @brief Sets alpha mask url
+   * @param[in] alphaMaskUrl Url for alpha mask.
+   */
+  void SetAlphaMaskUrl(std::string& alphaMaskUrl);
+
+  /**
+   * @brief Retrieves alpha mask url
+   * @return Alpha mask url.
+   */
+  std::string GetAlphaMaskUrl();
+
+  /**
+   * @brief Sets mask content scale factor
+   * @param[in] maskContentScaleFactor Scale factor for mask content.
+   */
+  void SetMaskContentScaleFactor(float maskContentScaleFactor);
+
+  /**
+   * @brief Retrieves mask content scale factor
+   * @return Scale factor for mask content.
+   */
+  float GetMaskContentScaleFactor();
+
+  /**
+   * @brief Sets whether the rendered result will be crop to mask or not.
+   * @param[in] enableCropToMask True for crop rendered result to mask.
+   */
+  void EnableCropToMask(bool enableCropToMask);
+
+  /**
+   * @brief Retrieves whether the crop to mask is enabled or not.
+   * @return True when rendered result is cropped to mask.
+   */
+  bool IsEnabledCropToMask();
+
+  // Properties
+
+  /**
+   * Called when a property of an object of this type is set.
+   * @param[in] object The object whose property is set.
+   * @param[in] index The property index.
+   * @param[in] value The new property value.
+   */
+  static void SetProperty(BaseObject* object, Property::Index index, const Property::Value& value);
+
+  /**
+   * Called to retrieve a property of an object of this type.
+   * @param[in] object The object whose property is to be retrieved.
+   * @param[in] index The property index.
+   * @return The current value of the property.
+   */
+  static Property::Value GetProperty(BaseObject* object, Property::Index index);
+
 protected:
   /**
    * @brief Constructs a new SceneView.
@@ -371,6 +425,12 @@ private:
   float                                          mSkyboxIntensity{1.0f};
   uint8_t                                        mFrameBufferMultiSamplingLevel{0u};
 
+  // Masking
+  std::string mAlphaMaskUrl;
+  float       mMaskContentScaleFactor{1.0f};
+  bool        mCropToMask{true};
+  bool        mMaskingPropertyChanged{false};
+
   // Shader Factory
   Dali::Scene3D::Loader::ShaderManagerPtr mShaderManager;
 
index 1f4ec52..41b9e17 100644 (file)
@@ -105,6 +105,50 @@ class DALI_SCENE3D_API SceneView : public Dali::Toolkit::Control
 {
 public:
   /**
+   * @brief The start and end property ranges for this control.
+   * @SINCE_2_3.1
+   */
+  enum PropertyRange
+  {
+    PROPERTY_START_INDEX = Control::CONTROL_PROPERTY_END_INDEX + 1,
+    PROPERTY_END_INDEX   = PROPERTY_START_INDEX + 1000
+  };
+
+  struct Property
+  {
+    enum
+    {
+      /**
+       * @brief URL of a masking image
+       * @details Name "alphaMaskUrl", type Property::STRING, URL of image to apply as
+       * a mask after SceneView is drawn.
+       * @note Alpha masking is only available when framebuffer is used.
+       * @note Optional.
+       */
+      ALPHA_MASK_URL = PROPERTY_START_INDEX,
+
+      /**
+       * @brief The scale factor to apply to the content image before masking
+       * @details Name "maskContentScale", type Property::FLOAT, The scale factor
+       * to apply to the content before masking. Note, scaled result is cropped to
+       * the same size as the alpha mask.
+       * @note Optional.
+       */
+      MASK_CONTENT_SCALE,
+
+      /**
+       * @brief Whether to crop rendered result to mask or scale mask to fit result
+       * @details Name "cropToMask", type Property::BOOLEAN, True if the rendered result should
+       * be cropped to match the mask size, or false if the result should remain the same size.
+       * @note Optional, Default true
+       * @note If this is false, then the mask is scaled to fit the rendered result before being applied.
+       */
+      CROP_TO_MASK,
+    };
+  };
+
+public:
+  /**
    * @brief Create an initialized SceneView.
    *
    * @SINCE_2_1.38
index 98a7347..f13f865 100644 (file)
@@ -17,6 +17,7 @@ uniform sampler2D sTextureV;
 
 #ifdef IS_REQUIRED_ALPHA_MASKING
 uniform sampler2D sMaskTexture;
+uniform lowp float uYFlipMaskTexture;
 INPUT mediump vec2 vMaskTexCoord;
 #endif
 
@@ -255,7 +256,9 @@ void main()
 #endif
 
 #ifdef IS_REQUIRED_ALPHA_MASKING
-  mediump float maskAlpha = TEXTURE(sMaskTexture, vMaskTexCoord).a;
+  mediump vec2 maskTexCoord = vMaskTexCoord;
+  maskTexCoord.y = mix(maskTexCoord.y, 1.0-maskTexCoord.y, uYFlipMaskTexture);
+  mediump float maskAlpha = TEXTURE(sMaskTexture, maskTexCoord).a;
   textureColor.a *= maskAlpha;
   textureColor.rgb *= mix(1.0, maskAlpha, preMultipliedAlpha);
 #endif
index c193fe7..7bbe186 100644 (file)
@@ -35,8 +35,9 @@ namespace
 {
 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
 
-const int NATIVE_SHADER_TYPE_OFFSET = VisualFactoryCache::ShaderType::NATIVE_IMAGE_SHADER - VisualFactoryCache::ShaderType::IMAGE_SHADER;
-
+const int                         NATIVE_SHADER_TYPE_OFFSET = VisualFactoryCache::ShaderType::NATIVE_IMAGE_SHADER - VisualFactoryCache::ShaderType::IMAGE_SHADER;
+static constexpr std::string_view Y_FLIP_MASK_TEXTURE       = "uYFlipMaskTexture";
+static constexpr float            NOT_FLIP_MASK_TEXTURE     = 0.0f;
 } // unnamed namespace
 
 static constexpr auto          SHADER_TYPE_COUNT = 6u;
@@ -121,6 +122,10 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, Ima
 
   shader = Shader::New(vertexShader, fragmentShader);
   shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
+  if(featureBuilder.IsEnabledAlphaMaskingOnRendering())
+  {
+    shader.RegisterProperty(Y_FLIP_MASK_TEXTURE, NOT_FLIP_MASK_TEXTURE);
+  }
   factoryCache.SaveShader(shaderType, shader);
 
   return shader;
index 9b3eb8c..6939cd1 100644 (file)
@@ -224,6 +224,11 @@ Dali::Texture ImageVisualShaderFeatureBuilder::GetTexture()
   return mTexture;
 }
 
+bool ImageVisualShaderFeatureBuilder::IsEnabledAlphaMaskingOnRendering() const
+{
+  return mAlphaMaskingOnRendering == ImageVisualShaderFeature::AlphaMaskingOnRendering::ENABLED;
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index d757f70..0f1eccb 100644 (file)
@@ -154,6 +154,8 @@ public:
 
   Dali::Texture GetTexture();
 
+  bool IsEnabledAlphaMaskingOnRendering() const;
+
 private:
   ImageVisualShaderFeature::TextureAtlas::Type            mTextureAtlas : 2;            ///< Whether use texture with atlas, or not. default as TextureAtlas::DISABLED
   ImageVisualShaderFeature::DefaultTextureWrapMode::Type  mDefaultTextureWrapMode : 2;  ///< Whether apply to texture wraping in default, or not. default as DefaultTextureWrapMode::APPLY
index ff8d961..9e18e36 100644 (file)
@@ -1323,6 +1323,10 @@ void ImageVisual::CheckMaskTexture()
       {
         mImpl->mRenderer.RegisterProperty(MASK_TEXTURE_RATIO_NAME, ComputeMaskTextureRatio());
       }
+      else
+      {
+        mImpl->mRenderer.RegisterProperty(MASK_TEXTURE_RATIO_NAME, Vector2::ONE);
+      }
       maskLoadFailed = false;
     }