[vlc-commits] [Git][videolan/vlc][master] 8 commits: qt: introduce `RoundedRectangleShadow.frag`

Steve Lhomme (@robUx4) gitlab at videolan.org
Tue May 13 05:07:30 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
c1acfdb4 by Fatih Uzunoglu at 2025-05-13T04:44:17+00:00
qt: introduce `RoundedRectangleShadow.frag`

- - - - -
ca824ecf by Fatih Uzunoglu at 2025-05-13T04:44:17+00:00
qt: introduce `RoundedRectangleShadow.qml`

- - - - -
871e5655 by Fatih Uzunoglu at 2025-05-13T04:44:17+00:00
qml: use `RoundedRectangleShadow` in `DoubleShadow`

- - - - -
d16e4843 by Fatih Uzunoglu at 2025-05-13T04:44:17+00:00
qml: workaround for `DragItem` images sometimes not getting rendered

This seems to be similar to 089271fd, I assume something is wrong
with QSG batch renderer's optimizations.

- - - - -
e18740cd by Fatih Uzunoglu at 2025-05-13T04:44:17+00:00
qml: use `RoundedRectangleShadow` instead of `DropShadowImage` in `PlayButton.qml`

- - - - -
eb86ceb3 by Fatih Uzunoglu at 2025-05-13T04:44:17+00:00
qml: deprecate `DropShadowImage.qml` in favor of `RoundedRectangleShadow.qml`

- - - - -
b10ba234 by Fatih Uzunoglu at 2025-05-13T04:44:17+00:00
qml: use `RoundedRectangleShadow` instead of `DynamicShadow` in `Player.qml`

- - - - -
ed50b45d by Fatih Uzunoglu at 2025-05-13T04:44:17+00:00
qt: get rid of `DynamicShadow`

- - - - -


21 changed files:

- modules/gui/qt/Makefile.am
- modules/gui/qt/medialibrary/qml/MusicAlbumsGridExpandDelegate.qml
- modules/gui/qt/medialibrary/qml/VideoInfoExpandPanel.qml
- modules/gui/qt/meson.build
- modules/gui/qt/network/qml/NetworkThumbnailItem.qml
- modules/gui/qt/player/qml/Player.qml
- modules/gui/qt/player/qml/controlbarcontrols/ArtworkInfoWidget.qml
- modules/gui/qt/player/qml/controlbarcontrols/PlayButton.qml
- modules/gui/qt/playlist/qml/PlaylistDelegate.qml
- + modules/gui/qt/shaders/RoundedRectangleShadow.frag
- modules/gui/qt/shaders/meson.build
- modules/gui/qt/shaders/shaders.qrc
- modules/gui/qt/widgets/qml/DoubleShadow.qml
- modules/gui/qt/widgets/qml/DragItem.qml
- modules/gui/qt/widgets/qml/DropShadowImage.qml
- modules/gui/qt/widgets/qml/EmptyLabel.qml
- modules/gui/qt/widgets/qml/FloatingNotification.qml
- modules/gui/qt/widgets/qml/GridItem.qml
- modules/gui/qt/widgets/qml/DynamicShadow.qml → modules/gui/qt/widgets/qml/RoundedRectangleShadow.qml
- modules/gui/qt/widgets/qml/TableColumns.qml
- − modules/gui/qt/widgets/qml/compat/DynamicShadow.qml


Changes:

=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -1307,14 +1307,13 @@ libqml_module_widgets_a_QML = \
 	widgets/qml/ImageExt.qml \
 	widgets/qml/ScrollBarExt.qml \
 	widgets/qml/FastBlend.qml \
-	widgets/qml/RadioButtonExt.qml
+	widgets/qml/RadioButtonExt.qml \
+	widgets/qml/RoundedRectangleShadow.qml
 if HAVE_QT65
 libqml_module_widgets_a_QML += \
-	widgets/qml/DynamicShadow.qml \
 	widgets/qml/BlurEffect.qml
 else
 libqml_module_widgets_a_QML += \
-	widgets/qml/compat/DynamicShadow.qml \
 	widgets/qml/compat/BlurEffect.qml
 endif
 nodist_libqml_module_widgets_a_SOURCES = widgets_qmlassets.cpp
