[vlc-commits] [Git][videolan/vlc][master] 3 commits: qml: make default `Image` visible only when image is ready in `ImageExt`

Steve Lhomme (@robUx4) gitlab at videolan.org
Sun Nov 9 18:38:32 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
168087d6 by Fatih Uzunoglu at 2025-11-09T18:23:16+00:00
qml: make default `Image` visible only when image is ready in `ImageExt`

This is to prevent intermediate scene graph node
initialization (if any) until the image loads,
because the `ShaderEffect` is the visual delegate
here and `Image` will not be visible by default.

- - - - -
2fd1c183 by Fatih Uzunoglu at 2025-11-09T18:23:16+00:00
qml: calculate painted size manually at all times in `ImageExt`

Calculating painted size turned out to be trivial, so we can
calculate the size ourselves at all times and not rely on
`Image`'s calculation because although it provides the painted
size when it is hidden at the moment, it is not guaranteed to
be the case in the future (as noted in the comments previously).

This allows us to remove the warning regarding supporting
fill modes that are different than preserve aspect crop (as
for that we already do the necessary calculation ourselves in
`cropRate`).

- - - - -
0c0b6035 by Fatih Uzunoglu at 2025-11-09T18:23:16+00:00
qml: make `EnhancedImageExt` respect tile fill mode by default

- - - - -


2 changed files:

- modules/gui/qt/widgets/qml/EnhancedImageExt.qml
- modules/gui/qt/widgets/qml/ImageExt.qml


Changes:

