58 lines
2.9 KiB
Bash
Executable File
58 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Turns a creature illustration on solid black into a transparent aquarium sprite.
|
|
#
|
|
# scripts/aquarium-bild.sh bild.png public/aquarium/krake.webp
|
|
#
|
|
# The illustrations come out of the image generator as RGB on pure black, with no alpha
|
|
# channel. "Black becomes transparent" is not enough: the eyes and eyelashes are black
|
|
# too, and a creature with see-through eyes looks haunted on a turquoise sea. So the
|
|
# background is found as a *region*, not a colour - every dark area big enough to not be
|
|
# a facial feature - and only that region is removed.
|
|
#
|
|
# The edge is the other half. The art was antialiased onto black, so its outline pixels
|
|
# are the creature's colour already mixed with black. Cutting them hard leaves a dark
|
|
# halo; instead, near the background the opacity follows the brightness and the colour
|
|
# is divided back out of the black (un-premultiplied), which is what the mix actually was.
|
|
set -euo pipefail
|
|
|
|
if [[ $# -lt 2 ]]; then
|
|
echo "usage: $0 input.png output.webp [size]" >&2
|
|
exit 1
|
|
fi
|
|
src=$1
|
|
out=$2
|
|
size=${3:-512}
|
|
|
|
tmp=$(mktemp -d)
|
|
trap 'rm -rf "$tmp"' EXIT
|
|
|
|
# How far each pixel is from black: its brightest channel.
|
|
magick "$src" -alpha off -channel RGB -separate -evaluate-sequence max "$tmp/hell.png"
|
|
|
|
# Dark pixels, then drop every dark island under 5000 px back into the body. Measured on
|
|
# the first five creatures: the background is one region of ~1M px, the largest facial
|
|
# feature is ~2000 px, and nothing sits in between.
|
|
magick "$tmp/hell.png" -threshold 8% -negate \
|
|
-define connected-components:area-threshold=5000 \
|
|
-define connected-components:mean-color=true \
|
|
-connected-components 8 "$tmp/grund.png"
|
|
|
|
# Opacity: solid inside the body, zero out in the background, and in a thin band along
|
|
# the outline a ramp on brightness - the band is what catches the antialiased edge. The
|
|
# ramp starts a little above black, or the generator's faint noise in the background
|
|
# would come back as a faint haze around every creature.
|
|
magick "$tmp/grund.png" -morphology Dilate Disk:3 -negate "$tmp/koerper.png"
|
|
magick "$tmp/grund.png" -negate -morphology Dilate Disk:5 "$tmp/umriss.png"
|
|
magick "$tmp/hell.png" -evaluate Subtract 4% -evaluate Multiply 3.8 "$tmp/rampe.png"
|
|
magick "$tmp/rampe.png" "$tmp/koerper.png" -compose Lighten -composite \
|
|
"$tmp/umriss.png" -compose Multiply -composite "$tmp/alpha.png"
|
|
|
|
# Divide the black back out of the edge colours, attach the opacity, cut to the creature
|
|
# and scale down - the sprites are drawn at a few hundred px at most. `-alpha background`
|
|
# blanks the colour of fully transparent pixels, which is what lets `-trim` see them as
|
|
# border at all.
|
|
magick "$src" -alpha off "$tmp/alpha.png" -compose DivideSrc -composite \
|
|
"$tmp/alpha.png" -alpha off -compose CopyOpacity -composite \
|
|
-background none -alpha background -trim +repage -resize "${size}x${size}>" \
|
|
-quality 90 -define webp:alpha-quality=100 "$out"
|