Ctrl K
ComunidadGuías…

scripts/optimize-docs-images.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bash
#
# optimize-docs-images.sh
#
# Shrinks PNG screenshots in the user guide so they don't bloat the repository.
# Each image is converted to sRGB, stripped of metadata, capped at MAX_WIDTH
# pixels wide, and reduced to a 256-colour palette. Images that lose too much
# fidelity as a palette (photos, gradients) stay truecolor, and an image is
# only rewritten when the result is at least 10% smaller. Images that are
# already optimized are skipped, so the script is safe to re-run.
#
# Usage:
#
#    scripts/optimize-docs-images.sh [file.png ...]
#
# With no arguments, all tracked PNGs under docs/user/rstudio are processed.
# Requires ImageMagick 7 (magick) built with lcms.
#
# Environment:
#
#    MAX_WIDTH     Maximum width, in pixels (default: 2000)
#    MIN_PSNR      Minimum PSNR, in dB, for the palette version (default: 40)
#    SRGB_PROFILE  Path to an sRGB ICC profile, for images that embed a profile
#                  of their own (default: the usual macOS and Linux locations)

set -euo pipefail

MAX_WIDTH="${MAX_WIDTH:-2000}"
MIN_PSNR="${MIN_PSNR:-40}"

if ! command -v magick > /dev/null 2>&1; then
   echo "error: ImageMagick 7 (magick) is required" >&2
   exit 1
fi

# Screenshots usually embed the profile of the display they were taken on.
# Dropping it without converting would shift colours in the browser. Images
# without a profile don't need this, so a missing one is only an error once an
# image that needs it turns up.
SRGB_PROFILE="${SRGB_PROFILE:-}"
if [ -z "$SRGB_PROFILE" ]; then
   for candidate in \
      "/System/Library/ColorSync/Profiles/sRGB Profile.icc" \
      "/usr/share/color/icc/colord/sRGB.icc" \
      "/usr/share/color/icc/sRGB.icc"
   do
      if [ -f "$candidate" ]; then
         SRGB_PROFILE="$candidate"
         break
      fi
   done
fi

root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
workdir="$(mktemp -d)"
trap 'rm -rf "$workdir"' EXIT

if [ "$#" -gt 0 ]; then
   printf '%s\n' "$@" > "$workdir/files"
else
   git -C "$root" ls-files ':(glob,icase)docs/user/rstudio/**/*.png' | sed "s|^|$root/|" > "$workdir/files"
fi

# Leave out the timestamp chunks ImageMagick writes by default, so the same
# input always produces the same bytes.
png_options=(-define png:compression-level=9 -define png:exclude-chunks=date,time)

changed=0
failed=0
before=0
after=0

while IFS= read -r file; do

   truecolor="$workdir/truecolor.png"
   palette="$workdir/palette.png"

   # identify warns on stderr about every image without a profile, so its
   # output is only worth showing when it fails.
   if ! info="$(magick identify -ping -format '%w|%[png:IHDR.color-type-orig]|%[profile:icc]' "$file" 2> "$workdir/identify.log")"; then
      echo "error: cannot read $file" >&2
      cat "$workdir/identify.log" >&2
      failed=$((failed + 1))
      continue
   fi

   IFS='|' read -r width color_type profile <<< "$info"

   # Already optimized: a palette PNG (colour type 3) is what this script
   # writes. Quantizing again is not a no-op: ImageMagick merges more colours
   # on each pass, so skip rather than degrade the image. This goes by how the
   # file is stored rather than how many colours it has, so that a 16-bit or
   # uncompressed image with few colours still gets processed.
   if [ -z "$profile" ] && [ "$width" -le "$MAX_WIDTH" ] && [ "$color_type" = 3 ]; then
      size="$(wc -c < "$file" | tr -d ' ')"
      before=$((before + size))
      after=$((after + size))
      continue
   fi

   if [ -n "$profile" ] && [ ! -f "$SRGB_PROFILE" ]; then
      echo "error: $file embeds a colour profile, and no sRGB ICC profile was found to convert it; set SRGB_PROFILE" >&2
      failed=$((failed + 1))
      continue
   fi

   if [ -n "$profile" ]; then
      magick "$file" -profile "$SRGB_PROFILE" -strip -resize "${MAX_WIDTH}x>" -depth 8 "${png_options[@]}" "$truecolor"
   else
      magick "$file" -strip -resize "${MAX_WIDTH}x>" -depth 8 "${png_options[@]}" "$truecolor"
   fi

   # No dithering: it adds noise to the flat regions that dominate screenshots.
   magick "$truecolor" -dither None -colors 256 "${png_options[@]}" "png8:$palette"

   # compare exits non-zero when the images differ at all
   psnr="$(magick compare -metric PSNR "$truecolor" "$palette" null: 2>&1 | awk '{ print $1 }' || true)"

   result="$truecolor"
   if awk -v psnr="$psnr" -v min="$MIN_PSNR" 'BEGIN { exit !(psnr == "inf" || psnr + 0 >= min) }'; then
      result="$palette"
   fi

   old_size="$(wc -c < "$file" | tr -d ' ')"
   new_size="$(wc -c < "$result" | tr -d ' ')"
   before=$((before + old_size))

   if [ $((new_size * 10)) -lt $((old_size * 9)) ]; then
      cp "$result" "$file"
      changed=$((changed + 1))
      after=$((after + new_size))
      echo "$((old_size / 1024)) KB -> $((new_size / 1024)) KB  ${file#"$root"/}"
   else
      after=$((after + old_size))
   fi

done < "$workdir/files"

echo "Rewrote $changed image(s): $((before / 1024)) KB -> $((after / 1024)) KB"

if [ "$failed" -gt 0 ]; then
   echo "error: $failed file(s) could not be processed" >&2
   exit 1
fi