Add GaussianBlurEffect + Share common blur algorithm + UniformBlock caching 36/323236/14
authorjmm <j0064423.lee@samsung.com>
Thu, 24 Apr 2025 09:00:07 +0000 (18:00 +0900)
committerjmm <j0064423.lee@samsung.com>
Tue, 13 May 2025 08:04:42 +0000 (17:04 +0900)
Change-Id: I560f7cafbd72a8c5fd48c12682403ffe1c35ff35

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

index 56a98d57150289bbc38352cc590bc188922bce15..f5d56cd6ed70698c288361ce879b5d6868fb3c05 100644 (file)
  */
 
 #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;
@@ -289,6 +290,7 @@ int UtcDaliRenderEffectResize(void)
   DALI_TEST_EQUALS(count, control.GetRendererCount(), TEST_LOCATION);
 
   ////////////////////////////////////////////
+  tet_infoline("resize test on BackgroundBlurEffect");
   control.SetRenderEffect(BackgroundBlurEffect::New());
 
   application.SendNotification();
@@ -336,6 +338,40 @@ int UtcDaliRenderEffectResize(void)
   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;
 }
@@ -935,6 +971,12 @@ int UtcDaliRenderEffectReInitialize(void)
   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;
 }
 
@@ -959,30 +1001,58 @@ int UtcDaliRenderEffectBlurOnce(void)
 
   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;
 }
@@ -1037,30 +1107,59 @@ int UtcDaliRenderEffectBlurStrengthAnimation(void)
   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;
 }
@@ -1108,29 +1207,56 @@ int UtcDaliRenderEffectBlurOpacityAnimation(void)
   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;
 }
index 010765c8d58794f35a64990beb5a6d2e7fcbc92f..f02dc3e6d0cacda7963a18d7386e6d366a63f788 100644 (file)
@@ -62,6 +62,7 @@
 #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>
 
index ec243e6a7d777097253f84aff01cc2c846969c2e..8278a2d04a5ac5ad6e59fcb804e94ee77b076bfa 100644 (file)
 #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
@@ -40,56 +36,11 @@ 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
@@ -108,7 +59,6 @@ BackgroundBlurEffectImpl::BackgroundBlurEffectImpl()
   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)
 {
@@ -120,11 +70,10 @@ BackgroundBlurEffectImpl::BackgroundBlurEffectImpl(uint32_t blurRadius)
   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;
@@ -216,8 +165,9 @@ void BackgroundBlurEffectImpl::SetBlurRadius(uint32_t blurRadius)
     }
 
     // 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))
@@ -299,45 +249,12 @@ void BackgroundBlurEffectImpl::OnInitialize()
     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);
@@ -355,9 +272,7 @@ void BackgroundBlurEffectImpl::OnInitialize()
     }
 
     // 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);
@@ -422,7 +337,19 @@ void BackgroundBlurEffectImpl::OnActivate()
   // 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();
@@ -480,7 +407,10 @@ void BackgroundBlurEffectImpl::OnRefresh()
   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);
@@ -624,65 +554,6 @@ void BackgroundBlurEffectImpl::ApplyRenderTaskSourceActor(RenderTask sourceRende
   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;
index 1e0493701deaeabe9b59b2ef983d52c66afd912c..79b486785afd1df6b695a59e2142217fc5227c9c 100644 (file)
@@ -1,5 +1,5 @@
-#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.
@@ -26,9 +26,9 @@
 #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>
 
@@ -48,7 +48,6 @@ 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 BackgroundBlurEffectImplPtr New();
 
   /**
@@ -174,27 +173,6 @@ private:
    */
   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.
@@ -232,7 +210,6 @@ private:
   float    mDownscaleFactor;
   uint32_t mBlurRadius;
   uint32_t mDownscaledBlurRadius;
-  float    mBellCurveWidth;
 
   bool mSkipBlur : 1;
   bool mBlurOnce : 1;
diff --git a/dali-toolkit/internal/controls/render-effects/gaussian-blur-algorithm.cpp b/dali-toolkit/internal/controls/render-effects/gaussian-blur-algorithm.cpp
new file mode 100644 (file)
index 0000000..bdd1e3c
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * 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
diff --git a/dali-toolkit/internal/controls/render-effects/gaussian-blur-algorithm.h b/dali-toolkit/internal/controls/render-effects/gaussian-blur-algorithm.h
new file mode 100644 (file)
index 0000000..ebb2b4b
--- /dev/null
@@ -0,0 +1,69 @@
+#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
diff --git a/dali-toolkit/internal/controls/render-effects/gaussian-blur-effect-impl.cpp b/dali-toolkit/internal/controls/render-effects/gaussian-blur-effect-impl.cpp
new file mode 100644 (file)
index 0000000..a448bd4
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * 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
diff --git a/dali-toolkit/internal/controls/render-effects/gaussian-blur-effect-impl.h b/dali-toolkit/internal/controls/render-effects/gaussian-blur-effect-impl.h
new file mode 100644 (file)
index 0000000..55e97d5
--- /dev/null
@@ -0,0 +1,235 @@
+#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
index 3d4092d385067791beffbc343b4678135a52c7e1..92341beaa1c37b0c937a55f041f4433b2895b6b1 100644 (file)
@@ -72,7 +72,9 @@ 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/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
index d16d6e06b199fd6cdbe5726187aa8b5502c8c4da..e310a24ddf5640313609d6c56ae9073145a12948 100644 (file)
@@ -6,20 +6,27 @@ precision highp float;
 
 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;
index 3538bba80380947f57a22d2958399e0cc84695d9..bdc8fdc4818299242f91c0ed6447189ea85647d6 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (c) 2025 Samsung Electronics Co., Ltd.
  *
index 306e9191d87325fd7a97bf8869955ad1dd0d0b5c..6e45205fca91c54e24ce282bf65973b87c2bd60f 100644 (file)
@@ -30,19 +30,20 @@ namespace Internal DALI_INTERNAL
 {
 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
  */
diff --git a/dali-toolkit/public-api/controls/render-effects/gaussian-blur-effect.cpp b/dali-toolkit/public-api/controls/render-effects/gaussian-blur-effect.cpp
new file mode 100644 (file)
index 0000000..b802f1e
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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
diff --git a/dali-toolkit/public-api/controls/render-effects/gaussian-blur-effect.h b/dali-toolkit/public-api/controls/render-effects/gaussian-blur-effect.h
new file mode 100644 (file)
index 0000000..f61661c
--- /dev/null
@@ -0,0 +1,137 @@
+#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
index fd3aa84ddd3d4e35528530efc1cc602df4574b7e..dc3e1b7e28c00cacc8c83be1d40533a19e6dc91c 100644 (file)
@@ -16,6 +16,7 @@ SET( public_api_src_files
   ${public_api_src_dir}/controls/progress-bar/progress-bar.cpp
   ${public_api_src_dir}/controls/render-effects/render-effect.cpp
   ${public_api_src_dir}/controls/render-effects/background-blur-effect.cpp
+  ${public_api_src_dir}/controls/render-effects/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
@@ -111,6 +112,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/gaussian-blur-effect.h
   ${public_api_src_dir}/controls/render-effects/mask-effect.h
 )