My image optimization trio as of now:
- GIF: gifski (squeeze out the best possible out of the ancient format)
- PNG: pngquant (lossy but respectful of what many PNGs are: lots of flat surfaces + limited color palette, transparency)
- JPG: jpegli (writing this post mostly to share the news about this one, it's new from libjxl learnings, approximately as good qualitatively as Guetzli, but 1000x faster -actual number, not an exaggeration-. It's amazing! https://github.com/libjxl/libjxl/tree/main/lib/jpegli )
Below's my lil' `image-opt` helper making for a nice workflow to optimize any gif/jpg/png file(s) :)
#!/usr/bin/env bash
set -euo pipefail
dbg() { if [ "${DEBUG:=false}" = 'true' ]; then echo "$@"; fi }
die() { echo "Error: $*" 1>&2; zenity --error --text="$*"; exit 1; }
if ! command -v pngquant > /dev/null; then die 'You must install pngquant'; fi
if ! command -v cjpegli > /dev/null; then die 'You must install cjpegli'; fi
if ! command -v gifsicle > /dev/null; then die 'You must install gifsicle'; fi
for filename in "${@:1}"; do
if [ -d "$filename" ]; then
echo && echo "ℹ File '$filename' is a directory. Skipping."
continue
fi
if [ ! -f "$filename" ]; then
echo && echo "ℹ File '$filename' does not exist. Skipping."
continue
fi
extension_raw="${filename##*.}"
extension="${extension_raw,,}" # Convert to lowercase
if [[ "$extension" != 'jpg' && "$extension" != 'png' && "$extension" != 'gif' ]]; then
echo && echo "ℹ Skipping file $filename due to unsupported extension: .$extension"
continue
fi
filename_noext="${filename%.*}"
filename_original="${filename_noext}_beforeOpt.${extension_raw}"
filename_final="${filename_noext}.${extension}"
dbg "filename($filename), filename_noext($filename_noext), extension($extension), filename_original($filename_original)"
echo && echo " Compressing $filename ..."
mv "$filename" "$filename_original"
if [ "$extension" == 'jpg' ]; then
cjpegli "$filename_original" "${filename_noext}_q50.${extension}" -q 50
cjpegli "$filename_original" "${filename_noext}_q60.${extension}" -q 60
cjpegli "$filename_original" "${filename_noext}_q70.${extension}" -q 70
cjpegli "$filename_original" "${filename_noext}_q80.${extension}" -q 80
cjpegli "$filename_original" "${filename_noext}_q90.${extension}" -q 90
elif [ "$extension" == 'png' ]; then
pngquant --strip --force --verbose --output "$filename_final" "$filename_original"
elif [ "$extension" == 'gif' ]; then
# https://kornel.ski/lossygif , set --lossy to 20 for light compression, 200 for heavy
gifsicle -O3 --lossy=50 --verbose --output "$filename_final" "$filename_original"
fi
gio trash "$filename_original"
done
sync