[vlc-commits] [Git][videolan/vlc][master] 7 commits: qt: do not tint alpha channel in `DualKawaseBlur.frag`

Steve Lhomme (@robUx4) gitlab at videolan.org
Thu Dec 18 12:01:29 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
7a211a7d by Fatih Uzunoglu at 2025-12-18T11:38:17+00:00
qt: do not tint alpha channel in `DualKawaseBlur.frag`

Tinting should not be applied to the alpha channel of
the source.

- - - - -
b010ebf1 by Fatih Uzunoglu at 2025-12-18T11:38:17+00:00
qt: introduce background coloring in `DualKawaseBlur.frag`

- - - - -
aad71ed0 by Fatih Uzunoglu at 2025-12-18T11:38:17+00:00
qt: expose background coloring in `DualKawaseBlur.qml`

- - - - -
b1e2a426 by Fatih Uzunoglu at 2025-12-18T11:38:17+00:00
qml: use bg coloring if source is translucent in blur effect of `ArtistTopBanner`

- - - - -
b8d2c949 by Fatih Uzunoglu at 2025-12-18T11:38:17+00:00
qml: provide `available` in `DualKawaseBlur`

- - - - -
f79b4c08 by Fatih Uzunoglu at 2025-12-18T11:38:17+00:00
qml: use background coloring in blur effect of `Player`

- - - - -
918c2d38 by Fatih Uzunoglu at 2025-12-18T11:38:17+00:00
qml: get rid of unnecessary `Item` in `ArtistTopBanner`

- - - - -


7 changed files:

- modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
- modules/gui/qt/player/qml/Player.qml
- modules/gui/qt/shaders/DualKawaseBlur.frag
- modules/gui/qt/shaders/DualKawaseBlur_downsample.frag
- modules/gui/qt/shaders/DualKawaseBlur_upsample.frag
- modules/gui/qt/shaders/DualKawaseBlur_upsample_postprocess.frag
- modules/gui/qt/widgets/qml/DualKawaseBlur.qml


Changes:

=====================================
modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
=====================================
@@ -74,63 +74,61 @@ FocusScope {
         opacity: blurEffect.visible ? 1.0 : 0.5
     }
 