@@ -1365,7 +1364,8 @@ libqt_plugin_la_SHADER := shaders/FadingEdge.frag \
                           shaders/FastBlend.frag \
                           shaders/FastBlend_additive.frag \
                           shaders/FastBlend_multiply.frag \
-                          shaders/FastBlend_screen.frag
+                          shaders/FastBlend_screen.frag \
+                          shaders/RoundedRectangleShadow.frag
 if ENABLE_QT
 
 libqt_plugin_la_LIBADD += libqml_module_dialogs.a \


=====================================
modules/gui/qt/medialibrary/qml/MusicAlbumsGridExpandDelegate.qml
=====================================
@@ -133,10 +133,6 @@ FocusScope {
                 readonly property real eDPR: MainCtx.effectiveDevicePixelRatio(Window.window)
 
                 Widgets.DefaultShadow {
-                    anchors.centerIn: parent
-
-                    sourceItem: parent
-
                     visible: (parent.status === Image.Ready)
                 }
             }


=====================================
modules/gui/qt/medialibrary/qml/VideoInfoExpandPanel.qml
=====================================
@@ -120,10 +120,6 @@ FocusScope {
                             readonly property real eDPR: MainCtx.effectiveDevicePixelRatio(Window.window)
 
                             Widgets.DefaultShadow {
-                                anchors.centerIn: parent
-
-                                sourceItem: parent
-
                                 visible: (parent.status === Image.Ready)
                             }
                         }


=====================================
modules/gui/qt/meson.build
=====================================
@@ -808,10 +808,8 @@ qml_modules += {
 }
 
 if qt6_dep.version().version_compare('>=6.5.0')
-    qml_dynamicshadow_file = 'widgets/qml/DynamicShadow.qml'
     qml_blureffect_file = 'widgets/qml/BlurEffect.qml'
 else
-    qml_dynamicshadow_file = 'widgets/qml/compat/DynamicShadow.qml'
     qml_blureffect_file = 'widgets/qml/compat/BlurEffect.qml'
 endif
 
@@ -901,12 +899,12 @@ qml_modules += {
         'widgets/qml/ViewHeader.qml',
         'widgets/qml/ProgressIndicator.qml',
         'widgets/qml/RectangularGlow.qml',
-        qml_dynamicshadow_file,
         qml_blureffect_file,
         'widgets/qml/ImageExt.qml',
         'widgets/qml/ScrollBarExt.qml',
         'widgets/qml/FastBlend.qml',
         'widgets/qml/RadioButtonExt.qml',
+        'widgets/qml/RoundedRectangleShadow.qml',
     ),
 }
 


