#!/usr/bin/env bash # Optimize site images in place. Lossy but visually conservative. # # - JPEG : strip metadata, progressive, quality 82 # - PNG : strip metadata, max zlib recompression (lossless) # - GIF : gifsicle -O3 (lossless frame optimization); --lossy on big ones # - A short list of oversized tile PNGs is downscaled to a sane max width, # since the layout never displays them larger than ~320px. # # Originals are tarred to "$BACKUP_DIR" before anything is touched. # Re-running is safe: already-small files are left alone. # # Env: # BACKUP_DIR where to write the pre-run tarball (default /tmp) # DRY_RUN=1 print what would change, touch nothing set -u cd "$(dirname "$0")/.." ROOT="$(pwd)" BACKUP_DIR="${BACKUP_DIR:-/tmp}" DRY="${DRY_RUN:-0}" have() { command -v "$1" >/dev/null 2>&1; } have convert || { echo "need ImageMagick (convert)"; exit 1; } # Tile PNGs the layout caps at ~320px wide; 640 keeps a 2x retina buffer. # Section heroes are capped wider (display size rarely exceeds ~960px). declare -A MAXW=( [assets/img/runescape.png]=640 [assets/img/soldierfront.png]=640 [assets/img/multibox.png]=640 [assets/img/stepmania.png]=640 [startpage/videogame.jpg]=720 [computers/laptop-compaq.jpg]=960 [gaming/runescape/runescape.png]=640 ) fmt() { numfmt --to=iec "$1" 2>/dev/null || echo "$1"; } size() { stat -c%s "$1"; } if [ "$DRY" != "1" ]; then ts="$(date +%Y%m%d-%H%M%S)" tar -czf "$BACKUP_DIR/images-pre-optimize-$ts.tar.gz" \ assets/img images startpage computers gaming vblog mstartpage 2>/dev/null \ && echo "backup: $BACKUP_DIR/images-pre-optimize-$ts.tar.gz" fi total_before=0; total_after=0 process() { local f="$1" before after; before="$(size "$f")"; total_before=$((total_before+before)) local tmp="$f.opt.$$" local fl fl="$(printf '%s' "$f" | tr '[:upper:]' '[:lower:]')" case "$fl" in *.jpg|*.jpeg) convert "$f" -strip -interlace Plane -quality 82 "$tmp" 2>/dev/null ;; *.png) local w="${MAXW[$f]:-}" [ -z "$w" ] && w="${MAXW[$fl]:-}" if [ -n "$w" ]; then convert "$f" -strip -resize "${w}x${w}>" -define png:compression-level=9 -define png:compression-filter=5 "$tmp" 2>/dev/null else convert "$f" -strip -define png:compression-level=9 -define png:compression-filter=5 "$tmp" 2>/dev/null fi ;; *.gif) if ! have gifsicle; then total_after=$((total_after+before)); return; fi if [ "$before" -gt 400000 ]; then gifsicle -O3 --lossy=60 "$f" -o "$tmp" 2>/dev/null else gifsicle -O3 "$f" -o "$tmp" 2>/dev/null fi ;; *) total_after=$((total_after+before)); return ;; esac if [ ! -s "$tmp" ]; then rm -f "$tmp"; total_after=$((total_after+before)); return; fi after="$(size "$tmp")" if [ "$after" -lt "$before" ]; then if [ "$DRY" = "1" ]; then printf ' %-40s %8s -> %8s\n' "$f" "$(fmt "$before")" "$(fmt "$after")"; rm -f "$tmp" else mv "$tmp" "$f" fi total_after=$((total_after+after)) else rm -f "$tmp"; total_after=$((total_after+before)) fi } while IFS= read -r -d '' f; do process "$f"; done < <( find assets/img images startpage computers gaming vblog mstartpage \ -type f \( -iname '*.jpg' -o -iname '*.jpeg' \ -o -iname '*.png' -o -iname '*.gif' \) -print0 2>/dev/null ) echo "total: $(fmt "$total_before") -> $(fmt "$total_after")"