Support borderline blur edge for color visual 70/318070/9
authorEunki, Hong <eunkiki.hong@samsung.com>
Mon, 13 Jan 2025 03:04:17 +0000 (12:04 +0900)
committerEunki Hong <eunkiki.hong@samsung.com>
Wed, 15 Jan 2025 05:33:55 +0000 (05:33 +0000)
Let we allow to blur radius applied to borderline cases.

For now, let we implement it as simple smoothstep.
But final version should resolve "Optical Illusion Effect"

Change-Id: I97c105c7c12ff89830842ba9bc51e71e8e2d7fbd
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-Visual.cpp
dali-toolkit/devel-api/visuals/color-visual-properties-devel.h
dali-toolkit/internal/graphics/shaders/color-visual-shader.frag
dali-toolkit/internal/graphics/shaders/color-visual-shader.vert
dali-toolkit/internal/visuals/color/color-visual-shader-factory.cpp
dali-toolkit/internal/visuals/color/color-visual-shader-factory.h
dali-toolkit/internal/visuals/color/color-visual.cpp
dali-toolkit/internal/visuals/visual-factory-cache.h
dali-toolkit/internal/visuals/visual-string-constants.cpp

index 652e007653ca3d2428a1519c2283f833539961f1..d6d5b24d808e36220af0103fe8e8997aaae955bc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * 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.
@@ -4783,7 +4783,7 @@ int UtcDaliVisualBorderlineBlendModeTest(void)
     application.GetScene().Remove(actor);
   }
 
-  // Case 4 : Test which animated corner radius occur.
+  // Case 4 : Test which animated corner radius or blur radius occur.
   {
     tet_printf("Test borderline animate case\n");
     Property::Map propertyMap;
@@ -4809,6 +4809,7 @@ int UtcDaliVisualBorderlineBlendModeTest(void)
 
     Animation animation = Animation::New(0.1f);
     animation.AnimateTo(DevelControl::GetVisualProperty(actor, DummyControl::Property::TEST_VISUAL, DevelVisual::Property::CORNER_RADIUS), Vector4(1.0f, 1.0f, 1.0f, 1.0f));
+    animation.AnimateTo(DevelControl::GetVisualProperty(actor, DummyControl::Property::TEST_VISUAL, DevelColorVisual::Property::BLUR_RADIUS), 1.0f);
     animation.Play();
 
     application.SendNotification();
@@ -4816,7 +4817,7 @@ int UtcDaliVisualBorderlineBlendModeTest(void)
     application.Render(101u); // End of animation
 
     blendModeValue = renderer.GetProperty(Renderer::Property::BLEND_MODE);
-    // BlendMode is ON_WITHOUT_CULL.
+    // BlendMode is still ON_WITHOUT_CULL.
     DALI_TEST_EQUALS(blendModeValue.Get<int>(), (int)BlendMode::ON_WITHOUT_CULL, TEST_LOCATION);
 
     application.GetScene().Remove(actor);
@@ -5184,6 +5185,54 @@ int UtcDaliVisualGetPropertyObject02(void)
 
   END_TEST;
 }