=====================================
modules/gui/qt/network/qml/NetworkThumbnailItem.qml
=====================================
@@ -80,14 +80,6 @@ Widgets.TableRowDelegate {
             width: artwork.width
             height: artwork.height
 
-            Widgets.DefaultShadow {
-                anchors.centerIn: artwork
-
-                // clip shadows to only the painted area of cover
-                rectWidth: artwork.paintedWidth
-                rectHeight: artwork.paintedHeight
-            }
-
             //FIXME: implement fillMode in RoundImage and use MediaCover here instead
             //or directly TableCollumns.titleHeaderDelegate in place of NetworkThumbnailItem
             NetworkCustomCover {
@@ -108,6 +100,10 @@ Widgets.TableRowDelegate {
                 color1: root.colorContext.fg.primary
                 accent: root.colorContext.accent
 
+                Widgets.DefaultShadow {
+
+                }
+
                 Widgets.PlayCover {
                     x: Math.round((artwork.width - width) / 2)
                     y: Math.round((artwork.height - height) / 2)


=====================================
modules/gui/qt/player/qml/Player.qml
=====================================
@@ -358,23 +358,6 @@ FocusScope {
 
                             readonly property real sizeConstant: 2.7182
 
-                            Widgets.DynamicShadow {
-                                anchors.centerIn: cover
-                                sourceItem: cover
-
-                                color: Qt.rgba(0, 0, 0, .18)
-                                yOffset: VLCStyle.dp(24)
-                                blurRadius: VLCStyle.dp(54)
-                            }
-
-                            Widgets.DynamicShadow {
-                                anchors.centerIn: cover
-                                sourceItem: cover
-
-                                color: Qt.rgba(0, 0, 0, .22)
-                                yOffset: VLCStyle.dp(5)
-                                blurRadius: VLCStyle.dp(14)
-                            }
 
                             Image {
                                 id: cover
@@ -401,6 +384,18 @@ FocusScope {
 
                                 Accessible.role: Accessible.Graphic
                                 Accessible.name: qsTr("Cover")
+
+                                Widgets.RoundedRectangleShadow {
+                                    color: Qt.rgba(0, 0, 0, .18)
+                                    yOffset: VLCStyle.dp(24)
+                                    blurRadius: VLCStyle.dp(54)
+                                }
+
+                                Widgets.RoundedRectangleShadow {
+                                    color: Qt.rgba(0, 0, 0, .22)
+                                    yOffset: VLCStyle.dp(5)
+                                    blurRadius: VLCStyle.dp(14)
+                                }
                             }
                         }
 


=====================================
modules/gui/qt/player/qml/controlbarcontrols/ArtworkInfoWidget.qml
=====================================
@@ -184,9 +184,6 @@ AbstractButton {
             }
 
             Widgets.DefaultShadow {
-                anchors.centerIn: coverImage
-
-                sourceItem: coverImage
 
             }
         }


=====================================
modules/gui/qt/player/qml/controlbarcontrols/PlayButton.qml
=====================================
@@ -235,18 +235,13 @@ T.Control {
         implicitWidth: height
         implicitHeight: VLCStyle.icon_medium
 
-        Widgets.DropShadowImage {
+        Widgets.RoundedRectangleShadow {
             id: hoverShadow
 
-            anchors.centerIn: parent
-
             visible: opacity > 0
             opacity: 0
 
-            rectWidth: parent.width
-            rectHeight: parent.height
-            xRadius: parent.width
-            yRadius: xRadius
+            radius: parent.width
 
             blurRadius: VLCStyle.dp(9)
             yOffset: VLCStyle.dp(4)


=====================================
modules/gui/qt/playlist/qml/PlaylistDelegate.qml
=====================================
@@ -193,10 +193,6 @@ T.Control {
                 }
 
                 Widgets.DefaultShadow {
-                    anchors.centerIn: parent
-
-                    sourceItem: parent
-
                     visible: (artwork.status === Image.Ready)
                 }
             }


=====================================
modules/gui/qt/shaders/RoundedRectangleShadow.frag
=====================================
@@ -0,0 +1,121 @@
+#version 440
+
+// TODO: Dithering is not necessary with light colors. It is pretty much
+//       necessary with dark colors due to premultiplied alpha to prevent
+//       color banding. So, this should ideally be used only when the
+//       background color is dark. When the build system starts supporting
+//       defines to feed qsb, we can have the non-dithering version as well.
+#define DITHERING
+
+/*****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#extension GL_GOOGLE_include_directive : enable
+
+#include "Common.glsl"
+
+// This is provided by the default vertex shader even when there is no texture:
+layout(location = 0) in vec2 qt_TexCoord0;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+  mat4 qt_Matrix;
+  float qt_Opacity;
+
+  float blurRadius;
+  float radius;
+  float compensationFactor;
+
+  vec2 size;
+  vec4 color;
+};
+
+// Evan Wallace's Fast Rounded Rectangle Shadows
+// https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/
+// License: CC0 (http://creativecommons.org/publicdomain/zero/1.0/)
+/// <roundedboxshadow>
+
+// A standard gaussian function, used for weighting samples
+float gaussian(float x, float sigma) {
+  const float pi = 3.141592653589793;
+  return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma);
+}
+
+// This approximates the error function, needed for the gaussian integral
+vec2 erf(vec2 x) {
+  vec2 s = sign(x), a = abs(x);
+  x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
+  x *= x;
+  return s - s / (x * x);
+}
+
+// Return the blurred mask along the x dimension
+float roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize) {
+  float delta = min(halfSize.y - corner - abs(y), 0.0);
+  float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));
+  vec2 integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma));
+  return integral.y - integral.x;
+}
+
+// Return the mask for the shadow of a box from lower to upper
+float roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner) {
+  // Center everything to make the math easier
+  vec2 center = (lower + upper) * 0.5;
+  vec2 halfSize = (upper - lower) * 0.5;
+  point -= center;
+
+  // The signal is only non-zero in a limited range, so don't waste samples
+  float low = point.y - halfSize.y;
+  float high = point.y + halfSize.y;
+  float start = clamp(-3.0 * sigma, low, high);
+  float end = clamp(3.0 * sigma, low, high);
+
+  // Accumulate samples (we can get away with surprisingly few samples)
+  float step = (end - start) / 4.0;
+  float y = start + step * 0.5;
+  float value = 0.0;
+  for (int i = 0; i < 4; i++) {
+    value += roundedBoxShadowX(point.x, point.y - y, sigma, corner, halfSize) * gaussian(y, sigma) * step;
+    y += step;
+  }
+
+  return value;
+}
+
+/// </roundedboxshadow>
+
+void main()
+{
+    vec2 compensatedOffset = vec2(blurRadius, blurRadius) * compensationFactor;
+    vec2 denormalCoord = size * qt_TexCoord0;
+
+    float shadow = roundedBoxShadow(compensatedOffset,
+                                    size - compensatedOffset,
+                                    denormalCoord,
+                                    blurRadius,
+                                    radius / 2.);
+
+    fragColor = color * shadow * qt_Opacity; // premultiplied
+
+#ifdef DITHERING
+    float r = rand(qt_TexCoord0) - 0.5;
+    vec4 noise = vec4(r,r,r,r) * DITHERING_STRENGTH;
+    fragColor += noise * step(DITHERING_CUTOFF, fragColor.a); // additive
+#endif
+}


=====================================
modules/gui/qt/shaders/meson.build
=====================================
@@ -21,7 +21,8 @@ shader_sources = [
     'FastBlend.frag',
     'FastBlend_additive.frag',
     'FastBlend_multiply.frag',
-    'FastBlend_screen.frag'
+    'FastBlend_screen.frag',
+    'RoundedRectangleShadow.frag',
 ]
 
 shader_files = files(shader_sources)


=====================================
modules/gui/qt/shaders/shaders.qrc
=====================================
@@ -16,5 +16,6 @@
         <file alias="FastBlend_additive.frag.qsb">FastBlend_additive.frag.qsb</file>
         <file alias="FastBlend_multiply.frag.qsb">FastBlend_multiply.frag.qsb</file>
         <file alias="FastBlend_screen.frag.qsb">FastBlend_screen.frag.qsb</file>
+        <file alias="RoundedRectangleShadow.frag.qsb">RoundedRectangleShadow.frag.qsb</file>
     </qresource>
 </RCC>


=====================================
modules/gui/qt/widgets/qml/DoubleShadow.qml
=====================================
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (C) 2022 VLC authors and VideoLAN
+ * Copyright (C) 2025 VLC authors and VideoLAN
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,117 +18,44 @@
 
 import QtQuick
 
-
-import VLC.Style
-import VLC.Util
-
-// A convenience file to encapsulate two drop shadow images stacked on top
-// of each other
+// A convenience file to encapsulate two drop shadow rectangles stacked
 Item {
-    implicitWidth: image.implicitWidth
-    implicitHeight: image.implicitHeight
-
-    property Item sourceItem: null
-
-    readonly property real viewportHorizontalOffset: (Math.max(Math.abs(primaryHorizontalOffset) + primaryBlurRadius, Math.abs(secondaryHorizontalOffset) + secondaryBlurRadius)) * 2
-    readonly property real viewportVerticalOffset: (Math.max(Math.abs(primaryVerticalOffset) + primaryBlurRadius, Math.abs(secondaryVerticalOffset) + secondaryBlurRadius)) * 2
-
-    property real viewportWidth: rectWidth + viewportHorizontalOffset
-    property real viewportHeight: rectHeight + viewportVerticalOffset
-
-    property real rectWidth: sourceItem ? Math.min((sourceItem.paintedWidth ?? Number.MAX_VALUE) - Math.ceil(sourceItem.padding ?? 0) * 2, sourceItem.width) : 0
-    property real rectHeight: sourceItem ? Math.min((sourceItem.paintedHeight ?? Number.MAX_VALUE) - Math.ceil(sourceItem.padding ?? 0) * 2, sourceItem.height) : 0
-    property real xRadius: (sourceItem ? (sourceItem.effectiveRadius ?? sourceItem.radius) : 0) ?? 0
-    property real yRadius: (sourceItem ? (sourceItem.effectiveRadius ?? sourceItem.radius) : 0) ?? 0
-
-    property color primaryColor: Qt.rgba(0, 0, 0, .18)
-    property real primaryVerticalOffset: 0
-    property real primaryHorizontalOffset: 0
-    property real primaryBlurRadius: 0
-
-    property color secondaryColor: Qt.rgba(0, 0, 0, .22)
-    property real secondaryVerticalOffset: 0
-    property real secondaryHorizontalOffset: 0
-    property real secondaryBlurRadius: 0
-
-    property alias sourceSize: image.sourceSize
-    property alias cache: image.cache
-    property alias asynchronous: image.asynchronous
-    property alias fillMode: image.fillMode
-
-    z: -1
+    id: root
 
-    visible: (width > 0 && height > 0)
+    property alias radius: primaryShadow.radius
 
-    //by default we request
-    sourceSize: Qt.size(viewportWidth, viewportHeight)
+    property alias primaryColor: primaryShadow.color
+    property alias primaryVerticalOffset: primaryShadow.yOffset
+    property alias primaryHorizontalOffset: primaryShadow.xOffset
+    property alias primaryBlurRadius: primaryShadow.blurRadius
 
-    ScaledImage {
-        id: image
+    property alias secondaryColor: secondaryShadow.color
+    property alias secondaryVerticalOffset: secondaryShadow.yOffset
+    property alias secondaryHorizontalOffset: secondaryShadow.xOffset
+    property alias secondaryBlurRadius: secondaryShadow.blurRadius
 
-        anchors.fill: parent
+    primaryColor: Qt.rgba(0, 0, 0, .18)
+    secondaryColor: Qt.rgba(0, 0, 0, .22)
 
-        cache: true
-        asynchronous: true
+    RoundedRectangleShadow {
+        id: primaryShadow
 
-        visible: !visualDelegate.readyForVisibility
+        parent: root.parent // similar to how Repeater behaves
 
-        fillMode: Image.Stretch
-
-        onSourceSizeChanged: {
-            // Do not load the image when size is not valid:
-            if (sourceSize.width > 0 && sourceSize.height > 0)
-                source = Qt.binding(() => {
-                    return Effects.url(
-                        Effects.DoubleRoundedRectDropShadow,
-                        {
-                            "viewportWidth" : viewportWidth,
-                            "viewportHeight" :viewportHeight,
-
-                            "rectWidth": rectWidth,
-                            "rectHeight": rectHeight,
-                            "xRadius": xRadius,
-                            "yRadius": yRadius,
-
-                            "primaryColor": primaryColor,
-                            "primaryBlurRadius": primaryBlurRadius,
-                            "primaryXOffset": primaryHorizontalOffset,
-                            "primaryYOffset": primaryVerticalOffset,
-
-                            "secondaryColor": secondaryColor,
-                            "secondaryBlurRadius": secondaryBlurRadius,
-                            "secondaryXOffset": secondaryHorizontalOffset,
-                            "secondaryYOffset": secondaryVerticalOffset,
-                        })
-                })
-            else
-                source = ""
-        }
+        opacity: root.opacity
+        visible: root.visible
+        enabled: root.enabled
     }
 
-    ShaderEffect {
-        id: visualDelegate
-
-        anchors.centerIn: parent
-        anchors.alignWhenCentered: true
-
-        implicitWidth: image.implicitWidth
-        implicitHeight: image.implicitHeight
-
-        width: image.paintedWidth
-        height: image.paintedHeight
-
-        visible: readyForVisibility
-
-        readonly property bool readyForVisibility: (GraphicsInfo.shaderType === GraphicsInfo.RhiShader)
+    RoundedRectangleShadow {
+        id: secondaryShadow
 
-        supportsAtlasTextures: true
-        blending: true
-        // cullMode: ShaderEffect.BackFaceCulling
+        parent: root.parent // similar to how Repeater behaves
 
-        readonly property Image source: image
+        opacity: root.opacity
+        visible: root.visible
+        enabled: root.enabled
 
-        // TODO: Dithered texture is not necessary if theme is not dark.
-        fragmentShader: "qrc:///shaders/DitheredTexture.frag.qsb"
+        radius: root.radius
     }
 }


=====================================
modules/gui/qt/widgets/qml/DragItem.qml
=====================================
@@ -510,9 +510,21 @@ Item {
                 }
 
                 DefaultShadow {
+
+                }
+
+                // FIXME: Qt bug (observed 6.2 and 6.8): The image does not get rendered without this.
+                Rectangle {
+                    z: -1
+
                     anchors.centerIn: parent
 
-                    sourceItem: parent
+                    width: 1
+                    height: 1
+
+                    opacity: 0.01
+
+                    color: "black"
                 }
             }
         }
@@ -544,9 +556,7 @@ Item {
         }
 
         DefaultShadow {
-            anchors.centerIn: parent
 
-            sourceItem: extraCovers
         }
     }
 


=====================================
modules/gui/qt/widgets/qml/DropShadowImage.qml
=====================================
@@ -20,6 +20,8 @@ import QtQuick
 
 import VLC.Util
 
+
+// WARNING: This is deprecated in favor of RoundedRectangleShadow, and may be removed at any time. Avoid using it.
 Item {
     implicitWidth: image.implicitWidth
     implicitHeight: image.implicitHeight


=====================================
modules/gui/qt/widgets/qml/EmptyLabel.qml
=====================================
@@ -76,9 +76,7 @@ T.Control {
             fillMode: Image.PreserveAspectFit
 
             Widgets.DefaultShadow {
-                anchors.centerIn: parent
 
-                sourceItem: parent
             }
         }
 


=====================================
modules/gui/qt/widgets/qml/FloatingNotification.qml
=====================================
@@ -156,9 +156,7 @@ Column {
             }
 
             DefaultShadow {
-                anchors.centerIn: parent
 
-                sourceItem: parent
             }
 
             SubtitleLabel {


=====================================
modules/gui/qt/widgets/qml/GridItem.qml
=====================================
@@ -297,41 +297,15 @@ T.ItemDelegate {
             DefaultShadow {
                 id: unselectedShadow
 
-                anchors.centerIn: parent
-
                 visible: opacity > 0
-
-                sourceItem: parent
-
-                width: picture.paintedWidth + viewportHorizontalOffset - Math.ceil(picture.padding) * 2
-                height: picture.paintedHeight + viewportVerticalOffset - Math.ceil(picture.padding) * 2
-
-                rectWidth: sourceSize.width
-                rectHeight: sourceSize.height
-
-                // TODO: Apply painted size's aspect ratio (constant) in source size
-                sourceSize: Qt.size(128, 128)
             }
 
             DoubleShadow {
                 id: selectedShadow
 
-                anchors.centerIn: parent
-
                 visible: opacity > 0
                 opacity: 0
 
-                sourceItem: parent
-
-                width: picture.paintedWidth + viewportHorizontalOffset - Math.ceil(picture.padding) * 2
-                height: picture.paintedHeight + viewportVerticalOffset - Math.ceil(picture.padding) * 2
-
-                rectWidth: sourceSize.width
-                rectHeight: sourceSize.height
-
-                // TODO: Apply painted size's aspect ratio (constant) in source size
-                sourceSize: Qt.size(128, 128)
-
                 primaryVerticalOffset: VLCStyle.dp(6, VLCStyle.scale)
                 primaryBlurRadius: VLCStyle.dp(18, VLCStyle.scale)
 


=====================================
modules/gui/qt/widgets/qml/DynamicShadow.qml → modules/gui/qt/widgets/qml/RoundedRectangleShadow.qml
=====================================
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (C) 2024 VLC authors and VideoLAN
+ * Copyright (C) 2025 VLC authors and VideoLAN
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -16,37 +16,46 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 import QtQuick
-import QtQuick.Effects
 
-MultiEffect {
-    id: effect
+// This item uses Evan Wallace's Fast Rounded Rectangle Shadow (CC0).
+// This is considered a better approach than any texture based approach (whether GPU or CPU generated).
+// Regarding non-texture based solutions, it has the advantage over Qt 6.9's `RectangularShadow` or
+// Qt 5's `RectangularGlow` that this uses proper approximation of gaussian blur instead of relying on
+// `smoothstep()`, which I assume is good for glow effect but not for shadows.
+ShaderEffect {
+    implicitWidth: parent ? Math.min((parent.paintedWidth ?? Number.MAX_VALUE) - Math.ceil(parent.padding ?? 0) * 2, parent.width) + (blurRadius * compensationFactor * 2)
+                          : 0
+    implicitHeight: parent ? Math.min((parent.paintedHeight ?? Number.MAX_VALUE) - Math.ceil(parent.padding ?? 0) * 2, parent.height) + (blurRadius * compensationFactor * 2)
+                           : 0
 
-    implicitWidth: sourceItem ? Math.min(sourceItem.paintedWidth ?? Number.MAX_VALUE, sourceItem.width) : 0
-    implicitHeight: sourceItem ? Math.min(sourceItem.paintedHeight ?? Number.MAX_VALUE, sourceItem.height) : 0
+    z: -1
 
-    shadowEnabled: true
-    shadowBlur: 1.0
+    anchors.centerIn: parent
+    anchors.horizontalCenterOffset: xOffset
+    anchors.verticalCenterOffset: yOffset
 
-    paddingRect: Qt.rect(xOffset, yOffset, 0, 0)
+    // Any of these properties can be freely animated:
+    property real blurRadius: 0.0
+    property color color
 
-    visible: (GraphicsInfo.shaderType === GraphicsInfo.RhiShader)
+    // These are only respected when anchoring is used to center the item:
+    property real xOffset
+    property real yOffset
 
-    property alias sourceItem: effect.source
+    // Currently different xRadius/yRadius is not supported.
+    property real radius: (parent ? (parent.effectiveRadius ?? parent.radius) : 0) ?? 0
 
-    property alias xOffset: effect.shadowHorizontalOffset
-    property alias yOffset: effect.shadowVerticalOffset
+    readonly property size size: Qt.size(width, height)
 
-    property alias blurRadius: effect.blurMax
+    // Increase the compensation factor if clipping occurs, but make it as small as possible
+    // to prevent overlapping shadows (breaks batching) and to decrease the shader coverage:
+    property real compensationFactor: 2.0
 
-    property alias color: effect.shadowColor
+    blending: true
 
-    Binding {
-        // Source item should be made invisible
-        // since multi effect already renders
-        // the source item.
-        when: sourceItem && effect.visible
-        target: sourceItem
-        property: "visible"
-        value: false
-    }
+    supportsAtlasTextures: true // irrelevant, but nevertheless...
+
+    // cullMode: ShaderEffect.BackFaceCulling // problematic with item layers
+
+    fragmentShader: "qrc:///shaders/RoundedRectangleShadow.frag.qsb"
 }


=====================================
modules/gui/qt/widgets/qml/TableColumns.qml
=====================================
@@ -135,9 +135,7 @@ Item {
                 }
 
                 DefaultShadow {
-                    anchors.centerIn: parent
 
-                    sourceItem: parent
                 }
 
                 onPlayIconClicked: root.playClicked(titleDel.rowModel)


=====================================
modules/gui/qt/widgets/qml/compat/DynamicShadow.qml deleted
=====================================
@@ -1,24 +0,0 @@
-/*****************************************************************************
- * Copyright (C) 2024 VLC authors and VideoLAN
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * ( at your option ) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-import QtQuick
-
-import VLC.Widgets
-
-DropShadowImage {
-    cache: false
-}



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/3a3d8dd7c2b773a8133e242a5fb52dc82d8c12d5...ed50b45d958a1d46a6272cd6252206be32082619

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/3a3d8dd7c2b773a8133e242a5fb52dc82d8c12d5...ed50b45d958a1d46a6272cd6252206be32082619
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list