-    Item {
-        anchors.fill: background
-
-        visible: (GraphicsInfo.shaderType === GraphicsInfo.RhiShader) && (root.artist.id) // do not display the effect during initialization
+    Widgets.DualKawaseBlur {
+        id: blurEffect
 
-        // This blur effect does not create an implicit layer that is updated
-        // each time the size changes. The source texture is static, so the blur
-        // is applied only once and we adjust the viewport through the parent item
-        // with clipping.
-        Widgets.DualKawaseBlur {
-            id: blurEffect
+        anchors.verticalCenter: background.verticalCenter
+        anchors.left: background.left
+        anchors.right: background.right
 
-            anchors.verticalCenter: parent.verticalCenter
-            anchors.left: parent.left
-            anchors.right: parent.right
+        visible: (available) && (root.artist.id) // do not display the effect during initialization
 
-            // NOTE: No need to disable `live`, as this uses two pass mode so there is no video memory saving benefit.
+        // NOTE: No need to disable `live`, as this uses two pass mode so there is no video memory saving benefit.
 
-            readonly property bool sourceNeedsTiling: (background.fillMode === Image.Tile)
+        readonly property bool sourceNeedsTiling: (background.fillMode === Image.Tile)
 
-            readonly property real aspectRatio: (background.implicitHeight / background.implicitWidth)
+        readonly property real aspectRatio: (background.implicitHeight / background.implicitWidth)
 
-            height: sourceNeedsTiling ? background.height : (aspectRatio * width)
+        height: sourceNeedsTiling ? background.height : (aspectRatio * width)
 
-            source: textureProviderItem
+        source: textureProviderItem
 
-            // Instead of clipping in the parent, denote the viewport here so we both
-            // do not need to clip the excess, and also save significant video memory:
-            viewportRect: !blurEffect.sourceNeedsLayering ? Qt.rect((width - parent.width) / 2, (height - parent.height) / 2, parent.width, parent.height)
-                                                          : Qt.rect(0, 0, 0, 0)
+        // Instead of clipping in the parent, denote the viewport here so we both
+        // do not need to clip the excess, and also save significant video memory:
+        viewportRect: !blurEffect.sourceNeedsLayering ? Qt.rect((width - background.width) / 2,
+                                                                (height - background.height) / 2,
+                                                                background.width,
+                                                                background.height)
+                                                      : Qt.rect(0, 0, 0, 0)
 
-            Widgets.TextureProviderItem {
-                id: textureProviderItem
+        backgroundColor: theme.bg.primary
+        postprocess: sourceTextureProviderObserver.hasAlphaChannel
 
-                // Like in `Player.qml`, this is used because when the source is
-                // mipmapped, sometimes it can not be sampled. This is considered
-                // a Qt bug, but `QSGTextureView` has a workaround for that. So,
-                // we can have an indirection here through `TextureProviderItem`.
-                // This is totally acceptable as there is virtually no overhead.
+        Widgets.TextureProviderItem {
+            id: textureProviderItem
 
-                source: background
+            // Like in `Player.qml`, this is used because when the source is
+            // mipmapped, sometimes it can not be sampled. This is considered
+            // a Qt bug, but `QSGTextureView` has a workaround for that. So,
+            // we can have an indirection here through `TextureProviderItem`.
+            // This is totally acceptable as there is virtually no overhead.
 
-                detachAtlasTextures: blurEffect.sourceNeedsTiling
+            source: background
 
-                horizontalWrapMode: blurEffect.sourceNeedsTiling ? Widgets.TextureProviderItem.Repeat : Widgets.TextureProviderItem.ClampToEdge
-                verticalWrapMode: blurEffect.sourceNeedsTiling ? Widgets.TextureProviderItem.Repeat : Widgets.TextureProviderItem.ClampToEdge
+            detachAtlasTextures: blurEffect.sourceNeedsTiling
 
-                textureSubRect: blurEffect.sourceNeedsTiling ? Qt.rect(blurEffect.width / 8,
-                                                                       blurEffect.height / 8,
-                                                                       blurEffect.width * 1.25,
-                                                                       blurEffect.height * 1.25) : undefined
-            }
+            horizontalWrapMode: blurEffect.sourceNeedsTiling ? Widgets.TextureProviderItem.Repeat : Widgets.TextureProviderItem.ClampToEdge
+            verticalWrapMode: blurEffect.sourceNeedsTiling ? Widgets.TextureProviderItem.Repeat : Widgets.TextureProviderItem.ClampToEdge
 
-            // Strong blurring is not wanted here:
-            mode: Widgets.DualKawaseBlur.Mode.TwoPass
-            radius: 1
+            textureSubRect: blurEffect.sourceNeedsTiling ? Qt.rect(blurEffect.width / 8,
+                                                                   blurEffect.height / 8,
+                                                                   blurEffect.width * 1.25,
+                                                                   blurEffect.height * 1.25) : undefined
         }
+
+        // Strong blurring is not wanted here:
+        mode: Widgets.DualKawaseBlur.Mode.TwoPass
+        radius: 1
     }
 
     Rectangle {


=====================================
modules/gui/qt/player/qml/Player.qml
=====================================
@@ -277,7 +277,9 @@ FocusScope {
                 // background image
                 Rectangle {
                     focus: false
-                    color: bgtheme.bg.primary
+                    // NOTE: Rectangle has an optimization that it does not use a scene graph node if the color is transparent.
+                    color: blurredBackground.available ? "transparent" // background coloring in blur effect is used otherwise
+                                                       : bgtheme.bg.primary
                     anchors.fill: parent
 
                     readonly property ColorContext colorContext: ColorContext {
@@ -304,6 +306,7 @@ FocusScope {
                         postprocess: true
                         tint: bgtheme.palette.isDark ? "black" : "white"
                         tintStrength: 0.5
+                        backgroundColor: bgtheme.bg.primary
 
                         // The window naturally clips the content, but having this saves some
                         // video memory, depending on the excess content in the last layer:


=====================================
modules/gui/qt/shaders/DualKawaseBlur.frag
=====================================
@@ -35,6 +35,7 @@ layout(std140, binding = 0) uniform qt_buf {
 
 #ifdef POSTPROCESS
     vec4 tint;
+    vec4 backgroundColor;
     float tintStrength;
     float exclusionStrength;
     float noiseStrength;
@@ -123,14 +124,18 @@ void main()
     result = exclude(result, exclColor);
 
     // Tint:
-    result = mix(result, fromPremult(tint), tintStrength);
+    result.rgb = mix(result.rgb, fromPremult(tint).rgb, tintStrength);
+
+    result = toPremult(result);
+
+    // Background color:
+    // Source over blending, premultiplied (S + D * (1 - S.a)):
+    result = result + backgroundColor * (1.0 - result.a);
 
     // Noise:
     float r = rand(qt_TexCoord0) - 0.5;
-    vec4 noise = vec4(r,r,r,1.0) * noiseStrength;
+    vec4 noise = vec4(r,r,r,r) * noiseStrength;
     result += noise;
-
-    result = toPremult(result);
 #endif
 
     fragColor = result * qt_Opacity; // premultiplied alpha


=====================================
modules/gui/qt/shaders/DualKawaseBlur_downsample.frag
=====================================
@@ -39,6 +39,7 @@ layout(std140, binding = 0) uniform qt_buf {
 
 #ifdef POSTPROCESS
     vec4 tint;
+    vec4 backgroundColor;
     float tintStrength;
     float exclusionStrength;
     float noiseStrength;
@@ -127,14 +128,18 @@ void main()
     result = exclude(result, exclColor);
 
     // Tint:
-    result = mix(result, fromPremult(tint), tintStrength);
+    result.rgb = mix(result.rgb, fromPremult(tint).rgb, tintStrength);
+
+    result = toPremult(result);
+
+    // Background color:
+    // Source over blending, premultiplied (S + D * (1 - S.a)):
+    result = result + backgroundColor * (1.0 - result.a);
 
     // Noise:
     float r = rand(qt_TexCoord0) - 0.5;
-    vec4 noise = vec4(r,r,r,1.0) * noiseStrength;
+    vec4 noise = vec4(r,r,r,r) * noiseStrength;
     result += noise;
-
-    result = toPremult(result);
 #endif
 
     fragColor = result * qt_Opacity; // premultiplied alpha


=====================================
modules/gui/qt/shaders/DualKawaseBlur_upsample.frag
=====================================
@@ -39,6 +39,7 @@ layout(std140, binding = 0) uniform qt_buf {
 
 #ifdef POSTPROCESS
     vec4 tint;
+    vec4 backgroundColor;
     float tintStrength;
     float exclusionStrength;
     float noiseStrength;
@@ -127,14 +128,18 @@ void main()
     result = exclude(result, exclColor);
 
     // Tint:
-    result = mix(result, fromPremult(tint), tintStrength);
+    result.rgb = mix(result.rgb, fromPremult(tint).rgb, tintStrength);
+
+    result = toPremult(result);
+
+    // Background color:
+    // Source over blending, premultiplied (S + D * (1 - S.a)):
+    result = result + backgroundColor * (1.0 - result.a);
 
     // Noise:
     float r = rand(qt_TexCoord0) - 0.5;
-    vec4 noise = vec4(r,r,r,1.0) * noiseStrength;
+    vec4 noise = vec4(r,r,r,r) * noiseStrength;
     result += noise;
-
-    result = toPremult(result);
 #endif
 
     fragColor = result * qt_Opacity; // premultiplied alpha


=====================================
modules/gui/qt/shaders/DualKawaseBlur_upsample_postprocess.frag
=====================================
@@ -40,6 +40,7 @@ layout(std140, binding = 0) uniform qt_buf {
 
 #ifdef POSTPROCESS
     vec4 tint;
+    vec4 backgroundColor;
     float tintStrength;
     float exclusionStrength;
     float noiseStrength;
@@ -128,14 +129,18 @@ void main()
     result = exclude(result, exclColor);
 
     // Tint:
-    result = mix(result, fromPremult(tint), tintStrength);
+    result.rgb = mix(result.rgb, fromPremult(tint).rgb, tintStrength);
+
+    result = toPremult(result);
+
+    // Background color:
+    // Source over blending, premultiplied (S + D * (1 - S.a)):
+    result = result + backgroundColor * (1.0 - result.a);
 
     // Noise:
     float r = rand(qt_TexCoord0) - 0.5;
-    vec4 noise = vec4(r,r,r,1.0) * noiseStrength;
+    vec4 noise = vec4(r,r,r,r) * noiseStrength;
     result += noise;
-
-    result = toPremult(result);
 #endif
 
     fragColor = result * qt_Opacity; // premultiplied alpha


=====================================
modules/gui/qt/widgets/qml/DualKawaseBlur.qml
=====================================
@@ -34,6 +34,8 @@ Item {
     implicitWidth: source ? Math.min(source.paintedWidth ?? Number.MAX_VALUE, source.width) : 0
     implicitHeight: source ? Math.min(source.paintedHeight ?? Number.MAX_VALUE, source.height) : 0
 
+    readonly property bool available: (GraphicsInfo.shaderType === GraphicsInfo.RhiShader)
+
     enum Mode {
         FourPass, // 2 downsample + 2 upsamples (3 layers/buffers)
         TwoPass // 1 downsample + 1 upsample (1 layer/buffer)
@@ -49,6 +51,7 @@ Item {
     property real tintStrength: 0.0
     property real noiseStrength: 0.0
     property real exclusionStrength: 0.0
+    property color backgroundColor: "transparent"
     /// </postprocess>
 
     // NOTE: This property is also an optimization hint. When it is false, the
@@ -64,12 +67,19 @@ Item {
     // The effective radius is always going to be a half-integer.
     property int radius: 1
 
+    // NOTE: This property concerns the blending in the scene graph, not the internal
+    //       blending used with regard to background coloring. In that case blending
+    //       is always done.
     // NOTE: It seems that if SG accumulated opacity is lower than 1.0, blending is
     //       used even if it is set false here. For that reason, it should not be
     //       necessary to check for opacity (well, accumulated opacity can not be
     //       checked directly in QML anyway).
-    property bool blending: (!sourceTextureIsValid || sourceTextureProviderObserver.hasAlphaChannel ||
-                             (postprocess && (tintStrength > 0.0 && tint.a < 1.0)))
+    property bool blending: (postprocess &&
+                             backgroundColor.a > (1.0 - Number.EPSILON)) ? false // the result is opaque, no need for sg blending
+                                                                         : _sourceIsTranslucent
+
+    // WARNING: If texture is not valid/ready, Qt generates a transparent texture to use as source.
+    readonly property bool _sourceIsTranslucent: (!sourceTextureIsValid || sourceTextureProviderObserver.hasAlphaChannel)
 
     // source must be a texture provider item. Some items such as `Image` and
     // `ShaderEffectSource` are inherently texture provider. Other items needs
@@ -122,7 +132,7 @@ Item {
         if (live)
             return // no-op
 
-        if (root.GraphicsInfo.shaderType !== GraphicsInfo.RhiShader)
+        if (!root.available)
             return // not applicable
 
         if (!root.sourceTextureIsValid) {
@@ -432,6 +442,7 @@ Item {
         property alias tintStrength: root.tintStrength
         property alias noiseStrength: root.noiseStrength
         property alias exclusionStrength: root.exclusionStrength
+        property alias backgroundColor: root.backgroundColor
 
         fragmentShader: root.postprocess ? "qrc:///shaders/DualKawaseBlur_upsample_postprocess.frag.qsb"
                                          : "qrc:///shaders/DualKawaseBlur_upsample.frag.qsb"



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/3cd86f0c90ee367a307b4d19802675c82ea5020b...918c2d380ad4fc63a9af223e57be6d33ba6fd835

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/3cd86f0c90ee367a307b4d19802675c82ea5020b...918c2d380ad4fc63a9af223e57be6d33ba6fd835
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