waycrate / wayshot

Mirrored at https://git.sr.ht/~shinyzenith/wayshot | screenshot tool for wlroots based compositors implementing zwlr_screencopy_v1
https://crates.io/crates/wayshot
BSD 2-Clause "Simplified" License
114 stars 20 forks source link

[FR] Optimize png with oxipng #95

Open murlakatamenka opened 7 months ago

murlakatamenka commented 7 months ago

png by default is a reasonable choice. However, it's surely doesn't offer the best compression as of today. For instance, lossless webp consistently beats png in terms of compression ratio.

I suggest adding a separate flag to optimize png output via awesome oxipng crate, something like:

wayshot --extension png --optimize -f /tmp/test.png

It is easy via oxipng::optimize_from_memory:

let opts = oxipng::Options::default(); // = preset 2
// let opts = oxipng::Options::from_preset(<u8 from 0 to 6>);
let optimized_png_buf = oxipng::optimize_from_memory(&png_buf, &opts)?;

Here is a little script to evaluate the savings:

mkdir /tmp/wayshot && cd /tmp/wayshot

wayshot -f wayshot.png

for i in {0..6}; do
  time oxipng --opt "$i" --pretend wayshot.png
done

Results for a sample image (2560x1080, 414 KiB) on Ryzen 5600:

oxipng 9.0
293709 bytes (30.67% smaller)
oxipng --opt "$i" --pretend wayshot.png  0.14s user 0.00s system 99% cpu 0.143 total
---
279314 bytes (34.07% smaller)
oxipng --opt "$i" --pretend wayshot.png  0.31s user 0.01s system 99% cpu 0.322 total
---
274489 bytes (35.21% smaller)
oxipng --opt "$i" --pretend wayshot.png  0.63s user 0.02s system 129% cpu 0.500 total
---
265868 bytes (37.24% smaller)
oxipng --opt "$i" --pretend wayshot.png  2.23s user 0.02s system 264% cpu 0.852 total
---
265107 bytes (37.42% smaller)
oxipng --opt "$i" --pretend wayshot.png  3.77s user 0.03s system 303% cpu 1.252 total
---
265107 bytes (37.42% smaller)
oxipng --opt "$i" --pretend wayshot.png  8.67s user 0.04s system 580% cpu 1.498 total
---
265107 bytes (37.42% smaller)
oxipng --opt "$i" --pretend wayshot.png  12.80s user 0.05s system 781% cpu 1.643 total

Pretty good imo, and using oxipng from Rust itself will be even faster (no CLI args parsing, no buffer copy).

Reasonable tradeoff of little extra CPU for decent savings in file size even with fast-ish presets 0-2.


Surely I can do the plumbing myself, wayshot --stdout | <best-image-encoder> - <name.ext>, but I think wayshot and its users will benefit from implementing this feature.

Shinyzenith commented 7 months ago

I actually wouldn't mind making this a default. This sounds like a good FR. Is there any way we can natively implement this optimization set using just the image crate? I'd prefer not expanding the dependency graph unless needed.

murlakatamenka commented 7 months ago

So far I don't think you can avoid pulling oxipng as a dependency.

Seeing your positive reaction I'll work on PR, we'll see how it goes there.

Shinyzenith commented 7 months ago

That sounds great, I wouldn't mind a PR for further discussion.