+int UtcDaliVisualGetPropertyObject03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliVisualGetPropertyObject03 GetProperty for color visual");
+
+  VisualFactory factory = VisualFactory::Get();
+
+  Property::Map properties;
+  properties[Visual::Property::TYPE] = Visual::COLOR;
+  properties.Insert(ColorVisual::Property::MIX_COLOR, Color::RED);
+  Visual::Base visual = factory.CreateVisual(properties);
+
+  DALI_TEST_CHECK(visual.GetType() == Visual::COLOR);
+
+  auto propertyTest = [](Visual::Base visual, Property::Key key, bool expect) {
+    {
+      std::ostringstream oss;
+      oss << "Test for key[" << key << "]";
+      tet_printf("%s\n", oss.str().c_str());
+    }
+
+    Dali::Property property = visual.GetPropertyObject(std::move(key));
+    tet_printf("Result object[%p] index of property[%d]\n", property.object.GetObjectPtr(), property.propertyIndex);
+
+    if(expect)
+    {
+      DALI_TEST_CHECK(property.object && property.propertyIndex != Property::INVALID_INDEX);
+    }
+    else
+    {
+      DALI_TEST_CHECK(!property.object && property.propertyIndex == Property::INVALID_INDEX);
+    }
+  };
+
+  // Test to get valid objects
+  propertyTest(visual, Visual::Property::MIX_COLOR, true);
+  propertyTest(visual, Visual::Property::OPACITY, true);
+  propertyTest(visual, Visual::Transform::Property::SIZE, true);
+
+  // Test subclass only property success to get
+  propertyTest(visual, DevelColorVisual::Property::BLUR_RADIUS, true);
+  propertyTest(visual, "blurRadius", true);
+
+  propertyTest(visual, DevelVisual::Property::CORNER_RADIUS, true);
+  propertyTest(visual, DevelVisual::Property::BORDERLINE_WIDTH, true);
+
+  END_TEST;
+}
 
 int UtcDaliVisualGetVisualProperty01(void)
 {
@@ -6558,7 +6607,6 @@ int UtcDaliVisualUpdatePropertyChangeShader03(void)
   Property::Map targetPropertyMap;
   targetPropertyMap[DevelColorVisual::Property::BLUR_RADIUS] = targetBlurRadius;
   targetPropertyMap[DevelVisual::Property::CORNER_RADIUS]    = targetCornerRadius;
-  targetPropertyMap[DevelVisual::Property::BORDERLINE_WIDTH] = 10.0f; // Don't care. just dummy
 
   callStack.Reset();
   callStack.Enable(true);
@@ -6582,7 +6630,7 @@ int UtcDaliVisualUpdatePropertyChangeShader03(void)
     dummyControl,
     {
       {"#define IS_REQUIRED_BLUR", true},
-      {"#define IS_REQUIRED_BORDERLINE", false}, // Note : We ignore borderline when blur radius occured
+      {"#define IS_REQUIRED_BORDERLINE", false},
       {"#define IS_REQUIRED_ROUNDED_CORNER", true},
       {"#define IS_REQUIRED_SQUIRCLE_CORNER", false},
     },
@@ -6602,7 +6650,6 @@ int UtcDaliVisualUpdatePropertyChangeShader03(void)
   Property::Map targetPropertyMap2;
   targetPropertyMap2[DevelColorVisual::Property::BLUR_RADIUS] = 0.0f;
   targetPropertyMap2[DevelVisual::Property::CORNER_RADIUS]    = Vector4::ZERO;
-  targetPropertyMap2[DevelVisual::Property::BORDERLINE_WIDTH] = 15.0f; // Don't care. just dummy
 
   // Update Properties with CornerRadius
   DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Action::UPDATE_PROPERTY, targetPropertyMap2);
