Introduce Mask Effect 75/322275/23
authorANZ1217 <chihun.jeong@samsung.com>
Mon, 7 Apr 2025 06:38:07 +0000 (15:38 +0900)
committerANZ1217 <chihun.jeong@samsung.com>
Mon, 14 Apr 2025 06:54:53 +0000 (15:54 +0900)
Change-Id: I18883959c71a80bbcfc7414aba1b2395e532c88e

automated-tests/src/dali-toolkit/utc-Dali-RenderEffect.cpp
dali-toolkit/internal/controls/render-effects/mask-effect-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/render-effects/mask-effect-impl.h [new file with mode: 0644]
dali-toolkit/internal/file.list
dali-toolkit/internal/graphics/shaders/mask-effect.frag [new file with mode: 0644]
dali-toolkit/public-api/controls/render-effects/mask-effect.cpp [new file with mode: 0644]
dali-toolkit/public-api/controls/render-effects/mask-effect.h [new file with mode: 0644]
dali-toolkit/public-api/file.list

index 4346c2f03f45f7656b5cb35d9d8cc8d49b4b9b2c..49655ecfbd9f4611cf213d9ce5207413400bf196 100644 (file)
@@ -19,6 +19,7 @@
 #include <dali-toolkit/devel-api/controls/control-devel.h>
 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
 #include <dali-toolkit/public-api/controls/render-effects/background-blur-effect.h>
+#include <dali-toolkit/public-api/controls/render-effects/mask-effect.h>
 #include <dali/devel-api/adaptor-framework/image-loading.h>
 
 using namespace Dali;
@@ -35,6 +36,20 @@ int UtcDaliRenderEffectNewP(void)
   RenderEffect blurEffect2 = BackgroundBlurEffect::New(0.5f, 10);
   DALI_TEST_CHECK(blurEffect2);
 
+  Control control = Control::New();
+
+  RenderEffect maskEffect1 = MaskEffect::New(control);
+  DALI_TEST_CHECK(maskEffect1);
+
+  RenderEffect maskEffect2 = MaskEffect::New(control, MaskEffect::MaskMode::LUMINANCE, Vector2(0.f, 0.f), Vector2(1.f, 1.f));
+  DALI_TEST_CHECK(maskEffect2);
+
+  MaskEffect maskEffect3 = MaskEffect::New(control);
+  DALI_TEST_CHECK(maskEffect3);
+
+  MaskEffect maskEffect4 = maskEffect3;
+  DALI_TEST_CHECK(maskEffect4);
+
   END_TEST;
 }
 
@@ -83,6 +98,12 @@ int UtcDaliRenderEffectActivateP01(void)
   taskList = scene.GetRenderTaskList();
   DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
 
+  Control newControl = Control::New();
+  childControl.SetRenderEffect(MaskEffect::New(newControl));
+
+  taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(3u, taskList.GetTaskCount(), TEST_LOCATION);
+
   END_TEST;
 }
 
@@ -116,6 +137,37 @@ int UtcDaliRenderEffectActivateP02(void)
   END_TEST;
 }
 
