Skip to content

Decode wallpapers at the size the screen can show - #9417

Open
VykosMolt wants to merge 1 commit into
omacom:quattrofrom
VykosMolt:fix/wallpaper-decode-size
Open

Decode wallpapers at the size the screen can show#9417
VykosMolt wants to merge 1 commit into
omacom:quattrofrom
VykosMolt:fix/wallpaper-decode-size

Conversation

@VykosMolt

Copy link
Copy Markdown

The three Image elements in Background.qml set no sourceSize, so each decodes its wallpaper at the file's native resolution and holds it that way for as long as the background is up. A decoded image is uncompressed no matter how small the file on disk is: retro-82/5-zen-boat.webp is 10456x3455, which is 138MB of RGBA sitting in memory for a 2560x1600 screen that can show 16MB of it. 37 of the 92 wallpapers shipped in themes/ are larger than the screen they get drawn on, and the two transition frames carry mipmap: true, which asks for roughly another third on top of whatever was decoded.

Each Image now caps sourceSize, so the decoder scales the image down as it reads it and the oversized buffer is never allocated in the first place. oldFrame and incomingFrame take their size from base, so the three frames of a cross-fade always agree on one size.

The cap is 1.6x the screen rather than exactly the screen, because setting both sourceSize dimensions makes Qt fit the image inside that box while preserving aspect. A box exactly the size of the screen would decode a 2.39:1 wallpaper too short to cover a 16:10 display, and PreserveAspectCrop would then scale it back up to fill, which is the blur this is meant to avoid. 1.6x covers every aspect ratio up to 1.6 times the screen's own and still leaves the largest wallpapers far under their native size.

The multiplier is the attached Screen.devicePixelRatio, which is what Menu.qml, Tray.qml and NotificationCard.qml already use. parent.screen is not an option here: a child declared inside a PanelWindow is reparented to the window's contentItem, and a QQuickItem has no screen property, so reading it yields undefined and would quietly decode every wallpaper at 1x. I had that wrong in a first draft and the attached property is the one that resolves.

This is independent of #9312 and #9313, which change which background file gets resolved and served. This caps how large whatever is served decodes, so it still applies on top of either.

Testing

background-test.sh gains three assertions: the base image caps its decode against the screen size and device pixel ratio, the two transition frames decode at the same size as the wallpaper they cross-fade, and every Image in the file caps its decode. The first fails on current quattro, which sets no sourceSize anywhere in the file.

Decoded RGBA for the wallpapers shipped in themes/, measured against a 2560x1600 screen:

                                              native      capped
retro-82/5-zen-boat.webp        10456x3455     138 MB       21 MB
osaka-jade/3-mountain-moon.webp  7680x4320     127 MB       36 MB
vantablack/1-twisted-stairs.webp 7680x4320     127 MB       36 MB
tokyo-night/4-omakub.webp        6930x3960     105 MB       37 MB
white/3-white.webp               6240x4160      99 MB       38 MB
tokyo-night/3-sunset-lake.webp   7680x3215      94 MB       27 MB

37 of the 92 shipped wallpapers shrink like this. The other 55 are already inside the box and decode exactly as they do today, so this caps the tail rather than changing the common case. On a smaller or lower-DPI screen the box is smaller and more of them are affected.

./test/shell — 217 of 221 files pass; the four failures (config-test, runtime-smoke-test, snapper-test, unowned-system-paths-test) fail identically on unmodified quattro on this machine.

This comes out of https://github.com/VykosMolt/omarchy-desktop, where I have been running the Quattro shell as a plain Arch session and fixing what turned up.

The three `Image` elements in `Background.qml` set no `sourceSize`, so each one
decodes its wallpaper at the file's native resolution and keeps it that way for
as long as the background is up. A decoded image is uncompressed regardless of
how small the file on disk is: `retro-82/5-zen-boat.webp` is 10456x3455, which
is 138MB of RGBA held for a 2560x1600 screen that can show 16MB of it. 37 of the
92 wallpapers shipped in `themes/` are larger than the screens they are drawn
on, and the two transition frames carry `mipmap: true`, which asks for another
third on top of whatever was decoded.

Each `Image` now caps `sourceSize`, so the decoder scales the image down as it
reads it and the oversized buffer is never allocated. `oldFrame` and
`incomingFrame` take their size from `base` so the three frames of a cross-fade
always agree.

The cap is 1.6x the screen rather than exactly the screen because setting both
`sourceSize` dimensions makes Qt fit the image inside that box while preserving
aspect. A box exactly the size of the screen would decode a 2.39:1 wallpaper too
short to cover a 16:10 display, and `PreserveAspectCrop` would scale it back up
to fill, which is the blur this is meant to avoid. 1.6x covers every aspect ratio
up to 1.6 times the screen's own and still leaves the largest wallpapers well
under their native size.

The multiplier is the attached `Screen.devicePixelRatio`, matching `Menu.qml`,
`Tray.qml` and `NotificationCard.qml`. `parent.screen` is not an option here: a
child declared inside a `PanelWindow` is reparented to the window's
`contentItem`, and a `QQuickItem` has no `screen` property, so reading it would
silently yield `undefined` and decode every wallpaper at 1x.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant