#!/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"