=====================================
modules/gui/qt/widgets/qml/EnhancedImageExt.qml
=====================================
@@ -55,6 +55,17 @@ ImageExt {
         implicitWidth: (source instanceof Image) ? source.implicitWidth : textureSize.width
         implicitHeight: (source instanceof Image) ? source.implicitHeight : textureSize.height
 
+        readonly property bool sourceNeedsTiling: (root.fillMode === Image.Tile ||
+                                                   root.fillMode === Image.TileVertically ||
+                                                   root.fillMode === Image.TileHorizontally)
+
+        detachAtlasTextures: sourceNeedsTiling
+
+        horizontalWrapMode: sourceNeedsTiling ? TextureProviderItem.Repeat : TextureProviderItem.ClampToEdge
+        verticalWrapMode: sourceNeedsTiling ? TextureProviderItem.Repeat : TextureProviderItem.ClampToEdge
+
+        textureSubRect: sourceNeedsTiling ? Qt.rect(0, 0, root.paintedWidth, root.paintedHeight) : undefined
+
         property size textureSize
 
         Connections {


=====================================
modules/gui/qt/widgets/qml/ImageExt.qml
=====================================
@@ -94,34 +94,14 @@ Item {
     readonly property real paintedHeight: (shaderEffect.readyForVisibility) ? shaderEffect.height
                                                                             : (image.clip ? image.height : image.paintedHeight)
 
-    // NOTE: Fill mode is not guaranteed to be supported,
-    //       it is supported to the extent QQuickImage
-    //       provides properly filled texture, paintedWidth/Height,
-    //       and applies attributes such as tiling (QSGTexture::Repeat)
-    //       to the texture itself WHILE being invisible.
-    //       Invisible items usually are not requested to
-    //       update their paint node, so it is not clear if QQuickImage
-    //       would synchronize QSGTexture attributes with its
-    //       node when it is invisible (rounding is active).
-    // NOTE: Experiments show that preserve aspect ratio can
-    //       be supported, because QQuickImage provides
-    //       appropriate painted size when it is invisible,
-    //       and the generated texture is pre-filled. In
-    //       the future, Qt Quick may prefer doing this
-    //       within its shader, but for now, we should be
-    //       able to use it. Currently, as of Qt 6.8,
-    //       PreserveAspectCrop, PreserveAspectFit, and
-    //       Stretch can be considered supported.
-    // NOTE: If you need a more guaranteed way to preserve
-    //       the aspect ratio, you can use `sourceSize` with
-    //       only one dimension set. Currently `fillMode` is
-    //       preferred instead of `sourceSize` because we need
-    //       to have control over both width and height.
-    // NOTE: Fill mode can be overridden, even when a foreign
-    //       texture provider is used. In that case, `ImageExt`
-    //       makes the required painted size calculations itself.
-    //       The mode is still subject to the same restrictions
-    //       mentioned above.
+    // NOTE: ImageExt calculates the painted size at all times,
+    //       regardless of whether a foreign texture provider
+    //       is used, because although `Image` provides painted
+    //       size when hidden at the moment, this approach is
+    //       considered more reliable.
+    // WARNING: If foreign texture provider is used, tile fill
+    //          modes are not supported by default. For that
+    //          purpose, `EnhancedImageExt` can be used.
     property alias fillMode: image.fillMode
 
     // Unlike QQuickImage where it needs `clip: true` (clip node)
@@ -168,8 +148,8 @@ Item {
         implicitWidth: (source.status === Image.Ready) ? source.implicitWidth : 64
         implicitHeight: (source.status === Image.Ready) ? source.implicitHeight : 64
 
-        width: ((source.status !== Image.Ready) || (root.fillMode === Image.PreserveAspectCrop)) ? root.width : effectivePaintedSize.width
-        height: ((source.status !== Image.Ready) || (root.fillMode === Image.PreserveAspectCrop)) ? root.height : effectivePaintedSize.height
+        width: paintedSize.width
+        height: paintedSize.height
 
         visible: readyForVisibility
 
@@ -229,32 +209,30 @@ Item {
             return ret
         }
 
-        // If source is a foreign `Image`, its `paintedWidth`/`paintedHeight` would not be based on the root size and can not be used.
-        // In that case, we calculate the painted size ourselves (see `effectivePaintedSize`). We also allow overriding the source's
-        // fill mode in that case. If source is a foreign item we still do the same to allow overriding the fill mode here.
-        readonly property size effectivePaintedSize: {
+        readonly property size paintedSize: {
             let ret = Qt.size(0.0, 0.0)
 
-            // No need to calculate if foreign texture provider is not used:
-            if (source === image)
-                return Qt.size(source.paintedWidth, source.paintedHeight)
-
-            // No need to calculate if image is not ready
+            // No need to calculate if the texture is not ready:
             if (source.status !== Image.Ready)
                 return ret
 
-            // NOTE: Calculations are based on `QQuickImage`
-            // WARNING: Note that `PreserveAspectCrop` mode does not use painted size and therefore is not handled here (see `cropRate`).
-            if (root.fillMode === Image.PreserveAspectFit) {
+            // NOTE: Calculations are based on `QQuickImage`,
+            //       except preserve aspect crop (see `cropRate`).
+            if (root.fillMode === Image.PreserveAspectCrop) {
+                return Qt.size(root.width, root.height)
+            } else if (root.fillMode === Image.PreserveAspectFit) {
                 const widthScale = root.width / implicitWidth
                 const heightScale = root.height / implicitHeight
 
-                if (widthScale <= heightScale) {
+                if (widthScale < heightScale) {
                     ret.width = root.width
                     ret.height = widthScale * implicitHeight
-                } else {
+                } else if (widthScale > heightScale) {
                     ret.width = heightScale * implicitWidth
                     ret.height = root.height
+                } else {
+                    ret.width = root.width
+                    ret.height = root.height
                 }
             } else if (root.fillMode === Image.Pad) {
                 ret.width = implicitWidth
@@ -264,6 +242,16 @@ Item {
                 ret.height = root.height
             }
 
+            /// <debug>
+            // Debug only, we use our calculation for reliability regardless of the source:
+            if (source === image) {
+               if (image.paintedWidth !== ret.width || source.paintedHeight !== ret.height) {
+                   console.debug(root, "painted sizes of the default texture provider and the calculation differ: (%1, %2), (%3, %4)"
+                                       .arg(image.paintedWidth).arg(image.paintedHeight).arg(ret.width).arg(ret.height))
+               }
+            }
+            /// </debug>
+
             return ret
         }
 
@@ -295,7 +283,7 @@ Item {
         // because the root item may be invisible (`grabToImage()` call on an invisible
         // item case). In that case, shader effect would report invisible although it
         // would appear in the grabbed image.
-        visible: !shaderEffect.readyForVisibility
+        visible: (status === Image.Ready) && !shaderEffect.readyForVisibility
 
         // Clipping is not a big concern for rendering performance
         // with the software or openvg scene graph adaptations.



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f581aa285168adc7bbc024206cdf87a37a1bfe3e...0c0b60355985f60d162fc428d0da6f69b633dbe6

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f581aa285168adc7bbc024206cdf87a37a1bfe3e...0c0b60355985f60d162fc428d0da6f69b633dbe6
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