@@ -6622,8 +6669,8 @@ int UtcDaliVisualUpdatePropertyChangeShader03(void)
   TestShaderCodeContainSubstrings(
     dummyControl,
     {
-      {"#define IS_REQUIRED_BLUR", true},           // Note : mAlwaysUsingBlurRadius is true.
-      {"#define IS_REQUIRED_BORDERLINE", false},    // Note : We ignore borderline when blur radius occured
+      {"#define IS_REQUIRED_BLUR", true}, // Note : mAlwaysUsingBlurRadius is true.
+      {"#define IS_REQUIRED_BORDERLINE", false},
       {"#define IS_REQUIRED_ROUNDED_CORNER", true}, // Note : mAlwaysUsingCornerRadius is true.
       {"#define IS_REQUIRED_SQUIRCLE_CORNER", false},
     },
@@ -6880,7 +6927,7 @@ int UtcDaliVisualUpdatePropertyChangeShader05(void)
       dummyControl,
       {
         {"#define IS_REQUIRED_BLUR", true},
-        {"#define IS_REQUIRED_BORDERLINE", false}, // Note : We ignore borderline when blur radius occured
+        {"#define IS_REQUIRED_BORDERLINE", true},
         {"#define IS_REQUIRED_ROUNDED_CORNER", true},
         {"#define IS_REQUIRED_SQUIRCLE_CORNER", {false, true}},
         {"#define SL_VERSION_LOW", {false, true}},
@@ -6922,7 +6969,7 @@ int UtcDaliVisualUpdatePropertyChangeShader05(void)
       dummyControl,
       {
         {"#define IS_REQUIRED_BLUR", true},                     // Note : mAlwaysUsingBlurRadius is true.
-        {"#define IS_REQUIRED_BORDERLINE", false},              // Note : We ignore borderline when blur radius occured
+        {"#define IS_REQUIRED_BORDERLINE", true},               // Note : mAlwaysUsingBorderline is true.
         {"#define IS_REQUIRED_ROUNDED_CORNER", true},           // Note : mAlwaysUsingCornerRadius is true.
         {"#define IS_REQUIRED_SQUIRCLE_CORNER", {false, true}}, // Note : mAlwaysUsingCornerSquareness is true.
         {"#define SL_VERSION_LOW", {false, true}},
@@ -7013,7 +7060,7 @@ int UtcDaliVisualCutoutPolicyChangeShader01(void)
       dummyControl,
       {
         {"#define IS_REQUIRED_BLUR", enableBlur},
-        {"#define IS_REQUIRED_BORDERLINE", !enableBlur && enableBorderline}, ///< Since borderline is ignored, due to blur enabled.
+        {"#define IS_REQUIRED_BORDERLINE", enableBorderline},
         {"#define IS_REQUIRED_ROUNDED_CORNER", enableCornerRadius},
         {"#define IS_REQUIRED_CUTOUT", cutoutPolicy != DevelColorVisual::CutoutPolicy::NONE},
       },
index 8b451d789c9559ae68be72a535b4aeab3fd6830e..105a5bc8ab5ab01731d1b8a05f8d9f46dda3de81 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_COLOR_VISUAL_PROPERTIES_DEVEL_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * 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.
@@ -46,8 +46,7 @@ enum
    * @note Optional.
    * @note The default is 0.
    * @note The visual size increases by the blur radius.
-   * @note If squareness is not zero, the width of borderline might not equal with it's real value.
-   * @note We cannot use blur radius and borderline properties at the same time.
+   * @note If squareness is not zero, the width of blur radius might not equal with it's real value.
    */
   BLUR_RADIUS = MIX_COLOR + 2,
 
index 3465cbaad42ea62f2e6179e3c64fbec7d85114fc..363a56a8071e8e53afbeb37c5a7352b694d04453 100644 (file)
@@ -40,7 +40,8 @@ UNIFORM_BLOCK SharedBlock
 
 #ifdef IS_REQUIRED_BLUR
     UNIFORM highp float blurRadius;
-#elif defined(IS_REQUIRED_BORDERLINE)
+#endif
+#ifdef IS_REQUIRED_BORDERLINE
     UNIFORM highp float borderlineWidth;
     UNIFORM highp float borderlineOffset;
     UNIFORM lowp vec4 borderlineColor;
@@ -70,8 +71,7 @@ highp float gPotential = 0.0;
 highp float gPotentialRange = 0.0;
 highp float gMaxOutlinePotential = 0.0;
 highp float gMinOutlinePotential = 0.0;
-#ifdef IS_REQUIRED_BLUR
-#elif defined(IS_REQUIRED_BORDERLINE)
+#ifdef IS_REQUIRED_BORDERLINE
 highp float gMaxInlinePotential = 0.0;
 highp float gMinInlinePotential = 0.0;
 #endif
@@ -103,8 +103,7 @@ void calculateFragmentPosition(highp vec2 position, highp vec2 halfSizeOfRect)
 void calculatePosition(highp float currentBorderlineWidth)
 {
   gCenterPosition = -gRadius;
-#ifdef IS_REQUIRED_BLUR
-#elif defined(IS_REQUIRED_BORDERLINE)
+#ifdef IS_REQUIRED_BORDERLINE
   gCenterPosition += currentBorderlineWidth * (clamp(borderlineOffset, -1.0, 1.0) + 1.0) * 0.5;
 #endif
   gDiff = gFragmentPosition - gCenterPosition;
@@ -151,11 +150,9 @@ void setupMinMaxPotential(highp float currentBorderlineWidth)
   gMaxOutlinePotential = gRadius + gPotentialRange;
   gMinOutlinePotential = gRadius - gPotentialRange;
 
-#ifdef IS_REQUIRED_BLUR
-#elif defined(IS_REQUIRED_BORDERLINE)
+#ifdef IS_REQUIRED_BORDERLINE
   gMaxInlinePotential = gMaxOutlinePotential - currentBorderlineWidth;
   gMinInlinePotential = gMinOutlinePotential - currentBorderlineWidth;
-#else
 #endif
 
   // reduce defect near edge of rounded corner.
@@ -174,8 +171,52 @@ void PreprocessPotential(highp vec4 cornerRadius, highp vec2 position, highp vec
 }
 #endif
 
+#ifdef IS_REQUIRED_BORDERLINE
 #ifdef IS_REQUIRED_BLUR
-#elif defined(IS_REQUIRED_BORDERLINE)
+// TODO : Current logic is only for PoC! We should make clean up!
+lowp vec4 convertBorderlineColorWithBlur(lowp vec4 textureColor,highp float currentBorderlineWidth, highp float blurRadius)
+{
+  highp float potential = gPotential;
+
+  blurRadius = max(blurRadius, 0.0) + vAliasMargin;
+
+  lowp vec3  borderlineColorRGB   = borderlineColor.rgb * uActorColor.rgb;
+  lowp float borderlineColorAlpha = borderlineColor.a * uActorColor.a;
+  // NOTE : color-visual is always preMultiplied.
+  borderlineColorRGB *= borderlineColorAlpha;
+
+  mediump float borderlineOpacity = 0.0;
+  mediump float textureOpacity = 0.0;
+
+  highp float outsideThreshold = gRadius;
+  highp float insideThreshold  = gRadius - currentBorderlineWidth;
+  highp float textureOutlineThreshold = -gCenterPosition;
+
+  // borderlineOpacity should be 0~1 whenever potential over insideThreshold
+  // and 1~0 whenever potential outsideThreshold.
+  // To combine this 2 information, we can use smoothstep and multiply function.
+
+  borderlineOpacity = smoothstep(insideThreshold - blurRadius, insideThreshold + blurRadius, potential) *
+                      (1.0 - smoothstep(outsideThreshold - blurRadius, outsideThreshold + blurRadius, potential));
+
+  textureOpacity = 1.0 - smoothstep(textureOutlineThreshold - blurRadius, textureOutlineThreshold + blurRadius, potential);
+
+  // NOTE : color-visual is always preMultiplied.
+  borderlineColorRGB *= borderlineOpacity;
+  borderlineColorAlpha *= borderlineOpacity;
+  textureColor *= textureOpacity;
+
+  // Manual blend operation with premultiplied colors.
+  // Final alpha = borderlineColorAlpha + (1.0 - borderlineColorAlpha) * textureColor.a.
+  // (Final rgb * alpha) =  borderlineColorRGB + (1.0 - borderlineColorAlpha) * textureColor.rgb
+  // If premultipliedAlpha == 1.0, just return vec4(rgb*alpha, alpha)
+  // Else, return vec4((rgb*alpha) / alpha, alpha)
+
+  lowp float finalAlpha = mix(textureColor.a, 1.0, borderlineColorAlpha);
+  lowp vec3  finalMultipliedRGB = borderlineColorRGB + (1.0 - borderlineColorAlpha) * textureColor.rgb;
+  return vec4(finalMultipliedRGB, finalAlpha);
+}
+#else
 lowp vec4 convertBorderlineColor(lowp vec4 textureColor)
 {
   highp float potential = gPotential;
@@ -238,6 +279,7 @@ lowp vec4 convertBorderlineColor(lowp vec4 textureColor)
   return mix(textureColor, vec4(borderlineColorRGB, borderlineColorAlpha), borderlineOpacity);
 }
 #endif
+#endif
 
 #ifdef IS_REQUIRED_BLUR
 #elif defined(IS_REQUIRED_ROUNDED_CORNER)
@@ -263,7 +305,7 @@ mediump float calculateCornerOpacity()
 #endif
 
 #ifdef IS_REQUIRED_BLUR
-#if defined(SL_VERSION_LOW) || defined(IS_REQUIRED_SQUIRCLE_CORNER)
+#if defined(SL_VERSION_LOW) || defined(IS_REQUIRED_SQUIRCLE_CORNER) || defined(IS_REQUIRED_BORDERLINE)
 // Legacy code for low version glsl
 mediump float calculateBlurOpacity()
 {
@@ -401,14 +443,22 @@ void main()
 #endif
 
 #ifdef IS_REQUIRED_BLUR
+#ifdef IS_REQUIRED_BORDERLINE
+    tempBorderlineWidth = borderlineWidth;
+#endif
     calculatePosition(tempBorderlineWidth);
     calculatePotential();
+
+#ifdef IS_REQUIRED_BORDERLINE
+    gl_FragColor = convertBorderlineColorWithBlur(targetColor, tempBorderlineWidth, blurRadius);
+#else
     setupMinMaxPotential(tempBorderlineWidth);
 
     gl_FragColor = targetColor;
 
     mediump float opacity = calculateBlurOpacity();
     gl_FragColor *= opacity;
+#endif
 #else
 #if defined(IS_REQUIRED_ROUNDED_CORNER) && !defined(IS_REQUIRED_BORDERLINE)
     // skip rounded corner calculate for performance
index 98abcec0965b039e72e55bcda037456cdefe6268..36e88984885cd75042b6d02136d44db0f7491d74 100644 (file)
@@ -48,7 +48,8 @@ UNIFORM_BLOCK SharedBlock
 
 #ifdef IS_REQUIRED_BLUR
     UNIFORM highp float blurRadius;
-#elif defined(IS_REQUIRED_BORDERLINE)
+#endif
+#ifdef IS_REQUIRED_BORDERLINE
     UNIFORM highp float borderlineWidth;
     UNIFORM highp float borderlineOffset;
     UNIFORM lowp vec4 borderlineColor;
@@ -74,16 +75,13 @@ vec4 ComputeVertexPosition()
 #endif
 
 #ifdef IS_REQUIRED_ROUNDED_CORNER
-#ifdef IS_REQUIRED_BLUR
-  highp float maxSize = max(visualSize.x, visualSize.y);
-  highp float minSize = min(visualSize.x, visualSize.y);
-#elif defined(IS_REQUIRED_BORDERLINE)
-  highp float maxSize = max(visualSize.x, visualSize.y) + (1.0 + clamp(borderlineOffset, -1.0, 1.0)) * borderlineWidth;
-  highp float minSize = min(visualSize.x, visualSize.y) + (1.0 + clamp(borderlineOffset, -1.0, 1.0)) * borderlineWidth;
-#else
-  highp float maxSize = max(visualSize.x, visualSize.y);
-  highp float minSize = min(visualSize.x, visualSize.y);
-#endif
+  #ifdef IS_REQUIRED_BORDERLINE
+    highp float maxSize = max(visualSize.x, visualSize.y) + (1.0 + clamp(borderlineOffset, -1.0, 1.0)) * borderlineWidth;
+    highp float minSize = min(visualSize.x, visualSize.y) + (1.0 + clamp(borderlineOffset, -1.0, 1.0)) * borderlineWidth;
+  #else
+    highp float maxSize = max(visualSize.x, visualSize.y);
+    highp float minSize = min(visualSize.x, visualSize.y);
+  #endif
   vCornerRadius = mix(cornerRadius * minSize, cornerRadius, cornerRadiusPolicy);
   vCornerRadius = min(vCornerRadius, minSize * 0.5);
   // Optimize fragment shader. 0.2929 ~= 1.0 - sqrt(0.5)
@@ -103,10 +101,15 @@ vec4 ComputeVertexPosition()
 #endif
 
 #ifdef IS_REQUIRED_BLUR
-  vPosition = aPosition * (visualSize + 2.0 * blurRadius + vertexMargin);
-  vOptRectSize -= blurRadius + 1.0;
+  #ifdef IS_REQUIRED_BORDERLINE
+    vPosition = aPosition * (visualSize + (1.0 + clamp(borderlineOffset, -1.0, 1.0)) * borderlineWidth + 2.0 * blurRadius + vertexMargin);
+    vOptRectSize -= (1.0 - clamp(borderlineOffset, -1.0, 1.0)) * 0.5 * borderlineWidth + blurRadius + 1.0;
+  #else
+    vPosition = aPosition * (visualSize + 2.0 * blurRadius + vertexMargin);
+    vOptRectSize -= blurRadius + 1.0;
+  #endif
 #elif defined(IS_REQUIRED_BORDERLINE)
-  vPosition = aPosition * (visualSize + (1.0 + clamp(borderlineOffset, -1.0, 1.0))* borderlineWidth + vertexMargin);
+  vPosition = aPosition * (visualSize + (1.0 + clamp(borderlineOffset, -1.0, 1.0)) * borderlineWidth + vertexMargin);
   vOptRectSize -= (1.0 - clamp(borderlineOffset, -1.0, 1.0)) * 0.5 * borderlineWidth + 1.0;
 #elif defined(IS_REQUIRED_ROUNDED_CORNER)
   vPosition = aPosition * (visualSize + vertexMargin);
index 2d44c659d332f2780545584eeafaca0dff62ce75..1f29c3c0126c0015641392e01366990b2ffcdffe 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * 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.
@@ -40,6 +40,9 @@ constexpr VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[] = {
   VisualFactoryCache::COLOR_SHADER_BLUR_EDGE,
   VisualFactoryCache::COLOR_SHADER_ROUNDED_CORNER_BLUR_EDGE,
   VisualFactoryCache::COLOR_SHADER_SQUIRCLE_CORNER_BLUR_EDGE,
+  VisualFactoryCache::COLOR_SHADER_BORDERLINE_BLUR_EDGE,
+  VisualFactoryCache::COLOR_SHADER_ROUNDED_BORDERLINE_BLUR_EDGE,
+  VisualFactoryCache::COLOR_SHADER_SQUIRCLE_BORDERLINE_BLUR_EDGE,
   VisualFactoryCache::COLOR_SHADER_CUTOUT,
   VisualFactoryCache::COLOR_SHADER_CUTOUT_ROUNDED_CORNER,
   VisualFactoryCache::COLOR_SHADER_CUTOUT_SQUIRCLE_CORNER,
@@ -49,6 +52,9 @@ constexpr VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[] = {
   VisualFactoryCache::COLOR_SHADER_CUTOUT_BLUR_EDGE,
   VisualFactoryCache::COLOR_SHADER_CUTOUT_ROUNDED_CORNER_BLUR_EDGE,
   VisualFactoryCache::COLOR_SHADER_CUTOUT_SQUIRCLE_CORNER_BLUR_EDGE,
+  VisualFactoryCache::COLOR_SHADER_CUTOUT_BORDERLINE_BLUR_EDGE,
+  VisualFactoryCache::COLOR_SHADER_CUTOUT_ROUNDED_BORDERLINE_BLUR_EDGE,
+  VisualFactoryCache::COLOR_SHADER_CUTOUT_SQUIRCLE_BORDERLINE_BLUR_EDGE,
 };
 constexpr uint32_t SHADER_TYPE_TABLE_COUNT = sizeof(SHADER_TYPE_TABLE) / sizeof(SHADER_TYPE_TABLE[0]);
 
@@ -59,10 +65,9 @@ enum ColorVisualRequireFlag
   ROUNDED_CORNER  = 1,
   SQUIRCLE_CORNER = 2,
 
-  BORDERLINE = (1) * 3,
-  BLUR       = (2) * 3,
-
-  CUTOUT = (1) * 3 * 3,
+  BORDERLINE = (1 << 0) * 3,
+  BLUR       = (1 << 1) * 3,
+  CUTOUT     = (1 << 2) * 3,
 };
 
 constexpr uint32_t MINIMUM_SHADER_VERSION_SUPPORT_ROUNDED_BLUR = 300;
@@ -131,7 +136,7 @@ VisualFactoryCache::ShaderType FeatureBuilder::GetShaderType() const
     shaderTypeFlag += ColorVisualRequireFlag::ROUNDED_CORNER;
   }
 
-  if(mColorBorderline && !mColorBlur)
+  if(mColorBorderline)
   {
     shaderTypeFlag += ColorVisualRequireFlag::BORDERLINE;
   }
@@ -163,7 +168,7 @@ void FeatureBuilder::GetVertexShaderPrefixList(std::string& vertexShaderPrefixLi
   {
     vertexShaderPrefixList += "#define IS_REQUIRED_BLUR\n";
   }
-  if(mColorBorderline == Borderline::ENABLED && mColorBlur == Blur::DISABLED)
+  if(mColorBorderline == Borderline::ENABLED)
   {
     vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
   }
@@ -192,7 +197,7 @@ void FeatureBuilder::GetFragmentShaderPrefixList(std::string& fragmentShaderPref
       fragmentShaderPrefixList += "#define SL_VERSION_LOW\n";
     }
   }
-  if(mColorBorderline == Borderline::ENABLED && mColorBlur == Blur::DISABLED)
+  if(mColorBorderline == Borderline::ENABLED)
   {
     fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
   }
index 55425c6a74d1e291ee0d2c96ef1c4725cc9248ba..81b56f46f89100d8533a0874c6518685462ad6a0 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_COLOR_VISUAL_SHADER_FACTORY_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * 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.
@@ -96,7 +96,7 @@ public:
   void                           GetFragmentShaderPrefixList(std::string& fragmentShaderPrefixList) const;
 
 private:
-  RoundedCorner::Type mColorRoundCorner : 2; ///< Whether use rounded corner, or not. default as RoundedCorner::DISABLED
+  RoundedCorner::Type mColorRoundCorner : 3; ///< Whether use rounded corner, or not. default as RoundedCorner::DISABLED
   Borderline::Type    mColorBorderline : 2;  ///< Whether use border line, or not. default as Borderline::DISABLED
   Blur::Type          mColorBlur : 2;        ///< Whether use blur, or not. default as Blur::DISABLED
   Cutout::Type        mColorCutout : 2;      ///< Whether use cutout, or not. default as Cutout::DISABLED
index b2bb68554fcfd512ca65467720cb700dac8aa9dd..fbd455ca2af11aaeef6141b8577acddb71f162e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * 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.
@@ -124,7 +124,12 @@ void ColorVisual::DoSetProperties(const Property::Map& propertyMap)
         // Change the shader must not be occured many times. we always have to use blur feature.
         mAlwaysUsingBlurRadius = true;
 
-        mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+        if(!IsBorderlineRequired())
+        {
+          // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
+          mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+        }
+
         // Change shader
         if(!mImpl->mCustomShader)
         {
@@ -261,7 +266,8 @@ Dali::Property ColorVisual::OnGetPropertyObject(Dali::Property::Key key)
     return Dali::Property(handle, Property::INVALID_INDEX);
   }
 
-  if((key.type == Property::Key::INDEX && key.indexKey == DevelColorVisual::Property::BLUR_RADIUS) || (key.type == Property::Key::STRING && key.stringKey == BLUR_RADIUS_NAME))
+  if((key.type == Property::Key::INDEX && key.indexKey == DevelColorVisual::Property::BLUR_RADIUS) ||
+     (key.type == Property::Key::STRING && key.stringKey == BLUR_RADIUS_NAME))
   {
     const bool updateShader = !mImpl->mCustomShader && !IsBlurRequired();
 
@@ -277,7 +283,11 @@ Dali::Property ColorVisual::OnGetPropertyObject(Dali::Property::Key key)
       // Change shader
       UpdateShader();
     }
-    mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+    if(!IsBorderlineRequired())
+    {
+      // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
+      mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+    }
     return Dali::Property(mImpl->mRenderer, DecoratedVisualRenderer::Property::BLUR_RADIUS);
   }
 
index 8ef02100425193dacbb42c3fe3d29844f9820613..a1b0caf62cde8cf4a218da077395424c5ebf507f 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_VISUAL_FACTORY_CACHE_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * 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.
@@ -65,6 +65,9 @@ public:
     COLOR_SHADER_BLUR_EDGE,
     COLOR_SHADER_ROUNDED_CORNER_BLUR_EDGE,
     COLOR_SHADER_SQUIRCLE_CORNER_BLUR_EDGE,
+    COLOR_SHADER_BORDERLINE_BLUR_EDGE,
+    COLOR_SHADER_ROUNDED_BORDERLINE_BLUR_EDGE,
+    COLOR_SHADER_SQUIRCLE_BORDERLINE_BLUR_EDGE,
     COLOR_SHADER_CUTOUT,
     COLOR_SHADER_CUTOUT_ROUNDED_CORNER,
     COLOR_SHADER_CUTOUT_SQUIRCLE_CORNER,
@@ -74,6 +77,9 @@ public:
     COLOR_SHADER_CUTOUT_BLUR_EDGE,
     COLOR_SHADER_CUTOUT_ROUNDED_CORNER_BLUR_EDGE,
     COLOR_SHADER_CUTOUT_SQUIRCLE_CORNER_BLUR_EDGE,
+    COLOR_SHADER_CUTOUT_BORDERLINE_BLUR_EDGE,
+    COLOR_SHADER_CUTOUT_ROUNDED_BORDERLINE_BLUR_EDGE,
+    COLOR_SHADER_CUTOUT_SQUIRCLE_BORDERLINE_BLUR_EDGE,
     BORDER_SHADER,
     BORDER_SHADER_ANTI_ALIASING,
     GRADIENT_SHADER_LINEAR_BOUNDING_BOX,
index 59185503b4fde43ac2f7f2a0ce38eb0709c960df..a54e8ad1d0d936677b95ac5e17d29eda1993b703 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * 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.
@@ -56,6 +56,9 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(VISUAL_SHADER_TYPE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_BLUR_EDGE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_ROUNDED_CORNER_BLUR_EDGE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_SQUIRCLE_CORNER_BLUR_EDGE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_BORDERLINE_BLUR_EDGE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_ROUNDED_BORDERLINE_BLUR_EDGE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_SQUIRCLE_BORDERLINE_BLUR_EDGE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_ROUNDED_CORNER)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_SQUIRCLE_CORNER)
@@ -65,6 +68,9 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(VISUAL_SHADER_TYPE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_BLUR_EDGE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_ROUNDED_CORNER_BLUR_EDGE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_SQUIRCLE_CORNER_BLUR_EDGE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_BORDERLINE_BLUR_EDGE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_ROUNDED_BORDERLINE_BLUR_EDGE)
+  DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, COLOR_SHADER_CUTOUT_SQUIRCLE_BORDERLINE_BLUR_EDGE)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, BORDER_SHADER)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, BORDER_SHADER_ANTI_ALIASING)
   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::Internal::VisualFactoryCache::ShaderType, GRADIENT_SHADER_LINEAR_BOUNDING_BOX)