From: ANZ1217 Date: Mon, 7 Apr 2025 06:38:07 +0000 (+0900) Subject: Introduce Mask Effect X-Git-Tag: dali_2.4.15~4^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=acbc3914364dfe0c4f386aecdef73f803bccff90;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git Introduce Mask Effect Change-Id: I18883959c71a80bbcfc7414aba1b2395e532c88e --- diff --git a/automated-tests/src/dali-toolkit/utc-Dali-RenderEffect.cpp b/automated-tests/src/dali-toolkit/utc-Dali-RenderEffect.cpp index 4346c2f03f..49655ecfbd 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-RenderEffect.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-RenderEffect.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include 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 index 0000000000..6ed4621f5f --- /dev/null +++ b/dali-toolkit/internal/controls/render-effects/mask-effect-impl.cpp @@ -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 + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include + +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& 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(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 index 0000000000..812fa88fc8 --- /dev/null +++ b/dali-toolkit/internal/controls/render-effects/mask-effect-impl.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +class MaskEffectImpl; +using MaskEffectImplPtr = IntrusivePtr; + +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& 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 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 diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index 6cc4e5f5c6..0e66eb83f2 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -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 index 0000000000..d12818fca9 --- /dev/null +++ b/dali-toolkit/internal/graphics/shaders/mask-effect.frag @@ -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 index 0000000000..1edf51a3fd --- /dev/null +++ b/dali-toolkit/public-api/controls/render-effects/mask-effect.cpp @@ -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 +// INTERNAL INCLUDES +#include +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 index 0000000000..371d34ff8f --- /dev/null +++ b/dali-toolkit/public-api/controls/render-effects/mask-effect.h @@ -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 +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 diff --git a/dali-toolkit/public-api/file.list b/dali-toolkit/public-api/file.list index 9665ce9902..fd3aa84ddd 100644 --- a/dali-toolkit/public-api/file.list +++ b/dali-toolkit/public-api/file.list @@ -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