Expand description
How many pixels it is safe to keep, and how that number is reached.
The one place a cap lives, and pure arithmetic — every test here takes numbers and touches no file.
§Why a viewer’s rule is the opposite of a thumbnail’s
hyprforge-clipmenu refuses any image over 16 megapixels and shows a
text row instead, which is right for a clipboard popup: the picture is
a convenience and the popup has other rows to draw. A viewer may not
refuse. Showing the photograph is the app, and “that image is too
big” is not an answer anyone accepts from the program they opened it
with.
So the rule inverts: decode to fit the window, not the file. A 36-megapixel photograph on a 2560x1600 display has no business becoming a 144MB buffer, because the screen cannot show more than four million of those pixels at once. What is retained is bounded by the viewport and stays bounded no matter what the camera produced.
§What this does and does not buy
Honest about its own limits, because a budget that is believed to do more than it does is worse than none:
- Retained memory is capped. That is the allocation that lives for as long as the picture is on screen, and the one a cache multiplies.
- Transient memory is not, and cannot be here:
image0.25 exposes no DCT-scaled decode, so a JPEG is decoded whole and then scaled down. The peak is a property of the source, not of this budget.
§The measured numbers
A 36-megapixel JPEG (8001x4501, 2.8MB on disk — a file no larger than
an email attachment) shown in a 2560x1600 viewport, measured through
/proc/self/status’s VmHWM in a process that had allocated nothing
else:
| peak | retained | |
|---|---|---|
decoded and scaled with resize_exact | 509MB | 56MB |
| decoded with no scaling at all | 252MB | 137MB |
decoded and scaled with thumbnail_exact | 214MB | 56MB |
Two things in that table were worth the measuring. The scaling step
cost more than the decode it followed — resize_exact works through
floating-point intermediates, and on a 36-megapixel source those are
larger than the picture. And the cheap integer path is not merely
cheaper than the expensive one, it is cheaper than not scaling,
because it never materialises the full-size RGBA buffer on the way.
decode uses the third row. See its note on why that is not a
quality compromise at this ratio.
What is left is a 214MB peak on a machine with any amount of memory, and the lock screen’s lesson is that a peak is not free just because it is brief. Bringing it lower needs a decoder that can scale while decoding, which is a change of dependency rather than of arithmetic.
Structs§
- Budget
- How much of a picture may be kept in memory at once.
- Decode
Pixels - The size something will be decoded to — always at or under the source, never larger. A viewer showing a 64x64 icon full-screen scales it up when drawing; decoding it big would allocate a buffer full of invented pixels.
- Viewport
Pixels - The size of the area a picture is being shown in, in physical pixels — the buffer’s unit, not the widget tree’s.