feat: render both variants by default (DARK/LIGHT to select one); add attributed Commons samples to guide

This commit is contained in:
Richard Zink
2026-09-22 11:40:54 +02:00
parent 4ca243c13d
commit b04bd317fd
13 changed files with 120 additions and 33 deletions
+31 -23
View File
@@ -1,24 +1,29 @@
#!/usr/bin/env bash
# ash-wallpaper.sh — turn a color photo into an Ash-palette wallpaper:
# ash-wallpaper.sh — turn a color photo into Ash-palette wallpapers:
# grayscale body tinted toward the Ash tones, with warm "ember" highlights
# (reds/oranges/golds) selectively preserved. Pleasantville-style.
#
# usage: tools/ash-wallpaper.sh INPUT [OUTPUT]
# usage: tools/ash-wallpaper.sh INPUT [OUTBASE]
#
# By default BOTH variants are rendered:
# OUTBASE-dark.png (void -> chalk ramp)
# OUTBASE-light.png (stone -> paper ramp)
# Set DARK=1 to render only dark, LIGHT=1 to render only light.
# OUTBASE defaults to INPUT-without-extension + "-ash".
#
# tuning via environment (defaults shown):
# PRESET=dark dark (void->chalk) | light (stone->paper)
# HUE_MAX=13% warm band: hue <= HUE_MAX
# HUE_MIN=94% ... or hue >= HUE_MIN (catches pure reds)
# HUE_MIN=94% ... or hue >= HUE_MIN (catches pure reds)
# SAT_MIN=12% below this saturation pixels lose the highlight
# SAT_MAX=45% at/above this saturation the highlight is full
# EMBER_BOOST=130 saturation multiplier (%) applied to highlight areas
# SENSITIVITY=8x45% sigmoidal contrast of the final mask
# BASE_LO= BASE_HI= override the grayscale tint endpoints
# BASE_LO= BASE_HI= override the grey ramp endpoints (applies to both
# rendered variants unless only one is generated)
set -euo pipefail
IN=${1:?usage: ash-wallpaper.sh INPUT [OUTPUT]}
OUT=${2:-${IN%.*}-ash.png}
PRESET=${PRESET:-dark}
IN=${1:?usage: ash-wallpaper.sh INPUT [OUTBASE]}
OUTBASE=${2:-${IN%.*}-ash}
HUE_MAX=${HUE_MAX:-13%}
HUE_MIN=${HUE_MIN:-94%}
SAT_MIN=${SAT_MIN:-12%}
@@ -28,11 +33,9 @@ SENSITIVITY=${SENSITIVITY:-8x45%}
if command -v magick >/dev/null; then IM=magick; else IM=convert; fi
case "$PRESET" in
dark) BASE_LO=${BASE_LO:-#100f12}; BASE_HI=${BASE_HI:-#f3ede1} ;;
light) BASE_LO=${BASE_LO:-#b7ae98}; BASE_HI=${BASE_HI:-#f2ecdf} ;;
*) echo "PRESET must be dark or light" >&2; exit 1 ;;
esac
if [[ ${DARK:-0} == 1 ]]; then VARIANTS=(dark)
elif [[ ${LIGHT:-0} == 1 ]]; then VARIANTS=(light)
else VARIANTS=(dark light); fi
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
@@ -47,13 +50,18 @@ trap 'rm -rf "$TMP"' EXIT
"$IM" "$TMP/band.png" "$TMP/satmask.png" -compose Multiply -composite \
-gaussian-blur 0x2 -sigmoidal-contrast "$SENSITIVITY" "$TMP/mask.png"
# ash base + ember-touched highlights, joined by the mask
"$IM" "$IN" \
\( -clone 0 -modulate 100,0 +level-colors "$BASE_LO","$BASE_HI" \) \
\( -clone 0 -modulate "100,$EMBER_BOOST" \) \
-delete 0 \
\( +clone "$TMP/mask.png" -alpha off -compose CopyOpacity -composite \) \
-delete 1 -compose Over -composite \
-colorspace sRGB "$OUT"
echo "wrote $OUT"
for preset in "${VARIANTS[@]}"; do
case "$preset" in
dark) lo=${BASE_LO:-#100f12}; hi=${BASE_HI:-#f3ede1} ;;
light) lo=${BASE_LO:-#b7ae98}; hi=${BASE_HI:-#f2ecdf} ;;
esac
# ash base + ember-touched highlights, joined by the mask
"$IM" "$IN" \
\( -clone 0 -modulate 100,0 +level-colors "$lo","$hi" \) \
\( -clone 0 -modulate "100,$EMBER_BOOST" \) \
-delete 0 \
\( +clone "$TMP/mask.png" -alpha off -compose CopyOpacity -composite \) \
-delete 1 -compose Over -composite \
-colorspace sRGB "$OUTBASE-$preset.png"
echo "wrote $OUTBASE-$preset.png"
done