*/
#include <dali-toolkit-test-suite-utils.h>
+
+#include <dali-toolkit/dali-toolkit.h>
+
#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;
DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
////////////////////////////////////////////
+ tet_infoline("resize test on BackgroundBlurEffect");
control.SetRenderEffect(BackgroundBlurEffect::New());
application.SendNotification();
DALI_TEST_EQUALS(control.GetProperty<float>(Actor::Property::SIZE_WIDTH), 0.0f, TEST_LOCATION);
DALI_TEST_EQUALS(control.GetProperty<float>(Actor::Property::SIZE_HEIGHT), 0.0f, TEST_LOCATION);
tet_infoline("Background blur effect deactivated.\n");
+ /////////////////////////////////////////////
+ tet_infoline("resize test on GaussianBlurEffect");
+ GaussianBlurEffect effect = GaussianBlurEffect::New(20u);
+ control.SetRenderEffect(effect);
+
+ DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+ DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
+ tet_infoline("size zero owner control's effect is not activated.");
+
+ control.SetProperty(Actor::Property::SIZE, Vector2(30.0f, 30.0f));
+
+ application.SendNotification();
+ application.Render();
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+ DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION); // Uses cache renderer
+
+ DALI_TEST_EQUALS(control.GetProperty<float>(Actor::Property::SIZE_WIDTH), 30.0f, TEST_LOCATION);
+ DALI_TEST_EQUALS(control.GetProperty<float>(Actor::Property::SIZE_HEIGHT), 30.0f, TEST_LOCATION);
+ tet_infoline("Blur effect activated.\n");
+ DALI_TEST_EQUALS(effect.GetBlurRadius(), 20u, TEST_LOCATION);
+
+ control.SetProperty(Actor::Property::SIZE, Vector2(10.0f, 10.0f));
+
+ application.SendNotification();
+ application.Render();
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(control.GetProperty<float>(Actor::Property::SIZE_WIDTH), 10.0f, TEST_LOCATION);
+ DALI_TEST_EQUALS(control.GetProperty<float>(Actor::Property::SIZE_HEIGHT), 10.0f, TEST_LOCATION);
+ tet_infoline("Blur effect refreshed.\n");
END_TEST;
}
DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
DALI_TEST_EQUALS(effect.GetBlurRadius(), 2u, TEST_LOCATION);
+ control.SetRenderEffect(GaussianBlurEffect::New(2u)); // invalid blur radius value(too small)
+ application.SendNotification();
+
+ DALI_TEST_EQUALS(1u, taskList.GetTaskCount(), TEST_LOCATION);
+ DALI_TEST_EQUALS(effect.GetBlurRadius(), 2u, TEST_LOCATION);
+
END_TEST;
}
scene.Add(control);
- // Add render effect during scene on.
- BackgroundBlurEffect effect = BackgroundBlurEffect::New(20u);
- DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
+ {
+ // Add render effect during scene on.
+ BackgroundBlurEffect effect = BackgroundBlurEffect::New(20u);
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
- effect.SetBlurOnce(true);
- effect.FinishedSignal().Connect(&application, &BlurRenderingFinishedCallback);
- control.SetRenderEffect(effect);
- DALI_TEST_EQUALS(effect.GetBlurOnce(), true, TEST_LOCATION);
+ effect.SetBlurOnce(true);
+ effect.FinishedSignal().Connect(&application, &BlurRenderingFinishedCallback);
+ control.SetRenderEffect(effect);
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), true, TEST_LOCATION);
- application.SendNotification();
+ application.SendNotification();
- RenderTaskList taskList = scene.GetRenderTaskList();
+ RenderTaskList taskList = scene.GetRenderTaskList();
- // Render effect activated.
- DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
- tet_printf("order : %d\n", taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex());
- DALI_TEST_EQUALS(INT32_MIN + 2, taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex(), TEST_LOCATION);
+ // Render effect activated.
+ DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+ tet_printf("order : %d\n", taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex());
+ DALI_TEST_EQUALS(INT32_MIN + 2, taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex(), TEST_LOCATION);
- effect.SetBlurOnce(false);
- DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
+ effect.SetBlurOnce(false);
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
- DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
- tet_printf("order : %d\n", taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex());
- DALI_TEST_EQUALS(INT32_MIN + 2, taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex(), TEST_LOCATION);
+ DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+ tet_printf("order : %d\n", taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex());
+ DALI_TEST_EQUALS(INT32_MIN + 2, taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex(), TEST_LOCATION);
+ }
+ {
+ // Add render effect during scene on.
+ GaussianBlurEffect effect = GaussianBlurEffect::New(20u);
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
+
+ effect.SetBlurOnce(true);
+ effect.FinishedSignal().Connect(&application, &BlurRenderingFinishedCallback);
+ control.SetRenderEffect(effect);
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), true, TEST_LOCATION);
+
+ application.SendNotification();
+
+ RenderTaskList taskList = scene.GetRenderTaskList();
+
+ // Render effect activated.
+ DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+ tet_printf("order : %d\n", taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex());
+ DALI_TEST_EQUALS(0u, taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex(), TEST_LOCATION);
+
+ effect.SetBlurOnce(false);
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
+ tet_printf("order : %d\n", taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex());
+ DALI_TEST_EQUALS(0u, taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex(), TEST_LOCATION);
+ }
END_TEST;
}
control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
- BackgroundBlurEffect effect = BackgroundBlurEffect::New();
- control.SetRenderEffect(effect);
- scene.Add(control);
-
- float durationSeconds = 0.05f;
- Animation animation = Animation::New(durationSeconds);
-
- effect.AddBlurStrengthAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
- animation.Play();
- application.SendNotification();
- application.Render(static_cast<unsigned int>(durationSeconds * 1000.0f) + 1u /*just beyond the animation duration*/);
- animation.Clear();
- DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
-
- effect.SetBlurOnce(true);
- effect.AddBlurStrengthAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
- // animation will not be added but cannot check
- animation.Clear();
- DALI_TEST_EQUALS(effect.GetBlurOnce(), true, TEST_LOCATION);
-
- effect.SetBlurRadius(2u);
- effect.AddBlurStrengthAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
- // animation will not be added but cannot check
- animation.Clear();
+ {
+ BackgroundBlurEffect effect = BackgroundBlurEffect::New();
+ control.SetRenderEffect(effect);
+ scene.Add(control);
+
+ float durationSeconds = 0.05f;
+ Animation animation = Animation::New(durationSeconds);
+
+ effect.AddBlurStrengthAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ animation.Play();
+ application.SendNotification();
+ application.Render(static_cast<unsigned int>(durationSeconds * 1000.0f) + 1u /*just beyond the animation duration*/);
+ animation.Clear();
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
+
+ effect.SetBlurOnce(true);
+ effect.AddBlurStrengthAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ // animation will not be added but cannot check
+ animation.Clear();
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), true, TEST_LOCATION);
+
+ effect.SetBlurRadius(2u);
+ effect.AddBlurStrengthAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ // animation will not be added but cannot check
+ animation.Clear();
+ }
+
+ {
+ GaussianBlurEffect effect = GaussianBlurEffect::New();
+ control.SetRenderEffect(effect);
+ scene.Add(control);
+
+ float durationSeconds = 0.05f;
+ Animation animation = Animation::New(durationSeconds);
+
+ effect.AddBlurStrengthAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ animation.Play();
+ application.SendNotification();
+ application.Render(static_cast<unsigned int>(durationSeconds * 1000.0f) + 1u /*just beyond the animation duration*/);
+ animation.Clear();
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
+
+ effect.SetBlurOnce(true);
+ effect.AddBlurStrengthAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ // animation will not be added but cannot check
+ animation.Clear();
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), true, TEST_LOCATION);
+
+ effect.SetBlurRadius(2u);
+ effect.AddBlurStrengthAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ // animation will not be added but cannot check
+ animation.Clear();
+ }
END_TEST;
}
control.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
control.SetProperty(Actor::Property::SIZE, Vector2(1.0f, 1.0f));
- BackgroundBlurEffect effect = BackgroundBlurEffect::New();
- control.SetRenderEffect(effect);
- scene.Add(control);
-
- float durationSeconds = 0.05f;
- Animation animation = Animation::New(durationSeconds);
-
- effect.AddBlurOpacityAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
- animation.Play();
- application.SendNotification();
- application.Render(static_cast<unsigned int>(durationSeconds * 1000.0f) + 1u /*just beyond the animation duration*/);
- DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
-
- effect.SetBlurOnce(true);
- effect.AddBlurOpacityAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
- // animation will not be added but cannot check
- animation.Clear();
- DALI_TEST_EQUALS(effect.GetBlurOnce(), true, TEST_LOCATION);
-
- effect.SetBlurRadius(2u);
- effect.AddBlurOpacityAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
- // animation will not be added but cannot check
- animation.Clear();
+ {
+ BackgroundBlurEffect effect = BackgroundBlurEffect::New();
+ control.SetRenderEffect(effect);
+ scene.Add(control);
+
+ float durationSeconds = 0.05f;
+ Animation animation = Animation::New(durationSeconds);
+
+ effect.AddBlurOpacityAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ animation.Play();
+ application.SendNotification();
+ application.Render(static_cast<unsigned int>(durationSeconds * 1000.0f) + 1u /*just beyond the animation duration*/);
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
+
+ effect.SetBlurOnce(true);
+ effect.AddBlurOpacityAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ // animation will not be added but cannot check
+ animation.Clear();
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), true, TEST_LOCATION);
+
+ effect.SetBlurRadius(2u);
+ effect.AddBlurOpacityAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ // animation will not be added but cannot check
+ animation.Clear();
+ }
+ {
+ Toolkit::GaussianBlurEffect effect = Toolkit::GaussianBlurEffect::New();
+ control.SetRenderEffect(effect);
+ scene.Add(control);
+
+ float durationSeconds = 0.05f;
+ Animation animation = Animation::New(durationSeconds);
+
+ effect.AddBlurOpacityAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ animation.Play();
+ application.SendNotification();
+ application.Render(static_cast<unsigned int>(durationSeconds * 1000.0f) + 1u /*just beyond the animation duration*/);
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
+
+ effect.SetBlurOnce(true);
+ effect.AddBlurOpacityAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ // animation will not be added but cannot check
+ animation.Clear();
+ DALI_TEST_EQUALS(effect.GetBlurOnce(), true, TEST_LOCATION);
+
+ effect.SetBlurRadius(2u);
+ effect.AddBlurOpacityAnimation(animation, AlphaFunction::BuiltinFunction::EASE_IN, TimePeriod(0, durationSeconds), 0.0f, 1.0f);
+ // animation will not be added but cannot check
+ animation.Clear();
+ }
END_TEST;
}
#include <dali-toolkit/public-api/styling/style-manager.h>
#include <dali-toolkit/public-api/controls/render-effects/background-blur-effect.h>
+#include <dali-toolkit/public-api/controls/render-effects/gaussian-blur-effect.h>
#include <dali-toolkit/public-api/controls/render-effects/mask-effect.h>
#include <dali-toolkit/public-api/controls/render-effects/render-effect.h>
#include <dali/public-api/animation/key-frames.h>
#include <dali/public-api/images/image-operations.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
static constexpr float BLUR_EFFECT_DOWNSCALE_FACTOR = 0.25f;
static constexpr uint32_t BLUR_EFFECT_BLUR_RADIUS = 10u;
-static constexpr float MINIMUM_DOWNSCALE_FACTOR = 0.1f;
-static constexpr float MAXIMUM_DOWNSCALE_FACTOR = 1.0f;
-
-static constexpr uint32_t MINIMUM_GPU_ARRAY_SIZE = 2u; // GPU cannot handle array size smaller than 2.
-static constexpr uint32_t MAXIMUM_BLUR_RADIUS = 500u; ///< Maximum pixel radius for blur effect. (GL_MAX_FRAGMENT_UNIFORM_COMPONENTS(Usually 1024) - 19 (vertex shader used)) / 3 float
-
-static constexpr float MAXIMUM_BELL_CURVE_WIDTH = 171.352f; ///< bell curve width for MAXIMUM_BLUR_RADIUS case
-static constexpr int32_t MAXIMUM_BELL_CURVE_LOOP_TRIAL_COUNT = 50;
+static constexpr uint32_t MINIMUM_GPU_ARRAY_SIZE = 2u; // GPU cannot handle array size smaller than 2.
static constexpr std::string_view UNIFORM_BLUR_STRENGTH_NAME("uAnimationRatio");
static constexpr std::string_view UNIFORM_BLUR_OPACITY_NAME("uOpacity");
-
-/**
- * @brief Calculates gaussian weight
- * @param[in] localOffset Input variable of gaussian distribution
- * @param[in] sigma Standard deviation of gaussian distribution, the width of the "bell"
- * @note Expected value of this gaussian distribution is 0.
- */
-inline float CalculateGaussianWeight(float localOffset, float sigma)
-{
- return (1.0f / (sigma * sqrt(2.0f * Dali::Math::PI))) * exp(-0.5f * (localOffset / sigma * localOffset / sigma));
-}
-
-/**
- * @brief
- * @return Downscaled(optimized) blur radius
- */
-uint32_t FitBlurRadiusToValidRange(float& downscaleFactor, uint32_t& blurRadius)
-{
- downscaleFactor = BLUR_EFFECT_DOWNSCALE_FACTOR;
-
- if(DALI_UNLIKELY(blurRadius > MAXIMUM_BLUR_RADIUS))
- {
- const uint32_t fixedBlurRadius = MAXIMUM_BLUR_RADIUS;
- const float fixedDownScaleFactor = Dali::Clamp(
- downscaleFactor * static_cast<float>(fixedBlurRadius) / static_cast<float>(blurRadius),
- MINIMUM_DOWNSCALE_FACTOR,
- MAXIMUM_DOWNSCALE_FACTOR);
-
- DALI_LOG_ERROR("Blur radius is out of bound: %u. Use %u and make downscale factor %f to %f.\n",
- blurRadius,
- fixedBlurRadius,
- downscaleFactor,
- fixedDownScaleFactor);
-
- downscaleFactor = fixedDownScaleFactor;
- blurRadius = fixedBlurRadius;
- }
- return static_cast<uint32_t>(blurRadius * downscaleFactor);
-}
+static constexpr std::string_view UNIFORM_BLUR_OFFSET_DIRECTION_NAME("uOffsetDirection");
} // namespace
namespace Dali
mDownscaleFactor(BLUR_EFFECT_DOWNSCALE_FACTOR),
mBlurRadius(BLUR_EFFECT_BLUR_RADIUS),
mDownscaledBlurRadius(static_cast<uint32_t>(BLUR_EFFECT_BLUR_RADIUS * BLUR_EFFECT_DOWNSCALE_FACTOR)),
- mBellCurveWidth(Math::MACHINE_EPSILON_1),
mSkipBlur(false),
mBlurOnce(false)
{
mDownscaleFactor(BLUR_EFFECT_DOWNSCALE_FACTOR),
mBlurRadius(blurRadius),
mDownscaledBlurRadius(BLUR_EFFECT_BLUR_RADIUS),
- mBellCurveWidth(Math::MACHINE_EPSILON_1),
mSkipBlur(false),
mBlurOnce(false)
{
- mDownscaledBlurRadius = FitBlurRadiusToValidRange(mDownscaleFactor, mBlurRadius);
+ mDownscaledBlurRadius = GaussianBlurAlgorithm::GetDownscaledBlurRadius(mDownscaleFactor, mBlurRadius);
if(DALI_UNLIKELY((mDownscaledBlurRadius >> 1) < MINIMUM_GPU_ARRAY_SIZE))
{
mSkipBlur = true;
}
// Reinitialize blur parameters
+ mDownscaledBlurRadius = BLUR_EFFECT_DOWNSCALE_FACTOR;
mBlurRadius = blurRadius;
- mDownscaledBlurRadius = FitBlurRadiusToValidRange(mDownscaleFactor, mBlurRadius);
+ mDownscaledBlurRadius = GaussianBlurAlgorithm::GetDownscaledBlurRadius(mDownscaleFactor, mBlurRadius);
mSkipBlur = false;
if(DALI_UNLIKELY((mDownscaledBlurRadius >> 1) < MINIMUM_GPU_ARRAY_SIZE))
return;
}
- // Calculate bell curve width
- {
- const float epsilon = 1e-2f / (mDownscaledBlurRadius * 2);
- const float localOffset = (mDownscaledBlurRadius * 2) - 1;
-
- float lowerBoundBellCurveWidth = Dali::Math::MACHINE_EPSILON_10000;
- float upperBoundBellCurveWidth = MAXIMUM_BELL_CURVE_WIDTH;
-
- int trialCount = 0;
- while(trialCount++ < MAXIMUM_BELL_CURVE_LOOP_TRIAL_COUNT && upperBoundBellCurveWidth - lowerBoundBellCurveWidth > Dali::Math::MACHINE_EPSILON_10000)
- {
- mBellCurveWidth = (lowerBoundBellCurveWidth + upperBoundBellCurveWidth) * 0.5f;
- if(CalculateGaussianWeight(localOffset, mBellCurveWidth) < epsilon)
- {
- lowerBoundBellCurveWidth = mBellCurveWidth;
- }
- else
- {
- upperBoundBellCurveWidth = mBellCurveWidth;
- }
- }
- }
-
- DALI_LOG_INFO(gRenderEffectLogFilter, Debug::Verbose, "[BlurEffect:%p] mBellCurveWidth calculated! [mBlurRadius:%u][mBellCurveWidth:%f]\n", this, mBlurRadius, mBellCurveWidth);
-
// Create blur actors
{
mInternalRoot.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
- // shader
- std::ostringstream fragmentStringStream;
- fragmentStringStream << "#define NUM_SAMPLES " << (mDownscaledBlurRadius >> 1) << "\n";
- fragmentStringStream << SHADER_BLUR_EFFECT_FRAG;
- std::string fragmentSource(fragmentStringStream.str());
-
- // Create an actor for performing a horizontal blur on the texture
- Renderer horizontalBlurRenderer = CreateRenderer(BASIC_VERTEX_SOURCE, fragmentSource.c_str());
- horizontalBlurRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true); // Always use pre-multiply alpha
-
+ // Create an actor for performing a vertical blur on the texture
+ Renderer horizontalBlurRenderer = GaussianBlurAlgorithm::CreateRenderer(mDownscaledBlurRadius);
if(mHorizontalBlurActor)
{
mHorizontalBlurActor.RemoveRenderer(0u);
}
// Create an actor for performing a vertical blur on the texture
- Renderer verticalBlurRenderer = CreateRenderer(BASIC_VERTEX_SOURCE, fragmentSource.c_str());
- verticalBlurRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true); // Always use pre-multiply alpha
-
+ Renderer verticalBlurRenderer = GaussianBlurAlgorithm::CreateRenderer(mDownscaledBlurRadius);
if(mVerticalBlurActor)
{
mVerticalBlurActor.RemoveRenderer(0u);
// Set blur
CreateFrameBuffers(ImageDimensions(downsampledWidth, downsampledHeight));
CreateRenderTasks(GetSceneHolder(), ownerControl);
- SetShaderConstants(downsampledWidth, downsampledHeight);
+
+ // Reset shader constants
+ auto& blurShader = GaussianBlurAlgorithm::GetGaussianBlurShader(mDownscaledBlurRadius);
+ {
+ Renderer renderer = mHorizontalBlurActor.GetRendererAt(0u);
+ renderer.SetShader(blurShader);
+ renderer.RegisterUniqueProperty(UNIFORM_BLUR_OFFSET_DIRECTION_NAME.data(), Vector2(1.0f / downsampledWidth, 0.0f));
+ }
+ {
+ Renderer renderer = mVerticalBlurActor.GetRendererAt(0u);
+ renderer.SetShader(blurShader);
+ renderer.RegisterUniqueProperty(UNIFORM_BLUR_OFFSET_DIRECTION_NAME.data(), Vector2(0.0f, 1.0f / downsampledHeight));
+ }
// Inject blurred output to control
Renderer renderer = GetTargetRenderer();
mVerticalBlurActor.SetProperty(Actor::Property::SIZE, Vector2(downsampledWidth, downsampledHeight));
CreateFrameBuffers(ImageDimensions(downsampledWidth, downsampledHeight));
- SetShaderConstants(downsampledWidth, downsampledHeight);
+
+ // Reset shader constants
+ mHorizontalBlurActor.RegisterProperty(UNIFORM_BLUR_OFFSET_DIRECTION_NAME.data(), Vector2(1.0f, 0.0f) / downsampledWidth);
+ mVerticalBlurActor.RegisterProperty(UNIFORM_BLUR_OFFSET_DIRECTION_NAME.data(), Vector2(0.0f, 1.0f) / downsampledHeight);
mSourceRenderTask.SetFrameBuffer(mInputBackgroundFrameBuffer);
mHorizontalBlurTask.SetFrameBuffer(mTemporaryFrameBuffer);
sourceRenderTask.RenderUntil(stopperActor);
}
-void BackgroundBlurEffectImpl::SetShaderConstants(uint32_t downsampledWidth, uint32_t downsampledHeight)
-{
- const uint32_t sampleCount = mDownscaledBlurRadius >> 1; // compression
- const uint32_t kernelSize = sampleCount * 4 - 1;
- const uint32_t halfKernelSize = kernelSize / 2 + 1; // Gaussian curve is symmetric
-
- // Output: Gaussian kernel compressed to half size
- std::vector<float> uvOffsets(sampleCount);
- std::vector<float> weights(sampleCount);
-
- // Generate half size kernel
- std::vector<float> halfSideKernel(halfKernelSize);
-
- halfSideKernel[0] = CalculateGaussianWeight(0.0f, mBellCurveWidth);
- float totalWeights = halfSideKernel[0];
- for(unsigned int i = 1; i < halfKernelSize; i++)
- {
- float w = CalculateGaussianWeight(i, mBellCurveWidth);
- halfSideKernel[i] = w;
- totalWeights += w * 2.0f;
- }
- for(unsigned int i = 0; i < halfKernelSize; i++)
- {
- halfSideKernel[i] /= totalWeights;
- }
- halfSideKernel[0] *= 0.5f;
-
- // Compress kernel to half size
- for(unsigned int i = 0; i < sampleCount; i++)
- {
- weights[i] = halfSideKernel[2 * i] + halfSideKernel[2 * i + 1];
- uvOffsets[i] = 2.0f * i + halfSideKernel[2 * i + 1] / weights[i];
- }
-
- // Set shader constants
- for(unsigned int i = 0; i < sampleCount; ++i)
- {
- mHorizontalBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), Vector2(uvOffsets[i] / downsampledWidth, 0.0f));
- mHorizontalBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
-
- mVerticalBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), Vector2(0.0f, uvOffsets[i] / downsampledHeight));
- mVerticalBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
- }
-}
-
-std::string BackgroundBlurEffectImpl::GetSampleOffsetsPropertyName(unsigned int index) const
-{
- std::ostringstream oss;
- oss << "uSampleOffsets[" << index << "]";
- return oss.str();
-}
-
-std::string BackgroundBlurEffectImpl::GetSampleWeightsPropertyName(unsigned int index) const
-{
- std::ostringstream oss;
- oss << "uSampleWeights[" << index << "]";
- return oss.str();
-}
-
Dali::Toolkit::BackgroundBlurEffect::FinishedSignalType& BackgroundBlurEffectImpl::FinishedSignal()
{
return mFinishedSignal;
-#ifndef DALI_TOOLKIT_INTERNAL_BLUR_EFFECT_H
-#define DALI_TOOLKIT_INTERNAL_BLUR_EFFECT_H
+#ifndef DALI_TOOLKIT_INTERNAL_BACKGROUND_BLUR_EFFECT_H
+#define DALI_TOOLKIT_INTERNAL_BACKGROUND_BLUR_EFFECT_H
/*
* Copyright (c) 2025 Samsung Electronics Co., Ltd.
#include <dali/public-api/object/weak-handle.h>
#include <dali/public-api/render-tasks/render-task.h>
#include <dali/public-api/rendering/frame-buffer.h>
-#include <string>
// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/render-effects/gaussian-blur-algorithm.h>
#include <dali-toolkit/internal/controls/render-effects/render-effect-impl.h>
#include <dali-toolkit/public-api/controls/render-effects/background-blur-effect.h>
* @brief Creates an initialized BlurEffect implementation, using default settings. As default, blur radius is set to 10u.
* @return A handle to a newly allocated Dali resource
*/
-
static BackgroundBlurEffectImplPtr New();
/**
*/
void ApplyRenderTaskSourceActor(RenderTask sourceRenderTask, const Toolkit::Control sourceControl);
- /**
- * @brief Sets shader constants, gaussian kernel weights and pixel offsets.
- * @param[in] downsampledWidth Downsized width of input texture.
- * @param[in] downsampledHeight Downsized height of input texture.
- */
- void SetShaderConstants(uint32_t downsampledWidth, uint32_t downsampledHeight);
-
- /**
- * @brief Get an offset property in std::string format
- * @param[in] index Property's index
- * @return A string for shader
- */
- std::string GetSampleOffsetsPropertyName(unsigned int index) const;
-
- /**
- * @brief Get a weight property in std::string format
- * @param[in] index Property's index
- * @return A string for shader
- */
- std::string GetSampleWeightsPropertyName(unsigned int index) const;
-
/**
* @brief Emits render finished signal of the effect,
* when mBlurOnce is true and finished signal of the last render task(mVerticalBlurTask) is emitted.
float mDownscaleFactor;
uint32_t mBlurRadius;
uint32_t mDownscaledBlurRadius;
- float mBellCurveWidth;
bool mSkipBlur : 1;
bool mBlurOnce : 1;
--- /dev/null
+/*
+ * 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 "gaussian-blur-algorithm.h"
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/shader-integ.h>
+#include <cstdint>
+#include <random>
+#include <vector>
+
+namespace
+{
+static constexpr float MINIMUM_DOWNSCALE_FACTOR = 0.1f;
+static constexpr float MAXIMUM_DOWNSCALE_FACTOR = 1.0f;
+
+static constexpr uint32_t MAXIMUM_BLUR_RADIUS = 500u; ///< Maximum pixel radius for blur effect. (GL_MAX_FRAGMENT_UNIFORM_COMPONENTS(Usually 1024) - 19 (vertex shader used)) / 3 float
+static constexpr uint32_t MAXIMUM_NUMBER_OF_SAMPLES = (MAXIMUM_BLUR_RADIUS >> 1);
+
+static constexpr float MAXIMUM_BELL_CURVE_WIDTH = 171.352f; ///< bell curve width for MAXIMUM_BLUR_RADIUS case
+static constexpr int32_t MAXIMUM_BELL_CURVE_LOOP_TRIAL_COUNT = 50;
+
+/**
+* @brief Calculates gaussian weight
+* @param[in] localOffset Input variable of gaussian distribution
+* @param[in] sigma Standard deviation of gaussian distribution, the width of the "bell"
+* @note Expected value of this gaussian distribution is 0.
+*/
+inline static float CalculateGaussianWeight(float localOffset, float sigma)
+{
+ return (1.0f / (sigma * sqrt(2.0f * Dali::Math::PI))) * exp(-0.5f * (localOffset / sigma * localOffset / sigma));
+}
+
+/**
+* @brief Calculates gaussian bell curve width from given radius.
+* Appropriate bell curve width lets gaussian bell curve reside in a valid range(with non-tail, distinguishable values).
+* @param[in] blurRadius Given radius value of gaussian bell curve.
+* @return Bell curve width of gaussian bell curve.
+*/
+float CalculateBellCurveWidth(uint32_t blurRadius)
+{
+ const float epsilon = 1e-2f / (blurRadius * 2);
+ const float localOffset = (blurRadius * 2) - 1;
+
+ float lowerBoundBellCurveWidth = Dali::Math::MACHINE_EPSILON_10000;
+ float upperBoundBellCurveWidth = MAXIMUM_BELL_CURVE_WIDTH;
+
+ float bellCurveWidth = -1.0f;
+
+ int trialCount = 0;
+ while(trialCount++ < MAXIMUM_BELL_CURVE_LOOP_TRIAL_COUNT && upperBoundBellCurveWidth - lowerBoundBellCurveWidth > Dali::Math::MACHINE_EPSILON_10000)
+ {
+ bellCurveWidth = (lowerBoundBellCurveWidth + upperBoundBellCurveWidth) * 0.5f;
+ if(CalculateGaussianWeight(localOffset, bellCurveWidth) < epsilon)
+ {
+ lowerBoundBellCurveWidth = bellCurveWidth;
+ }
+ else
+ {
+ upperBoundBellCurveWidth = bellCurveWidth;
+ }
+ }
+ return bellCurveWidth;
+}
+
+/**
+* @brief Fills in gaussian kernel vectors of a specific size. Uses 4*numSamples+1 sized gaussian bell curve for sampling.
+* @param[in] numSamples Number of samples. Note that we sample half size of given blur radius, and the curve is symmetric.
+* @param[out] weights Samples from a gaussian bell curve.
+* @param[out] offsets Adjacent pixel offsets to apply gaussian weights.
+*/
+void CalculateGaussianConstants(uint32_t numSamples, std::vector<float>& weights, std::vector<float>& offsets)
+{
+ const float bellCurveWidth = CalculateBellCurveWidth(numSamples);
+
+ const uint32_t kernelSize = numSamples * 4 - 1;
+ const uint32_t halfKernelSize = kernelSize / 2 + 1; // Gaussian curve is symmetric
+
+ // Generate half size kernel
+ std::vector<float> halfSideKernel(halfKernelSize);
+
+ halfSideKernel[0] = CalculateGaussianWeight(0.0f, bellCurveWidth);
+ float totalWeights = halfSideKernel[0];
+ for(unsigned int i = 1; i < halfKernelSize; i++)
+ {
+ float w = CalculateGaussianWeight(i, bellCurveWidth);
+ halfSideKernel[i] = w;
+ totalWeights += w * 2.0f;
+ }
+ for(unsigned int i = 0; i < halfKernelSize; i++)
+ {
+ halfSideKernel[i] /= totalWeights;
+ }
+ halfSideKernel[0] *= 0.5f;
+
+ weights.clear();
+ weights.resize(numSamples);
+ offsets.clear();
+ offsets.resize(numSamples);
+
+ // Compress kernel to half size
+ for(unsigned int i = 0; i < numSamples; i++)
+ {
+ weights[i] = halfSideKernel[2 * i] + halfSideKernel[2 * i + 1];
+ offsets[i] = 2.0f * i + halfSideKernel[2 * i + 1] / weights[i];
+ }
+}
+
+/**
+ * @brief Retrieves precalculated UniformBlock of gaussian kernel. If none, creates new UniformBlock.
+ * @param[in] numSamples Number of samples.
+ * @return UniformBlock for SHADER_BLUR_EFFECT_FRAG
+ */
+inline static Dali::UniformBlock& GetCachedUniformBlock(const uint32_t numSamples)
+{
+ static Dali::UniformBlock gPredefinedUniformBlock[MAXIMUM_NUMBER_OF_SAMPLES + 1u];
+ DALI_ASSERT_DEBUG(numSamples <= MAXIMUM_NUMBER_OF_SAMPLES && "numSamples too big!");
+ return gPredefinedUniformBlock[numSamples];
+}
+
+/**
+ * @brief Retrieves precalculated fragment shader of gaussian kernel. If none, creates new shader.
+ * @param[in] numSamples Number of samples.
+ * @return Fragment shader of gaussian blur.
+ */
+inline static Dali::Shader& GetCachedShader(const uint32_t numSamples)
+{
+ static Dali::Shader gPredefinedShader[MAXIMUM_NUMBER_OF_SAMPLES + 1u];
+ DALI_ASSERT_DEBUG(numSamples <= MAXIMUM_NUMBER_OF_SAMPLES && "numSamples too big!");
+ return gPredefinedShader[numSamples];
+}
+
+} // namespace
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+Dali::Renderer GaussianBlurAlgorithm::CreateRenderer(const uint32_t blurRadius)
+{
+ Dali::Renderer renderer = Dali::Toolkit::Internal::CreateRenderer("", "");
+ renderer.SetShader(GetGaussianBlurShader(blurRadius));
+ renderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true); // Always use premultiplied alpha
+ return renderer;
+}
+
+Dali::Shader& GaussianBlurAlgorithm::GetGaussianBlurShader(const uint32_t blurRadius)
+{
+ uint32_t numSamples = blurRadius >> 1;
+
+ auto& cachedShader = GetCachedShader(numSamples);
+ if(!cachedShader)
+ {
+ auto& cachedUniformBlock = GetCachedUniformBlock(numSamples);
+ if(!cachedUniformBlock)
+ {
+ std::vector<float> weights;
+ std::vector<float> offsets;
+ CalculateGaussianConstants(numSamples, weights, offsets);
+
+ Dali::UniformBlock sharedUBO = Dali::UniformBlock::New("GaussianBlurSampleBlock");
+
+ for(uint32_t i = 0; i < numSamples; i++)
+ {
+ {
+ std::stringstream oss;
+ oss << "uSampleOffsets[" << i << "]";
+ sharedUBO.RegisterProperty(oss.str(), offsets[i]);
+ }
+ {
+ std::stringstream oss;
+ oss << "uSampleWeights[" << i << "]";
+ sharedUBO.RegisterProperty(oss.str(), weights[i]);
+ }
+ }
+ cachedUniformBlock = sharedUBO;
+ }
+
+ std::ostringstream shaderNameBuilder;
+ shaderNameBuilder << "GaussianBlurShader_" << numSamples;
+
+ std::ostringstream fragmentStringStream;
+ fragmentStringStream << "#define NUM_SAMPLES " << numSamples << "\n";
+ fragmentStringStream << SHADER_BLUR_EFFECT_FRAG;
+ std::string fragmentSource(fragmentStringStream.str());
+
+ cachedShader = Dali::Integration::ShaderNewWithUniformBlock(BASIC_VERTEX_SOURCE, fragmentSource.c_str(), Dali::Shader::Hint::FILE_CACHE_SUPPORT, shaderNameBuilder.str(), {cachedUniformBlock});
+ }
+ return cachedShader;
+}
+
+uint32_t GaussianBlurAlgorithm::GetDownscaledBlurRadius(float& downscaleFactor, uint32_t& blurRadius)
+{
+ if(DALI_UNLIKELY(blurRadius > MAXIMUM_BLUR_RADIUS))
+ {
+ const uint32_t fixedBlurRadius = MAXIMUM_BLUR_RADIUS;
+ const float fixedDownScaleFactor = Dali::Clamp(
+ downscaleFactor * static_cast<float>(fixedBlurRadius) / static_cast<float>(blurRadius),
+ MINIMUM_DOWNSCALE_FACTOR,
+ MAXIMUM_DOWNSCALE_FACTOR);
+
+ DALI_LOG_ERROR("Blur radius is out of bound: %u. Use %u and make downscale factor %f to %f.\n",
+ blurRadius,
+ fixedBlurRadius,
+ downscaleFactor,
+ fixedDownScaleFactor);
+
+ downscaleFactor = fixedDownScaleFactor;
+ blurRadius = fixedBlurRadius;
+ }
+ return static_cast<uint32_t>(blurRadius * downscaleFactor);
+}
+
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TOOLKIT_INTERNAL_BLUR_ALGORITHM_H
+#define DALI_TOOLKIT_INTERNAL_BLUR_ALGORITHM_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/debug.h>
+#include <dali/public-api/common/constants.h>
+#include <dali/public-api/math/math-utils.h>
+#include <dali/public-api/rendering/renderer.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/control/control-renderers.h>
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+/**
+ * @brief A utility file to generate gaussian kernel.
+ * @note Every function is static. Do NOT instantiate this class.
+ */
+class GaussianBlurAlgorithm
+{
+public:
+ /**
+ * @brief Creates gaussian blur renderer.
+ * @param[in] blurRadius Blur intensity
+ * @return Gaussian blur renderer
+ */
+ static Dali::Renderer CreateRenderer(const uint32_t blurRadius);
+
+ /**
+ * @brief Get cached fragment shader with given blurRadius.
+ * @param[in] blurRadius Blur intensity
+ * @return Gaussian blur shader
+ */
+ static Dali::Shader& GetGaussianBlurShader(const uint32_t blurRadius);
+
+ /**
+ * @brief Gets blur radius in a downscaled size. If the value is too big, fit arguments in desired range.
+ * @param[in] downscaleFactor Reference value of downscale factor.
+ * @param[in] blurRadius Reference value of blur radius.
+ * @return Downscaled(optimized) blur radius
+ */
+ static uint32_t GetDownscaledBlurRadius(float& downscaleFactor, uint32_t& blurRadius);
+};
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
+#endif // DALI_TOOLKIT_INTERNAL_BLUR_ALGORITHM_H
--- /dev/null
+/*
+ * 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/gaussian-blur-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/animation/key-frames.h>
+#include <dali/public-api/images/image-operations.h>
+#include <dali/public-api/render-tasks/render-task-list.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
+#include <dali-toolkit/public-api/controls/control-impl.h>
+
+namespace
+{
+// Default values
+static constexpr float BLUR_EFFECT_DOWNSCALE_FACTOR = 0.25f;
+static constexpr uint32_t BLUR_EFFECT_BLUR_RADIUS = 10u;
+
+static constexpr uint32_t MINIMUM_GPU_ARRAY_SIZE = 2u; // GPU cannot handle array size smaller than 2.
+
+static constexpr std::string_view UNIFORM_BLUR_STRENGTH_NAME("uAnimationRatio");
+static constexpr std::string_view UNIFORM_BLUR_OPACITY_NAME("uOpacity");
+static constexpr std::string_view UNIFORM_BLUR_OFFSET_DIRECTION_NAME("uOffsetDirection");
+} // namespace
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+#ifdef DEBUG_ENABLED
+extern Debug::Filter* gRenderEffectLogFilter; ///< Define at render-effect-impl.cpp
+#endif
+
+GaussianBlurEffectImpl::GaussianBlurEffectImpl()
+: RenderEffectImpl(),
+ mInternalRoot(Actor::New()),
+ mDownscaleFactor(BLUR_EFFECT_DOWNSCALE_FACTOR),
+ mBlurRadius(BLUR_EFFECT_BLUR_RADIUS),
+ mDownscaledBlurRadius(static_cast<uint32_t>(BLUR_EFFECT_BLUR_RADIUS * BLUR_EFFECT_DOWNSCALE_FACTOR)),
+ mSkipBlur(false),
+ mBlurOnce(false)
+{
+}
+
+GaussianBlurEffectImpl::GaussianBlurEffectImpl(uint32_t blurRadius)
+: RenderEffectImpl(),
+ mInternalRoot(Actor::New()),
+ mDownscaleFactor(BLUR_EFFECT_DOWNSCALE_FACTOR),
+ mBlurRadius(blurRadius),
+ mDownscaledBlurRadius(BLUR_EFFECT_BLUR_RADIUS),
+ mSkipBlur(false),
+ mBlurOnce(false)
+{
+ mDownscaledBlurRadius = GaussianBlurAlgorithm::GetDownscaledBlurRadius(mDownscaleFactor, mBlurRadius);
+ if(DALI_UNLIKELY((mDownscaledBlurRadius >> 1) < MINIMUM_GPU_ARRAY_SIZE))
+ {
+ mSkipBlur = true;
+ DALI_LOG_ERROR("Blur radius is too small. This blur will be ignored.\n");
+ }
+}
+
+GaussianBlurEffectImpl::~GaussianBlurEffectImpl()
+{
+}
+
+GaussianBlurEffectImplPtr GaussianBlurEffectImpl::New()
+{
+ GaussianBlurEffectImplPtr handle = new GaussianBlurEffectImpl();
+ handle->Initialize();
+ return handle;
+}
+
+GaussianBlurEffectImplPtr GaussianBlurEffectImpl::New(uint32_t blurRadius)
+{
+ GaussianBlurEffectImplPtr handle = new GaussianBlurEffectImpl(blurRadius);
+ handle->Initialize();
+ return handle;
+}
+
+OffScreenRenderable::Type GaussianBlurEffectImpl::GetOffScreenRenderableType()
+{
+ return mSkipBlur ? OffScreenRenderable::NONE : OffScreenRenderable::FORWARD;
+}
+
+void GaussianBlurEffectImpl::GetOffScreenRenderTasks(std::vector<Dali::RenderTask>& tasks, bool isForward)
+{
+}
+
+void GaussianBlurEffectImpl::SetBlurOnce(bool blurOnce)
+{
+ mBlurOnce = blurOnce;
+
+ if(!mSkipBlur && IsActivated()) // if false, no render task exists yet(nothing to do)
+ {
+ if(mBlurOnce)
+ {
+ mSourceRenderTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
+ mHorizontalBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
+ mVerticalBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
+
+ mVerticalBlurTask.FinishedSignal().Connect(this, &GaussianBlurEffectImpl::OnRenderFinished);
+ }
+ else
+ {
+ mSourceRenderTask.SetRefreshRate(RenderTask::REFRESH_ALWAYS);
+ mHorizontalBlurTask.SetRefreshRate(RenderTask::REFRESH_ALWAYS);
+ mVerticalBlurTask.SetRefreshRate(RenderTask::REFRESH_ALWAYS);
+ }
+ }
+}
+
+bool GaussianBlurEffectImpl::GetBlurOnce() const
+{
+ return mBlurOnce;
+}
+
+void GaussianBlurEffectImpl::SetBlurRadius(uint32_t blurRadius)
+{
+ if(mBlurRadius != blurRadius)
+ {
+ if(!mSkipBlur && IsActivated())
+ {
+ OnDeactivate();
+ }
+
+ // Reinitialize blur parameters
+ mDownscaledBlurRadius = BLUR_EFFECT_DOWNSCALE_FACTOR;
+ mBlurRadius = blurRadius;
+ mDownscaledBlurRadius = GaussianBlurAlgorithm::GetDownscaledBlurRadius(mDownscaleFactor, mBlurRadius);
+
+ mSkipBlur = false;
+ if(DALI_UNLIKELY((mDownscaledBlurRadius >> 1) < MINIMUM_GPU_ARRAY_SIZE))
+ {
+ mSkipBlur = true;
+ DALI_LOG_ERROR("Blur radius is too small. This blur will be ignored.\n");
+ }
+
+ OnInitialize();
+
+ if(!mSkipBlur && IsActivated())
+ {
+ OnActivate();
+ }
+ }
+}
+
+uint32_t GaussianBlurEffectImpl::GetBlurRadius() const
+{
+ return mBlurRadius;
+}
+
+void GaussianBlurEffectImpl::AddBlurStrengthAnimation(Animation& animation, AlphaFunction alphaFunction, TimePeriod timePeriod, float fromValue, float toValue)
+{
+ if(DALI_UNLIKELY(mSkipBlur))
+ {
+ DALI_LOG_ERROR("Blur radius is too small. Blur animation will be ignored.");
+ return;
+ }
+ if(mBlurOnce)
+ {
+ DALI_LOG_ERROR("This blur effect is set to render only once, so the animation will be ignored. Call SetBlurOnce(false) to render it every frame.");
+ return;
+ }
+
+ fromValue = std::clamp(fromValue, 0.0f, 1.0f);
+ toValue = std::clamp(toValue, 0.0f, 1.0f);
+
+ KeyFrames keyFrames = KeyFrames::New();
+ keyFrames.Add(0.0f, fromValue, AlphaFunction::BuiltinFunction::LINEAR);
+ keyFrames.Add(1.0f, toValue, AlphaFunction::BuiltinFunction::LINEAR);
+
+ Property::Index horizontalAnimationIndex = mHorizontalBlurActor.GetPropertyIndex(UNIFORM_BLUR_STRENGTH_NAME.data());
+ animation.AnimateBetween(Property(mHorizontalBlurActor, horizontalAnimationIndex), keyFrames, alphaFunction, timePeriod);
+ Property::Index verticalAnimationIndex = mVerticalBlurActor.GetPropertyIndex(UNIFORM_BLUR_STRENGTH_NAME.data());
+ animation.AnimateBetween(Property(mVerticalBlurActor, verticalAnimationIndex), keyFrames, alphaFunction, timePeriod);
+}
+
+void GaussianBlurEffectImpl::AddBlurOpacityAnimation(Animation& animation, AlphaFunction alphaFunction, TimePeriod timePeriod, float fromValue, float toValue)
+{
+ if(DALI_UNLIKELY(mSkipBlur))
+ {
+ DALI_LOG_ERROR("Blur radius is too small. Blur animation will be ignored.");
+ return;
+ }
+ if(mBlurOnce)
+ {
+ DALI_LOG_ERROR("This blur effect is set to render only once, so the animation will be ignored. Call SetBlurOnce(false) to render it every frame.");
+ return;
+ }
+
+ fromValue = std::clamp(fromValue, 0.0f, 1.0f);
+ toValue = std::clamp(toValue, 0.0f, 1.0f);
+
+ KeyFrames keyFrames = KeyFrames::New();
+ keyFrames.Add(0.0f, fromValue, AlphaFunction::BuiltinFunction::LINEAR);
+ keyFrames.Add(1.0f, toValue, AlphaFunction::BuiltinFunction::LINEAR);
+
+ Property::Index horizontalAnimationIndex = mHorizontalBlurActor.GetPropertyIndex(UNIFORM_BLUR_OPACITY_NAME.data());
+ animation.AnimateBetween(Property(mHorizontalBlurActor, horizontalAnimationIndex), keyFrames, alphaFunction, timePeriod);
+ Property::Index verticalAnimationIndex = mVerticalBlurActor.GetPropertyIndex(UNIFORM_BLUR_OPACITY_NAME.data());
+ animation.AnimateBetween(Property(mVerticalBlurActor, verticalAnimationIndex), keyFrames, alphaFunction, timePeriod);
+}
+
+void GaussianBlurEffectImpl::OnInitialize()
+{
+ if(DALI_UNLIKELY(mSkipBlur))
+ {
+ return;
+ }
+
+ // Create blur actors
+ {
+ mInternalRoot.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+
+ // Create an actor for performing a vertical blur on the texture
+ Renderer horizontalBlurRenderer = GaussianBlurAlgorithm::CreateRenderer(mDownscaledBlurRadius);
+ if(mHorizontalBlurActor)
+ {
+ mHorizontalBlurActor.RemoveRenderer(0u);
+ mHorizontalBlurActor.AddRenderer(horizontalBlurRenderer);
+ }
+ else
+ {
+ mHorizontalBlurActor = Actor::New();
+ mHorizontalBlurActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+ mHorizontalBlurActor.AddRenderer(horizontalBlurRenderer);
+ mInternalRoot.Add(mHorizontalBlurActor);
+
+ mHorizontalBlurActor.RegisterProperty(UNIFORM_BLUR_OPACITY_NAME.data(), 1.0f);
+ mHorizontalBlurActor.RegisterProperty(UNIFORM_BLUR_STRENGTH_NAME.data(), 1.0f);
+ }
+
+ // Create an actor for performing a vertical blur on the texture
+ Renderer verticalBlurRenderer = GaussianBlurAlgorithm::CreateRenderer(mDownscaledBlurRadius);
+ if(mVerticalBlurActor)
+ {
+ mVerticalBlurActor.RemoveRenderer(0u);
+ mVerticalBlurActor.AddRenderer(verticalBlurRenderer);
+ }
+ else
+ {
+ mVerticalBlurActor = Actor::New();
+ mVerticalBlurActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+ mVerticalBlurActor.AddRenderer(verticalBlurRenderer);
+ mInternalRoot.Add(mVerticalBlurActor);
+
+ mVerticalBlurActor.RegisterProperty(UNIFORM_BLUR_OPACITY_NAME.data(), 1.0f);
+ mVerticalBlurActor.RegisterProperty(UNIFORM_BLUR_STRENGTH_NAME.data(), 1.0f);
+ }
+ }
+}
+
+void GaussianBlurEffectImpl::OnActivate()
+{
+ if(DALI_UNLIKELY(mSkipBlur))
+ {
+ return;
+ }
+
+ Toolkit::Control ownerControl = GetOwnerControl();
+ DALI_ASSERT_ALWAYS(ownerControl && "Set the owner of RenderEffect before you activate.");
+
+ // Get size
+ Vector2 size = GetTargetSize();
+ DALI_LOG_INFO(gRenderEffectLogFilter, Debug::General, "[BlurEffect:%p] OnActivated! [ID:%d][size:%fx%f]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1, size.x, size.y);
+
+ uint32_t downsampledWidth = std::max(static_cast<uint32_t>(size.width * mDownscaleFactor), 1u);
+ uint32_t downsampledHeight = std::max(static_cast<uint32_t>(size.height * mDownscaleFactor), 1u);
+
+ // Set size
+ if(!mCamera)
+ {
+ mCamera = CameraActor::New();
+ mCamera.SetInvertYAxis(true);
+ mCamera.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+ mCamera.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+ mCamera.SetType(Dali::Camera::FREE_LOOK);
+ mInternalRoot.Add(mCamera);
+ }
+ mCamera.SetPerspectiveProjection(GetTargetSize());
+
+ if(!mRenderDownsampledCamera)
+ {
+ mRenderDownsampledCamera = CameraActor::New();
+ mRenderDownsampledCamera.SetInvertYAxis(true);
+ mRenderDownsampledCamera.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+ mRenderDownsampledCamera.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+ mRenderDownsampledCamera.SetType(Dali::Camera::FREE_LOOK);
+ mInternalRoot.Add(mRenderDownsampledCamera);
+ }
+ mRenderDownsampledCamera.SetPerspectiveProjection(Size(downsampledWidth, downsampledHeight));
+
+ mHorizontalBlurActor.SetProperty(Actor::Property::SIZE, Vector2(downsampledWidth, downsampledHeight));
+ mVerticalBlurActor.SetProperty(Actor::Property::SIZE, Vector2(downsampledWidth, downsampledHeight));
+
+ // Set blur
+ CreateFrameBuffers(ImageDimensions(downsampledWidth, downsampledHeight));
+ CreateRenderTasks(GetSceneHolder(), ownerControl);
+
+ // Reset shader constants
+ auto& blurShader = GaussianBlurAlgorithm::GetGaussianBlurShader(mDownscaledBlurRadius);
+ {
+ Renderer renderer = mHorizontalBlurActor.GetRendererAt(0u);
+ renderer.SetShader(blurShader);
+ renderer.RegisterUniqueProperty(UNIFORM_BLUR_OFFSET_DIRECTION_NAME.data(), Vector2(1.0f / downsampledWidth, 0.0f));
+ }
+ {
+ Renderer renderer = mVerticalBlurActor.GetRendererAt(0u);
+ renderer.SetShader(blurShader);
+ renderer.RegisterUniqueProperty(UNIFORM_BLUR_OFFSET_DIRECTION_NAME.data(), Vector2(0.0f, 1.0f / downsampledHeight));
+ }
+
+ // Inject blurred output to control
+ Renderer renderer = GetTargetRenderer();
+ renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Dali::Toolkit::DepthIndex::FOREGROUND_EFFECT);
+ ownerControl.GetImplementation().SetCacheRenderer(renderer);
+ ownerControl.GetImplementation().RegisterOffScreenRenderableType(OffScreenRenderable::Type::FORWARD);
+ SetRendererTexture(renderer, mBlurredOutputFrameBuffer);
+
+ ownerControl.Add(mInternalRoot);
+
+ // Reorder render task
+ // TODO : Can we remove this GetImplementation?
+ GetImplementation(ownerControl).RequestRenderTaskReorder();
+}
+
+void GaussianBlurEffectImpl::OnDeactivate()
+{
+ if(DALI_UNLIKELY(mSkipBlur))
+ {
+ return;
+ }
+
+ auto ownerControl = GetOwnerControl();
+ if(DALI_LIKELY(ownerControl))
+ {
+ ownerControl.GetImplementation().RemoveCacheRenderer();
+ ownerControl.GetImplementation().UnregisterOffScreenRenderableType(OffScreenRenderable::Type::FORWARD);
+ }
+ DALI_LOG_INFO(gRenderEffectLogFilter, Debug::General, "[BlurEffect:%p] OnDeactivated! [ID:%d]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1);
+
+ mInternalRoot.Unparent();
+
+ DestroyFrameBuffers();
+ DestroyRenderTasks();
+}
+
+void GaussianBlurEffectImpl::OnRefresh()
+{
+ if(DALI_UNLIKELY(mSkipBlur))
+ {
+ return;
+ }
+
+ mInputFrameBuffer.Reset();
+ mTemporaryFrameBuffer.Reset();
+ mBlurredOutputFrameBuffer.Reset();
+
+ Vector2 size = GetTargetSize();
+ uint32_t downsampledWidth = std::max(static_cast<uint32_t>(size.width * mDownscaleFactor), 1u);
+ uint32_t downsampledHeight = std::max(static_cast<uint32_t>(size.height * mDownscaleFactor), 1u);
+
+ // Set size
+ mCamera.SetPerspectiveProjection(size);
+ mRenderDownsampledCamera.SetPerspectiveProjection(Size(downsampledWidth, downsampledHeight));
+ mHorizontalBlurActor.SetProperty(Actor::Property::SIZE, Vector2(downsampledWidth, downsampledHeight));
+ mVerticalBlurActor.SetProperty(Actor::Property::SIZE, Vector2(downsampledWidth, downsampledHeight));
+
+ CreateFrameBuffers(ImageDimensions(downsampledWidth, downsampledHeight));
+
+ // Reset shader constants
+ mHorizontalBlurActor.RegisterProperty(UNIFORM_BLUR_OFFSET_DIRECTION_NAME.data(), Vector2(1.0f, 0.0f) / downsampledWidth);
+ mVerticalBlurActor.RegisterProperty(UNIFORM_BLUR_OFFSET_DIRECTION_NAME.data(), Vector2(0.0f, 1.0f) / downsampledHeight);
+
+ mSourceRenderTask.SetFrameBuffer(mInputFrameBuffer);
+ mHorizontalBlurTask.SetFrameBuffer(mTemporaryFrameBuffer);
+ mVerticalBlurTask.SetFrameBuffer(mBlurredOutputFrameBuffer);
+}
+
+void GaussianBlurEffectImpl::CreateFrameBuffers(const ImageDimensions downsampledSize)
+{
+ uint32_t downsampledWidth = downsampledSize.GetWidth();
+ uint32_t downsampledHeight = downsampledSize.GetHeight();
+
+ // buffer to draw input texture
+ mInputFrameBuffer = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::DEPTH_STENCIL);
+ Texture inputBackgroundTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, downsampledWidth, downsampledHeight);
+ mInputFrameBuffer.AttachColorTexture(inputBackgroundTexture);
+
+ // buffer to draw half-blurred output
+ mTemporaryFrameBuffer = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::DEPTH_STENCIL);
+ Texture temporaryTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, downsampledWidth, downsampledHeight);
+ mTemporaryFrameBuffer.AttachColorTexture(temporaryTexture);
+
+ // buffer to draw blurred output
+ mBlurredOutputFrameBuffer = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::DEPTH_STENCIL);
+ Texture sourceTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, downsampledWidth, downsampledHeight);
+ mBlurredOutputFrameBuffer.AttachColorTexture(sourceTexture);
+}
+
+void GaussianBlurEffectImpl::DestroyFrameBuffers()
+{
+ mInputFrameBuffer.Reset();
+ mTemporaryFrameBuffer.Reset();
+ mBlurredOutputFrameBuffer.Reset();
+}
+
+void GaussianBlurEffectImpl::CreateRenderTasks(Integration::SceneHolder sceneHolder, const Toolkit::Control sourceControl)
+{
+ RenderTaskList taskList = sceneHolder.GetRenderTaskList();
+
+ // draw input texture
+ mSourceRenderTask = taskList.CreateTask();
+ mSourceRenderTask.SetSourceActor(sourceControl); // -> should use cache renderer
+ mSourceRenderTask.SetExclusive(true);
+ mSourceRenderTask.SetInputEnabled(true);
+ mSourceRenderTask.SetScreenToFrameBufferMappingActor(mHorizontalBlurActor);
+ mSourceRenderTask.SetCameraActor(mCamera);
+ mSourceRenderTask.SetFrameBuffer(mInputFrameBuffer);
+
+ // Clear inputBackgroundTexture as scene holder
+ mSourceRenderTask.SetClearEnabled(true);
+ mSourceRenderTask.SetClearColor(Color::TRANSPARENT);
+
+ // draw half-blurred output
+ SetRendererTexture(mHorizontalBlurActor.GetRendererAt(0), mInputFrameBuffer);
+ mHorizontalBlurTask = taskList.CreateTask();
+ mHorizontalBlurTask.SetSourceActor(mHorizontalBlurActor);
+ mHorizontalBlurTask.SetExclusive(true);
+ mHorizontalBlurTask.SetInputEnabled(true);
+ mHorizontalBlurTask.SetScreenToFrameBufferMappingActor(mVerticalBlurActor);
+ mHorizontalBlurTask.SetCameraActor(mRenderDownsampledCamera);
+ mHorizontalBlurTask.SetFrameBuffer(mTemporaryFrameBuffer);
+
+ // Clear temporaryTexture as Transparent.
+ mHorizontalBlurTask.SetClearEnabled(true);
+ mHorizontalBlurTask.SetClearColor(Color::TRANSPARENT);
+
+ // draw blurred output
+ SetRendererTexture(mVerticalBlurActor.GetRendererAt(0), mTemporaryFrameBuffer);
+ mVerticalBlurTask = taskList.CreateTask();
+ mVerticalBlurTask.SetSourceActor(mVerticalBlurActor);
+ mVerticalBlurTask.SetExclusive(true);
+ mVerticalBlurTask.SetInputEnabled(true);
+ mVerticalBlurTask.SetScreenToFrameBufferMappingActor(sourceControl);
+ mVerticalBlurTask.SetCameraActor(mRenderDownsampledCamera);
+ mVerticalBlurTask.SetFrameBuffer(mBlurredOutputFrameBuffer);
+
+ // Clear sourceTexture as Transparent.
+ mVerticalBlurTask.SetClearEnabled(true);
+ mVerticalBlurTask.SetClearColor(Color::TRANSPARENT);
+
+ // Adjust refresh rate
+ if(mBlurOnce)
+ {
+ mSourceRenderTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
+ mHorizontalBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
+ mVerticalBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
+
+ mVerticalBlurTask.FinishedSignal().Connect(this, &GaussianBlurEffectImpl::OnRenderFinished);
+ }
+ else
+ {
+ mSourceRenderTask.SetRefreshRate(RenderTask::REFRESH_ALWAYS);
+ mHorizontalBlurTask.SetRefreshRate(RenderTask::REFRESH_ALWAYS);
+ mVerticalBlurTask.SetRefreshRate(RenderTask::REFRESH_ALWAYS);
+ }
+}
+
+void GaussianBlurEffectImpl::OnRenderFinished(Dali::RenderTask& renderTask)
+{
+ mFinishedSignal.Emit();
+}
+
+void GaussianBlurEffectImpl::DestroyRenderTasks()
+{
+ auto sceneHolder = GetSceneHolder();
+ if(DALI_LIKELY(sceneHolder))
+ {
+ RenderTaskList taskList = sceneHolder.GetRenderTaskList();
+ taskList.RemoveTask(mHorizontalBlurTask);
+ taskList.RemoveTask(mVerticalBlurTask);
+ taskList.RemoveTask(mSourceRenderTask);
+ }
+
+ mHorizontalBlurTask.Reset();
+ mVerticalBlurTask.Reset();
+ mSourceRenderTask.Reset();
+}
+
+Dali::Toolkit::GaussianBlurEffect::FinishedSignalType& GaussianBlurEffectImpl::FinishedSignal()
+{
+ return mFinishedSignal;
+}
+
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TOOLKIT_INTERNAL_BLUR_EFFECT_H
+#define DALI_TOOLKIT_INTERNAL_BLUR_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/object/weak-handle.h>
+#include <dali/public-api/render-tasks/render-task.h>
+#include <dali/public-api/rendering/frame-buffer.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/render-effects/gaussian-blur-algorithm.h>
+#include <dali-toolkit/internal/controls/render-effects/render-effect-impl.h>
+#include <dali-toolkit/public-api/controls/render-effects/gaussian-blur-effect.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+class GaussianBlurEffectImpl;
+using GaussianBlurEffectImplPtr = IntrusivePtr<GaussianBlurEffectImpl>;
+
+class GaussianBlurEffectImpl : public RenderEffectImpl
+{
+public:
+ /**
+ * @brief Creates an initialized BlurEffect implementation, using default settings. As default, blur radius is set to 10u.
+ * @return A handle to a newly allocated Dali resource
+ */
+
+ static GaussianBlurEffectImplPtr New();
+
+ /**
+ * @brief Creates an initialized BlurEffect implementation.
+ *
+ * @param[in] blurRadius The radius of Gaussian kernel.
+ * @return A handle to a newly allocated Dali resource
+ */
+ static GaussianBlurEffectImplPtr New(uint32_t blurRadius);
+
+ /**
+ * @copydoc Toolkit::Internal::RenderEffectImpl::GetOffScreenRenderableType
+ */
+ OffScreenRenderable::Type GetOffScreenRenderableType() override;
+
+ /**
+ * @copydoc Toolkit::Internal::RenderEffectImpl::GetOffScreenRenderTasks
+ */
+ void GetOffScreenRenderTasks(std::vector<Dali::RenderTask>& tasks, bool isForward) override;
+
+ /**
+ * @copydoc Toolkit::GaussianBlurEffect::SetBlurOnce
+ */
+ void SetBlurOnce(bool blurOnce);
+
+ /**
+ * @copydoc Toolkit::GaussianBlurEffect::GetBlurOnce
+ */
+ bool GetBlurOnce() const;
+
+ /**
+ * @copydoc Toolkit::GaussianBlurEffect::SetBlurRadius
+ */
+ void SetBlurRadius(uint32_t blurRadius);
+
+ /**
+ * @copydoc Toolkit::GaussianBlurEffect::GetBlurRadius
+ */
+ uint32_t GetBlurRadius() const;
+
+ /**
+ * @copydoc Toolkit::GaussianBlurEffect::AddBlurStrengthAnimation
+ */
+ void AddBlurStrengthAnimation(Animation& animation, AlphaFunction alphaFunction, TimePeriod timePeriod, float fromValue, float toValue);
+
+ /**
+ * @copydoc Toolkit::GaussianBlurEffect::AddBlurOpacityAnimation
+ */
+ void AddBlurOpacityAnimation(Animation& animation, AlphaFunction alphaFunction, TimePeriod timePeriod, float fromValue, float toValue);
+
+ /**
+ * @copydoc Toolkit::GaussianBlurEffect::FinishedSignal
+ */
+ Dali::Toolkit::GaussianBlurEffect::FinishedSignalType& FinishedSignal();
+
+protected:
+ /**
+ * @brief Creates an uninitialized blur effect implementation
+ */
+ GaussianBlurEffectImpl();
+
+ /**
+ * @brief Creates an uninitialized blur effect implementation
+ * @param[in] blurRadius The radius of Gaussian kernel.
+ */
+ GaussianBlurEffectImpl(uint32_t blurRadius);
+
+ /**
+ * @brief Destructor
+ */
+ virtual ~GaussianBlurEffectImpl();
+
+ /**
+ * @brief Initializes blur effect
+ */
+ void OnInitialize() override;
+
+ /**
+ * @brief Activates blur effect
+ */
+ void OnActivate() override;
+
+ /**
+ * @brief Dectivates blur effect
+ */
+ void OnDeactivate() override;
+
+ /**
+ * @brief Redraw effect without deactivation
+ */
+ void OnRefresh() override;
+
+private:
+ // Inner functions
+ /**
+ * @brief Sets frame buffers to draw blurred output.
+ * @param[in] downsampledSize Downsampled size for performance.
+ */
+ void CreateFrameBuffers(const ImageDimensions downsampledSize);
+
+ /**
+ * @brief Removes and destroys local frame buffers.
+ */
+ void DestroyFrameBuffers();
+
+ /**
+ * @brief Sets blur render tasks.
+ * Requires initialized buffers, source actors, and source cameras.
+ * @param[in] sceneHolder SceneHolder of source control
+ * @param[in] sourceControl Input source control
+ */
+ void CreateRenderTasks(Integration::SceneHolder sceneHolder, const Toolkit::Control sourceControl);
+
+ /**
+ * @brief Removes and destroys local render tasks.
+ */
+ void DestroyRenderTasks();
+
+ /**
+ * @brief Apply render tasks source actor, and some other options.
+ * @param[in] renderTask Target render task to change source actor and exclusiveness
+ * @param[in] sourceControl Input source control
+ */
+ void ApplyRenderTaskSourceActor(RenderTask sourceRenderTask, const Toolkit::Control sourceControl);
+
+ /**
+ * @brief Emits render finished signal of the effect,
+ * when mBlurOnce is true and finished signal of the last render task(mVerticalBlurTask) is emitted.
+ * @param[in] renderTask that emits source signal.
+ */
+ void OnRenderFinished(Dali::RenderTask& renderTask);
+
+ GaussianBlurEffectImpl(const GaussianBlurEffectImpl&) = delete;
+ GaussianBlurEffectImpl(GaussianBlurEffectImpl&&) = delete;
+ GaussianBlurEffectImpl& operator=(GaussianBlurEffectImpl&&) = delete; // no move()
+ GaussianBlurEffectImpl& operator=(const GaussianBlurEffectImpl&) = delete; // no copy()
+
+public:
+ Dali::Toolkit::GaussianBlurEffect::FinishedSignalType mFinishedSignal; // Emits when blur once is enabled
+
+private:
+ // Camera actors
+ CameraActor mCamera;
+ CameraActor mRenderDownsampledCamera;
+
+ // Resource
+ FrameBuffer mInputFrameBuffer; // Input. Background. What to blur.
+
+ Actor mInternalRoot;
+ Actor mHorizontalBlurActor;
+ RenderTask mHorizontalBlurTask;
+ FrameBuffer mTemporaryFrameBuffer;
+ Actor mVerticalBlurActor;
+ RenderTask mVerticalBlurTask;
+
+ FrameBuffer mBlurredOutputFrameBuffer;
+ RenderTask mSourceRenderTask;
+
+ // Variables
+ float mDownscaleFactor;
+ uint32_t mBlurRadius;
+ uint32_t mDownscaledBlurRadius;
+
+ bool mSkipBlur : 1;
+ bool mBlurOnce : 1;
+};
+} // namespace Internal
+
+inline Toolkit::Internal::GaussianBlurEffectImpl& GetImplementation(Toolkit::GaussianBlurEffect& obj)
+{
+ BaseObject& handle = obj.GetBaseObject();
+ return static_cast<Toolkit::Internal::GaussianBlurEffectImpl&>(handle);
+}
+
+inline const Toolkit::Internal::GaussianBlurEffectImpl& GetImplementation(const Toolkit::GaussianBlurEffect& obj)
+{
+ const BaseObject& handle = obj.GetBaseObject();
+ return static_cast<const Toolkit::Internal::GaussianBlurEffectImpl&>(handle);
+}
+
+} // namespace Toolkit
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_BLUR_EFFECT_H
${toolkit_src_dir}/controls/alignment/alignment-impl.cpp
${toolkit_src_dir}/controls/render-effects/render-effect-impl.cpp
${toolkit_src_dir}/controls/render-effects/mask-effect-impl.cpp
+ ${toolkit_src_dir}/controls/render-effects/gaussian-blur-algorithm.cpp
${toolkit_src_dir}/controls/render-effects/background-blur-effect-impl.cpp
+ ${toolkit_src_dir}/controls/render-effects/gaussian-blur-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
INPUT highp vec2 vTexCoord;
UNIFORM sampler2D sTexture;
+
UNIFORM_BLOCK FragBlock
{
UNIFORM highp float uAnimationRatio;
UNIFORM highp float uOpacity;
- UNIFORM highp vec2 uSampleOffsets[NUM_SAMPLES];
+ UNIFORM highp vec2 uOffsetDirection;
+};
+
+UNIFORM_BLOCK GaussianBlurSampleBlock
+{
+ UNIFORM highp float uSampleOffsets[NUM_SAMPLES];
UNIFORM highp float uSampleWeights[NUM_SAMPLES];
};
void main()
{
highp vec4 col = vec4(0.0);
+
for (int i=0; i<NUM_SAMPLES; ++i)
{
- col += (TEXTURE(sTexture, vTexCoord + uSampleOffsets[i] * uAnimationRatio) + TEXTURE(sTexture, vTexCoord - uSampleOffsets[i] * uAnimationRatio)) * uSampleWeights[i];
+ col += (TEXTURE(sTexture, vTexCoord + uSampleOffsets[i] * uOffsetDirection * uAnimationRatio) + TEXTURE(sTexture, vTexCoord - uSampleOffsets[i] * uOffsetDirection * uAnimationRatio)) * uSampleWeights[i];
}
col *= uOpacity;
gl_FragColor = col;
-
/*
* Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
{
class BackgroundBlurEffectImpl;
} // namespace DALI_INTERNAL
+
/**
* @brief BackgroundBlurEffect is a visual effect that blurs owner control's background.
- * 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(BackgroundBlurEffect::New()); // Activate
- * ...
+ * @code
+ * BackgroundBlurEffect effect = BackgroundBlurEffect::New();
+ * control.SetRenderEffect(effect); // Activate
+ * effect.Deactivate();
+ * effect.Activate();
* control.ClearRenderEffect(); // Deactivate
+ * @endcode
*
- * Note that tree hierarchy matters for BackgroundBlurEffect. You should determine "what is the background".
- * Add() should preceed SetRenderEffect(), and the effect cannot have multiple owner controls.
+ * @note The owner control owns at most one render effect.
+ * @note Tree hierarchy matters for BackgroundBlurEffect. You should determine "what is the background".
*
* @SINCE_2_3.28
*/
--- /dev/null
+/*
+ * 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/gaussian-blur-effect.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/render-effects/gaussian-blur-effect-impl.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+GaussianBlurEffect::GaussianBlurEffect() = default;
+
+GaussianBlurEffect::GaussianBlurEffect(const GaussianBlurEffect& handle)
+: RenderEffect(handle)
+{
+}
+
+GaussianBlurEffect::GaussianBlurEffect(Internal::GaussianBlurEffectImpl* GaussianBlurEffectImpl)
+: RenderEffect(GaussianBlurEffectImpl)
+{
+}
+
+GaussianBlurEffect::~GaussianBlurEffect() = default;
+
+GaussianBlurEffect GaussianBlurEffect::New()
+{
+ Internal::GaussianBlurEffectImplPtr internal = Internal::GaussianBlurEffectImpl::New();
+ return GaussianBlurEffect(internal.Get());
+}
+
+GaussianBlurEffect GaussianBlurEffect::New(uint32_t blurRadius)
+{
+ Internal::GaussianBlurEffectImplPtr internal = Internal::GaussianBlurEffectImpl::New(blurRadius);
+ return GaussianBlurEffect(internal.Get());
+}
+
+void GaussianBlurEffect::SetBlurOnce(bool blurOnce)
+{
+ GetImplementation(*this).SetBlurOnce(blurOnce);
+}
+
+bool GaussianBlurEffect::GetBlurOnce() const
+{
+ return GetImplementation(*this).GetBlurOnce();
+}
+
+void GaussianBlurEffect::SetBlurRadius(uint32_t blurRadius)
+{
+ GetImplementation(*this).SetBlurRadius(blurRadius);
+}
+
+uint32_t GaussianBlurEffect::GetBlurRadius() const
+{
+ return GetImplementation(*this).GetBlurRadius();
+}
+
+void GaussianBlurEffect::AddBlurStrengthAnimation(Animation& animation, AlphaFunction alphaFunction, TimePeriod timePeriod, float fromValue, float toValue)
+{
+ GetImplementation(*this).AddBlurStrengthAnimation(animation, alphaFunction, timePeriod, fromValue, toValue);
+}
+
+void GaussianBlurEffect::AddBlurOpacityAnimation(Animation& animation, AlphaFunction alphaFunction, TimePeriod timePeriod, float fromValue, float toValue)
+{
+ GetImplementation(*this).AddBlurOpacityAnimation(animation, alphaFunction, timePeriod, fromValue, toValue);
+}
+
+GaussianBlurEffect::FinishedSignalType& GaussianBlurEffect::FinishedSignal()
+{
+ return GetImplementation(*this).FinishedSignal();
+}
+
+} // namespace Toolkit
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TOOLKIT_BLUR_EFFECT_H
+#define DALI_TOOLKIT_BLUR_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>
+// EXTERNAL INCLUDES
+#include <dali/public-api/animation/alpha-function.h>
+#include <dali/public-api/animation/time-period.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal DALI_INTERNAL
+{
+class GaussianBlurEffectImpl;
+} // namespace DALI_INTERNAL
+
+/**
+ * @brief GaussianBlurEffect is a visual effect that blurs owner control and its children.
+ *
+ * @code
+ * GaussianBlurEffect effect = GaussianBlurEffect::New();
+ * control.SetRenderEffect(effect); // Activate
+ * effect.Deactivate();
+ * effect.Activate();
+ * control.ClearRenderEffect(); // Deactivate
+ * @endcode
+ *
+ * @SINCE_2_4.20
+ */
+class DALI_TOOLKIT_API GaussianBlurEffect : public RenderEffect
+{
+public:
+ /**
+ * @brief Typedef for finished signals sent by this class.
+ * @SINCE_2_4.20
+ */
+ typedef Signal<void()> FinishedSignalType;
+
+ /**
+ * @brief Creates an initialized GaussianBlurEffect, with default blur radius 10u.
+ * @SINCE_2_4.20
+ * @return A handle to a newly allocated Dali resource
+ */
+ static GaussianBlurEffect New();
+
+ /**
+ * @brief Creates an initialized GaussianBlurEffect.
+ * @param[in] blurRadius The radius of Gaussian kernel.
+ * @SINCE_2_4.20
+ * @return A handle to a newly allocated Dali resource
+ */
+ static GaussianBlurEffect New(uint32_t blurRadius);
+
+ /**
+ * @brief Creates an uninitialized blur effect.
+ * @SINCE_2_4.20
+ */
+ GaussianBlurEffect();
+
+ /**
+ * @brief Copy constructor
+ * @SINCE_2_4.20
+ */
+ GaussianBlurEffect(const GaussianBlurEffect& handle);
+
+ /**
+ * @brief Destructor
+ * @SINCE_2_4.20
+ */
+ ~GaussianBlurEffect();
+
+ /**
+ * @copydoc Dali::Toolkit::BackgroundBlurEffect::SetBlurOnce
+ */
+ void SetBlurOnce(bool blurOnce);
+
+ /**
+ * @copydoc Dali::Toolkit::BackgroundBlurEffect::GetBlurOnce
+ */
+ bool GetBlurOnce() const;
+
+ /**
+ * @copydoc Dali::Toolkit::BackgroundBlurEffect::GetBlurOnce
+ */
+ void SetBlurRadius(uint32_t blurRadius);
+
+ /**
+ * @copydoc Dali::Toolkit::BackgroundBlurEffect::GetBlurOnce
+ */
+ uint32_t GetBlurRadius() const;
+
+ /**
+ * @copydoc Dali::Toolkit::BackgroundBlurEffect::AddBlurStrengthAnimation
+ */
+ void AddBlurStrengthAnimation(Animation& animation, AlphaFunction alphaFunction, TimePeriod timePeriod, float fromValue, float toValue);
+
+ /**
+ * @copydoc Dali::Toolkit::BackgroundBlurEffect::AddBlurOpacityAnimation
+ */
+ void AddBlurOpacityAnimation(Animation& animation, AlphaFunction alphaFunction, TimePeriod timePeriod, float fromValue, float toValue);
+
+public: // Signals
+ /**
+ * @copydoc Dali::Toolkit::BackgroundBlurEffect::FinishedSignal
+ */
+ FinishedSignalType& FinishedSignal();
+
+public: // Not intended for use by Application developers
+ ///@cond internal
+ /**
+ * @brief Creates a handle using the Toolkit::Internal implementation.
+ * @SINCE_2_4.20
+ * @param[in] blurEffectImpl The UI Control implementation.
+ */
+ explicit DALI_INTERNAL GaussianBlurEffect(Internal::GaussianBlurEffectImpl* gaussianBlurEffectImpl);
+ ///@endcond
+};
+} // namespace Toolkit
+} // namespace Dali
+#endif // DALI_TOOLKIT_BLUR_EFFECT_H
${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/gaussian-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
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/gaussian-blur-effect.h
${public_api_src_dir}/controls/render-effects/mask-effect.h
)