+int UtcDaliRenderEffectActivateP03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliRenderEffectActivateP03");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+  scene.Add(control);
+
+  Control newControl = Control::New();
+  RenderEffect maskEffect = MaskEffect::New(newControl);
+  control.SetRenderEffect(maskEffect);
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(3u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  Control control2 = Control::New();
+  control2.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control2.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+  scene.Add(control2);
+
+  control2.SetRenderEffect(maskEffect);
+  taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(3u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliRenderEffectDeactivateP(void)
 {
   ToolkitTestApplication application;
@@ -140,6 +192,17 @@ int UtcDaliRenderEffectDeactivateP(void)
   DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
   DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
 
+  Control newControl = Control::New();
+  count = control.GetRendererCount();
+  control.SetRenderEffect(MaskEffect::New(newControl));
+
+  taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(3u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  control.ClearRenderEffect();
+  taskList = scene.GetRenderTaskList();
+  DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+
   END_TEST;
 }
 
@@ -845,3 +908,74 @@ int UtcDaliRenderEffectBlurOnce(void)
 
   END_TEST;
 }
+
+int UtcDaliMaskEffect(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliMaskEffect");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+
+  scene.Add(control);
+
+  Control maskControl = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+  // Add render effect during scene on.
+  control.SetRenderEffect(MaskEffect::New(maskControl));
+
+  // send notification.
+  application.SendNotification();
+  application.Render();
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+
+  control.SetProperty(Actor::Property::SIZE, Vector2(3.0f, 3.0f));
+
+  // send notification twice to refresh.
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+
+  // Render effect activated.
+  DALI_TEST_EQUALS(3u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliMaskEffectScaleN(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliMaskEffect");
+
+  Integration::Scene scene = application.GetScene();
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+
+  scene.Add(control);
+
+  Control maskControl = Control::New();
+  control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
+
+  // adjust to epsilon.
+  control.SetRenderEffect(MaskEffect::New(maskControl, MaskEffect::MaskMode::ALPHA, Vector2(0.f, 0.f), Vector2(0.f, 0.f)));
+
+  // send notification.
+  application.SendNotification();
+  application.Render();
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+
+  // Render effect activated.
+  DALI_TEST_EQUALS(3u, taskList.GetTaskCount(), TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
diff --git a/dali-toolkit/internal/controls/render-effects/mask-effect-impl.cpp b/dali-toolkit/internal/controls/render-effects/mask-effect-impl.cpp
new file mode 100644 (file)
index 0000000..6ed4621
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2025 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/controls/render-effects/mask-effect-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/actors/actor-devel.h>
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/actors/custom-actor-impl.h>
+#include <dali/public-api/render-tasks/render-task-list.h>
+#include <dali/public-api/rendering/renderer.h>
+#include <dali/public-api/rendering/shader.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
+#include <dali-toolkit/internal/controls/control/control-renderers.h>
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-toolkit/public-api/controls/control-impl.h>
+
+namespace
+{
+const uint32_t        maskSourceIndex            = 0u;
+const uint32_t        maskTargetIndex            = 1u;
+constexpr const char* UNIFORM_MASK_MODE_NAME     = "uMaskMode";
+constexpr const char* UNIFORM_MASK_POSITION_NAME = "uMaskPosition";
+constexpr const char* UNIFORM_MASK_SCALE_NAME    = "uMaskScale";
+} // namespace
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+#ifdef DEBUG_ENABLED
+extern Debug::Filter* gRenderEffectLogFilter; ///< Define at render-effect-impl.cpp
+#endif
+
+MaskEffectImpl::MaskEffectImpl(Toolkit::Control maskControl)
+: RenderEffectImpl(),
+  mMaskControl(maskControl),
+  mMaskMode(MaskEffect::MaskMode::ALPHA),
+  mMaskPosition(Vector2(0.f, 0.f)),
+  mMaskScale(Vector2(1.f, 1.f))
+{
+}
+
+MaskEffectImpl::MaskEffectImpl(Toolkit::Control maskControl, MaskEffect::MaskMode maskMode, Vector2 maskPosition, Vector2 maskScale)
+: RenderEffectImpl(),
+  mMaskControl(maskControl),
+  mMaskMode(maskMode),
+  mMaskPosition(maskPosition),
+  mMaskScale(maskScale)
+{
+  if(mMaskScale.x < Math::MACHINE_EPSILON_100)
+  {
+    DALI_LOG_DEBUG_INFO("maskScale.x is less or equal to zero. Adjust to epsilon.\n");
+    mMaskScale.x = Math::MACHINE_EPSILON_100;
+  }
+
+  if(mMaskScale.y < Math::MACHINE_EPSILON_100)
+  {
+    DALI_LOG_DEBUG_INFO("maskScale.y is less or equal to zero. Adjust to epsilon.\n");
+    mMaskScale.y = Math::MACHINE_EPSILON_100;
+  }
+}
+
+MaskEffectImpl::~MaskEffectImpl()
+{
+}
+
+MaskEffectImplPtr MaskEffectImpl::New(Toolkit::Control maskControl)
+{
+  MaskEffectImplPtr handle = new MaskEffectImpl(maskControl);
+  handle->Initialize();
+  return handle;
+}
+
+MaskEffectImplPtr MaskEffectImpl::New(Toolkit::Control maskControl, MaskEffect::MaskMode maskMode, Vector2 maskPosition, Vector2 maskScale)
+{
+  MaskEffectImplPtr handle = new MaskEffectImpl(maskControl, maskMode, maskPosition, maskScale);
+  handle->Initialize();
+  return handle;
+}
+
+OffScreenRenderable::Type MaskEffectImpl::GetOffScreenRenderableType()
+{
+  return OffScreenRenderable::FORWARD;
+}
+
+void MaskEffectImpl::GetOffScreenRenderTasks(std::vector<Dali::RenderTask>& tasks, bool isForward)
+{
+  tasks.clear();
+  if(isForward)
+  {
+    if(mMaskTargetRenderTask)
+    {
+      tasks.push_back(mMaskTargetRenderTask);
+    }
+    if(mMaskSourceRenderTask)
+    {
+      tasks.push_back(mMaskSourceRenderTask);
+    }
+  }
+}
+
+void MaskEffectImpl::OnInitialize()
+{
+  // Create CameraActors
+  mCamera = CameraActor::New();
+  mCamera.SetInvertYAxis(true);
+  mCamera.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  mCamera.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+
+  // renderer
+  Renderer maskRenderer = GetTargetRenderer();
+  Shader   shader       = Dali::Shader::New(BASIC_VERTEX_SOURCE, SHADER_MASK_EFFECT_FRAG);
+  maskRenderer.SetShader(shader);
+  maskRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true); // Always use pre-multiply alpha
+}
+
+void MaskEffectImpl::OnActivate()
+{
+  Toolkit::Control ownerControl = GetOwnerControl();
+  DALI_ASSERT_ALWAYS(ownerControl && "Set the owner of RenderEffect before you activate.");
+
+  ownerControl.Add(mCamera);
+  Renderer maskRenderer = GetTargetRenderer();
+  ownerControl.GetImplementation().SetCacheRenderer(maskRenderer);
+  ownerControl.GetImplementation().SetOffScreenRenderableType(OffScreenRenderable::Type::FORWARD);
+
+  Vector2 size = GetTargetSize();
+  mCamera.SetPerspectiveProjection(size);
+
+  CreateFrameBuffers(ImageDimensions(size.x, size.y));
+  CreateRenderTasks(ownerControl);
+  SetShaderConstants(ownerControl);
+
+  mMaskTargetRenderTask.SetScreenToFrameBufferMappingActor(ownerControl);
+
+  TextureSet textureSet = TextureSet::New();
+
+  Texture maskSourceTexture = mMaskSourceFrameBuffer.GetColorTexture();
+  Texture maskTargetTexture = mMaskTargetFrameBuffer.GetColorTexture();
+
+  textureSet.SetTexture(maskSourceIndex, maskSourceTexture);
+  textureSet.SetTexture(maskTargetIndex, maskTargetTexture);
+
+  maskRenderer.SetTextures(textureSet);
+}
+
+void MaskEffectImpl::OnDeactivate()
+{
+  mCamera.Unparent();
+  mMaskTargetFrameBuffer.Reset();
+  mMaskSourceFrameBuffer.Reset();
+
+  auto sceneHolder = GetSceneHolder();
+  if(DALI_LIKELY(sceneHolder))
+  {
+    RenderTaskList taskList = sceneHolder.GetRenderTaskList();
+    taskList.RemoveTask(mMaskTargetRenderTask);
+    mMaskTargetRenderTask.Reset();
+    taskList.RemoveTask(mMaskSourceRenderTask);
+    mMaskSourceRenderTask.Reset();
+  }
+}
+
+void MaskEffectImpl::OnRefresh()
+{
+  mMaskTargetFrameBuffer.Reset();
+  mMaskSourceFrameBuffer.Reset();
+
+  Vector2 size = GetTargetSize();
+  mCamera.SetPerspectiveProjection(size);
+  CreateFrameBuffers(ImageDimensions(size.x, size.y));
+
+  mMaskTargetRenderTask.SetFrameBuffer(mMaskTargetFrameBuffer);
+  mMaskSourceRenderTask.SetFrameBuffer(mMaskSourceFrameBuffer);
+}
+
+void MaskEffectImpl::CreateFrameBuffers(const ImageDimensions size)
+{
+  uint32_t width  = size.GetWidth();
+  uint32_t height = size.GetHeight();
+
+  mMaskTargetFrameBuffer     = FrameBuffer::New(width, height, FrameBuffer::Attachment::DEPTH_STENCIL);
+  Texture mMaskTargetTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, width, height);
+  mMaskTargetFrameBuffer.AttachColorTexture(mMaskTargetTexture);
+
+  mMaskSourceFrameBuffer     = FrameBuffer::New(width, height, FrameBuffer::Attachment::DEPTH_STENCIL);
+  Texture mMaskSourceTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, width, height);
+  mMaskSourceFrameBuffer.AttachColorTexture(mMaskSourceTexture);
+}
+
+void MaskEffectImpl::CreateRenderTasks(Toolkit::Control ownerControl)
+{
+  RenderTaskList taskList = GetSceneHolder().GetRenderTaskList();
+
+  mMaskTargetRenderTask = taskList.CreateTask();
+  mMaskTargetRenderTask.SetCameraActor(mCamera);
+  mMaskTargetRenderTask.SetExclusive(true);
+  mMaskTargetRenderTask.SetInputEnabled(true);
+  mMaskTargetRenderTask.SetSourceActor(ownerControl);
+  mMaskTargetRenderTask.SetFrameBuffer(mMaskTargetFrameBuffer);
+  mMaskTargetRenderTask.SetClearEnabled(true);
+  mMaskTargetRenderTask.SetClearColor(Color::TRANSPARENT);
+
+  mMaskSourceRenderTask = taskList.CreateTask();
+  mMaskSourceRenderTask.SetCameraActor(mCamera);
+  mMaskSourceRenderTask.SetExclusive(true);
+  mMaskSourceRenderTask.SetInputEnabled(false);
+  mMaskSourceRenderTask.SetSourceActor(mMaskControl.GetHandle());
+  mMaskSourceRenderTask.SetFrameBuffer(mMaskSourceFrameBuffer);
+  mMaskSourceRenderTask.SetClearEnabled(true);
+  mMaskSourceRenderTask.SetClearColor(Color::TRANSPARENT);
+}
+
+void MaskEffectImpl::SetShaderConstants(Toolkit::Control ownerControl)
+{
+  ownerControl.RegisterProperty(UNIFORM_MASK_MODE_NAME, static_cast<float>(mMaskMode));
+
+  Vector2 newMaskPosition;
+  newMaskPosition.x = -mMaskPosition.x;
+  newMaskPosition.y = -mMaskPosition.y;
+  ownerControl.RegisterProperty(UNIFORM_MASK_POSITION_NAME, newMaskPosition);
+
+  Vector2 newMaskScale;
+  newMaskScale.x = 1.0f / std::max(Math::MACHINE_EPSILON_100, mMaskScale.x);
+  newMaskScale.y = 1.0f / std::max(Math::MACHINE_EPSILON_100, mMaskScale.y);
+
+  ownerControl.RegisterProperty(UNIFORM_MASK_SCALE_NAME, newMaskScale);
+}
+
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
diff --git a/dali-toolkit/internal/controls/render-effects/mask-effect-impl.h b/dali-toolkit/internal/controls/render-effects/mask-effect-impl.h
new file mode 100644 (file)
index 0000000..812fa88
--- /dev/null
@@ -0,0 +1,171 @@
+#ifndef DALI_TOOLKIT_INTERNAL_MASK_EFFECT_H
+#define DALI_TOOLKIT_INTERNAL_MASK_EFFECT_H
+
+/*
+ * Copyright (c) 2025 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/scene-holder.h>
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/actors/camera-actor.h>
+#include <dali/public-api/images/image-operations.h>
+#include <dali/public-api/render-tasks/render-task.h>
+#include <dali/public-api/rendering/frame-buffer.h>
+#include <dali-toolkit/public-api/controls/render-effects/mask-effect.h>
+#include <string>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/render-effects/render-effect-impl.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+class MaskEffectImpl;
+using MaskEffectImplPtr = IntrusivePtr<MaskEffectImpl>;
+
+class MaskEffectImpl : public RenderEffectImpl
+{
+public:
+  /**
+   * @brief Creates an initialized MaskEffect with control, using default settings. The default settings are:
+   *
+   * maskMode = MaskEffect::MaskMode::ALPHA
+   * maskPosition = Vector2(0.f, 0.f)
+   * maskScale = Vector2(1.f, 1.f)
+   *
+   * @param[in] maskControl The source Control to affect mask.
+   * @return A handle to a newly allocated Dali resource
+   */
+  static MaskEffectImplPtr New(Toolkit::Control maskControl);
+
+  /**
+   * @brief Creates an initialized MaskEffect.
+   * @param[in] maskControl The source Control to affect mask.
+   * @param[in] maskMode Defines pixel data type (alpha, luminance) used as the mask source.
+   * @param[in] maskPosition The Position of mask source.
+   * @param[in] maskScale The Scale of mask source.
+   * @return A handle to a newly allocated Dali resource
+   */
+  static MaskEffectImplPtr New(Toolkit::Control maskControl, MaskEffect::MaskMode maskMode, Vector2 maskPosition, Vector2 maskScale);
+
+  /**
+   * @copydoc Toolkit::Internal::RenderEffectImpl::GetOffScreenRenderableType
+   */
+  OffScreenRenderable::Type GetOffScreenRenderableType() override;
+
+  /**
+   * @copydoc Toolkit::Internal::RenderEffectImpl::GetOffScreenRenderTasks
+   */
+  void GetOffScreenRenderTasks(std::vector<Dali::RenderTask>& tasks, bool isForward) override;
+
+protected:
+  /**
+   * @brief Creates an uninitialized mask effect implementation
+   * @param[in] maskControl
+   */
+  MaskEffectImpl(Toolkit::Control maskControl);
+
+  /**
+   * @brief Creates an uninitialized mask effect implementation
+   * @param[in] maskControl
+   * @param[in] maskMode
+   * @param[in] maskPosition
+   * @param[in] maskScale
+   */
+  MaskEffectImpl(Toolkit::Control maskControl, MaskEffect::MaskMode maskMode, Vector2 maskPosition, Vector2 maskScale);
+
+  /**
+   * @brief Destructor
+   */
+  virtual ~MaskEffectImpl();
+
+  /**
+   * @brief Initializes mask effect
+   */
+  void OnInitialize() override;
+
+  /**
+   * @brief Activates mask effect
+   * @note If the mask source actor is an ancestor of the target actor,
+   * the target will not be rendered due to scene hierarchy.
+   */
+  void OnActivate() override;
+
+  /**
+   * @brief Dectivates mask effect
+   */
+  void OnDeactivate() override;
+
+  /**
+   * @brief Redraw effect without deactivation
+   */
+  void OnRefresh() override;
+
+private:
+  // inner functions
+  /**
+   * @brief Sets frame buffers to draw masked output.
+   * @param[in] size The size of target.
+   */
+  void CreateFrameBuffers(const ImageDimensions size);
+
+  /**
+   * @brief Sets mask render tasks.
+   * @param[in] sceneHolder SceneHolder of owner control
+   * @param[in] ownerControl Input owner control
+   */
+  void CreateRenderTasks(Toolkit::Control ownerControl);
+
+  /**
+   * @brief Sets shader constants, mask mode, position, and scale.
+   * @param[in] ownerControl Input owner control
+   */
+  void SetShaderConstants(Toolkit::Control ownerControl);
+
+private:
+
+  MaskEffectImpl(const MaskEffectImpl&) = delete;
+  MaskEffectImpl(MaskEffectImpl&&)      = delete;
+  MaskEffectImpl& operator=(MaskEffectImpl&&) = delete;      // no move()
+  MaskEffectImpl& operator=(const MaskEffectImpl&) = delete; // no copy()
+
+private:
+  // Camera actors
+  CameraActor mCamera;
+
+  // Resource
+  RenderTask  mMaskTargetRenderTask;
+  FrameBuffer mMaskTargetFrameBuffer;
+
+  WeakHandle<Toolkit::Control> mMaskControl;
+
+  RenderTask  mMaskSourceRenderTask;
+  FrameBuffer mMaskSourceFrameBuffer;
+
+  // Variables
+  MaskEffect::MaskMode mMaskMode;
+  Vector2 mMaskPosition;
+  Vector2 mMaskScale;
+};
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_BACKGROUND_MASK_EFFECT_H
index 6cc4e5f5c6e7fb240cb05e5a36b8460c09a062fb..0e66eb83f2c983a020eaae993eece3cdc87ca93b 100644 (file)
@@ -71,6 +71,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/controls/alignment/alignment-impl.cpp
    ${toolkit_src_dir}/controls/render-effects/render-effect-impl.cpp
    ${toolkit_src_dir}/controls/render-effects/blur-effect-impl.cpp
+   ${toolkit_src_dir}/controls/render-effects/mask-effect-impl.cpp
    ${toolkit_src_dir}/controls/render-effects/offscreen-rendering-impl.cpp
    ${toolkit_src_dir}/controls/bloom-view/bloom-view-impl.cpp
    ${toolkit_src_dir}/controls/bubble-effect/bubble-emitter-impl.cpp
diff --git a/dali-toolkit/internal/graphics/shaders/mask-effect.frag b/dali-toolkit/internal/graphics/shaders/mask-effect.frag
new file mode 100644 (file)
index 0000000..d12818f
--- /dev/null
@@ -0,0 +1,37 @@
+//@name mask-effect.frag
+
+//@version 100
+
+precision highp float;
+
+INPUT highp vec2 vTexCoord;
+
+UNIFORM sampler2D uMaskTexture;
+UNIFORM sampler2D uTargetTexture;
+
+UNIFORM_BLOCK FragBlock
+{
+  UNIFORM lowp vec4 uColor;
+  UNIFORM highp float uMaskMode;
+  UNIFORM highp vec2 uMaskPosition;
+  UNIFORM highp vec2 uMaskScale;
+};
+
+void main()
+{
+  highp vec2 transformedUV = vTexCoord * uMaskScale + uMaskPosition;
+
+  highp vec4 baseColor = TEXTURE(uTargetTexture, vTexCoord);
+  highp vec4 maskColor = TEXTURE(uMaskTexture, transformedUV);
+  highp float maskAlpha = 0.0f;
+
+  highp float alphaMask = maskColor.a;
+  highp float luminanceMask = dot(maskColor.rgb, vec3(0.299, 0.587, 0.114));
+
+  highp float uInBounds = step(0.0, transformedUV.x) * step(transformedUV.x, 1.0) * step(0.0, transformedUV.y) * step(transformedUV.y, 1.0);
+
+  maskAlpha = mix(alphaMask, luminanceMask, clamp(uMaskMode, 0.0f, 1.0f));
+  maskAlpha *= uInBounds;
+
+  gl_FragColor = baseColor * uColor * maskAlpha;
+}
diff --git a/dali-toolkit/public-api/controls/render-effects/mask-effect.cpp b/dali-toolkit/public-api/controls/render-effects/mask-effect.cpp
new file mode 100644 (file)
index 0000000..1edf51a
--- /dev/null
@@ -0,0 +1,48 @@
+
+/*
+ * Copyright (c) 2025 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/public-api/controls/render-effects/mask-effect.h>
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/render-effects/mask-effect-impl.h>
+namespace Dali
+{
+namespace Toolkit
+{
+MaskEffect::MaskEffect() = default;
+MaskEffect::MaskEffect(const MaskEffect& handle)
+: RenderEffect(handle)
+{
+}
+MaskEffect::MaskEffect(Internal::MaskEffectImpl* maskEffectImpl)
+: RenderEffect(maskEffectImpl)
+{
+}
+MaskEffect::~MaskEffect() = default;
+MaskEffect MaskEffect::New(Toolkit::Control maskControl)
+{
+  Internal::MaskEffectImplPtr internal = Internal::MaskEffectImpl::New(maskControl);
+  return MaskEffect(internal.Get());
+}
+MaskEffect MaskEffect::New(Toolkit::Control maskControl, MaskMode maskMode, Vector2 maskPosition, Vector2 maskScale)
+{
+  Internal::MaskEffectImplPtr internal = Internal::MaskEffectImpl::New(maskControl, maskMode, maskPosition, maskScale);
+  return MaskEffect(internal.Get());
+}
+} // namespace Toolkit
+} // namespace Dali
diff --git a/dali-toolkit/public-api/controls/render-effects/mask-effect.h b/dali-toolkit/public-api/controls/render-effects/mask-effect.h
new file mode 100644 (file)
index 0000000..371d34f
--- /dev/null
@@ -0,0 +1,117 @@
+#ifndef DALI_TOOLKIT_MASK_EFFECT_H
+#define DALI_TOOLKIT_MASK_EFFECT_H
+
+/*
+ * Copyright (c) 2025 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/render-effects/render-effect.h>
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal DALI_INTERNAL
+{
+class MaskEffectImpl;
+} // namespace Internal DALI_INTERNAL
+
+/**
+ * @brief MaskEffect is a visual effect that masks owner control.
+ * This class is a concrete class from RenderEffect interface.
+ * Add this effect to a control, clear manually to deactivate.
+ *
+ * Toolkit::Control control = Toolkit::Control::New();
+ * parent.Add(control);
+ * control.SetRenderEffect(MaskEffect::New(maskControl)); // Activate
+ * ...
+ * control.ClearRenderEffect(); // Deactivate
+ *
+ * @SINCE_2_4.15
+ */
+class DALI_TOOLKIT_API MaskEffect : public RenderEffect
+{
+public:
+
+  /**
+   * @brief Enumeration for selecting how the mask source interprets pixel data.
+   * @SINCE_2_4.15
+   */
+  enum MaskMode
+  {
+    ALPHA,     ///< Uses the alpha channel of the mask texture. (Default) @SINCE_2_4.15
+    LUMINANCE, ///< Converts RGB to grayscale and uses the luminance as mask value. @SINCE_2_4.15
+  };
+
+  /**
+   * @brief Creates an initialized MaskEffect with control, using default settings. The default settings are:
+   *
+   * maskMode = MaskMode::ALPHA
+   * maskPosition = Vector2(0.f, 0.f)
+   * maskScale = Vector2(1.f, 1.f)
+   *
+   * @param[in] maskControl The source Control to affect mask.
+   *
+   * @SINCE_2_4.15
+   * @return A handle to a newly allocated Dali resource
+   */
+  static MaskEffect New(Toolkit::Control maskControl);
+
+  /**
+   * @brief Creates an initialized MaskEffect.
+   *
+   * @param[in] maskControl The source Control to affect mask.
+   * @param[in] maskMode Defines pixel data type (alpha, luminance) used as the mask source.
+   * @param[in] maskPosition The Position of mask source.
+   * @param[in] maskScale The Scale of mask source.
+   *
+   * @SINCE_2_4.15
+   * @return A handle to a newly allocated Dali resource
+   */
+  static MaskEffect New(Toolkit::Control maskControl, MaskMode maskMode, Vector2 maskPosition, Vector2 maskScale);
+
+  /**
+   * @brief Creates an uninitialized mask effect.
+   * @SINCE_2_4.15
+   */
+  MaskEffect();
+
+  /**
+   * @brief Copy constructor.
+   * @SINCE_2_4.15
+   */
+  MaskEffect(const MaskEffect& handle);
+
+  /**
+   * @brief Destructor
+   * @SINCE_2_4.15
+   */
+  ~MaskEffect();
+
+public: // Not intended for use by Application developers
+  ///@cond internal
+  /**
+   * @brief Creates a handle using the Toolkit::Internal implementation.
+   * @SINCE_2_4.15
+   * @param[in] maskEffectImpl The mask effect internal implementation.
+   */
+  explicit DALI_INTERNAL MaskEffect(Internal::MaskEffectImpl* maskEffectImpl);
+  ///@endcond
+};
+} // namespace Toolkit
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_MASK_EFFECT_H
index 9665ce99026f22c93efd9048014ef252b935f84b..fd3aa84ddd3d4e35528530efc1cc602df4574b7e 100644 (file)
@@ -16,6 +16,7 @@ SET( public_api_src_files
   ${public_api_src_dir}/controls/progress-bar/progress-bar.cpp
   ${public_api_src_dir}/controls/render-effects/render-effect.cpp
   ${public_api_src_dir}/controls/render-effects/background-blur-effect.cpp
+  ${public_api_src_dir}/controls/render-effects/mask-effect.cpp
   ${public_api_src_dir}/controls/scrollable/item-view/default-item-layout.cpp
   ${public_api_src_dir}/controls/scrollable/item-view/item-layout.cpp
   ${public_api_src_dir}/controls/scrollable/item-view/item-view.cpp
@@ -110,6 +111,7 @@ SET( public_api_progress_bar_header_files
 SET( public_api_render_effects_header_files
   ${public_api_src_dir}/controls/render-effects/render-effect.h
   ${public_api_src_dir}/controls/render-effects/background-blur-effect.h
+  ${public_api_src_dir}/controls/render-effects/mask-effect.h
 )
 
 SET( public_api_scrollable_header_files