Last active
April 27, 2025 15:48
-
-
Save Winterhuman/c03a892f131c17f2ace4a1c42e9de06f to your computer and use it in GitHub Desktop.
A script which finds the minimum PNG dimensions for a given aspect ratio and pixel count, creates a colour-cycling PPM file, and then converts it to PNG.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env -S unshare --mount --map-root-user /bin/sh | |
# Licensed under the Zero-Clause BSD terms: https://opensource.org/license/0bsd | |
# Requires: imagemagick & [sisyphus](https://gist.github.com/Winterhuman/21d7b148db40ff041f397b07a7aafb83) | |
# | |
# Argument 1: Number of pixels to fill, required | |
# Argument 2: Height ratio, default 23 | |
# Argument 3: Width ratio, default 5 | |
set -Ceuf | |
pstat() { | |
printf "%b\033[1m%b\033[0;39m" "${2:-\033[1m\033[34m::\033[0;39m }" "$1" | |
} | |
pquit() { | |
pstat "$1" "\033[1m\033[31m!!\033[0;39m " | |
exit 1 | |
} | |
# Using '$RANDOM' is the only non-POSIX part of this script | |
_="${RANDOM:?$(pquit "The 'RANDOM' environment variable is not available!\n")}" | |
# Check if all arguments are valid | |
fill="${1:?$(pquit "No arguments given!\n")}" | |
if ! printf "%d" "$fill" >/dev/null 2>&1 || [ "$fill" -le 0 ]; then | |
pquit "'$fill' is not a positive integer!\n"; fi | |
pstat "Fill: $fill\n" | |
hratio="${2:-23}" | |
wratio="${3:-5}" | |
if ! printf "%d" "$hratio" "$wratio" >/dev/null 2>&1 || | |
[ "$hratio" -le 0 ] || [ "$wratio" -le 0 ]; then | |
pquit "Aspect ratio values must be positive integers!\n"; fi | |
# Calculate the optimal/minimal image dimensions for the given fill value | |
calc_w() { | |
## '+ wratio - 1' yields a rounded up answer, instead of rounded down | |
printf "%d\n" "$(( (("$1" * hratio) + wratio - 1 ) / wratio ))" | |
} | |
calc_h() { | |
printf "%d\n" "$(( ( ("$1" * wratio) + hratio - 1 ) / hratio ))" | |
} | |
height=1 | |
width="$(calc_w "$height")" | |
bad=0 | |
## 'input + 1' is required to make this round up, since '$wratio' is in | |
## both the numerator and denominator (manual calculations verify this). | |
### '-1' is omitted from 'hratio + wratio' to handle '2:1' ratios. | |
good="$(( | |
( (fill + 1) * wratio + hratio + wratio ) / | |
(hratio + wratio) | |
))" | |
good="$(( "$(calc_h "$good")" + 2 ))" | |
if [ "$good" -gt 1 ]; then | |
while [ "$bad" -le "$(( good - 1 ))" ]; do | |
if [ "$(( width * height ))" -le "$fill" ]; then | |
bad="$(( height + 1 ))" | |
else | |
good="$height" | |
fi | |
height="$(( (bad + good) / 2 ))" | |
width="$(calc_w "$height")" | |
done | |
fi | |
height="$good" | |
width="$(calc_w "$height")" | |
blank="$(( ("$height" * "$width") - "$fill" ))" | |
groups="$(( "$fill" / 3 ))" | |
remainofgroups="$(( "$fill" % 3 ))" | |
pstat "Image dimensions: $width x $height\n" | |
pstat "Pixel fill: ${fill}px (${blank}px left)\n" | |
# Setup a private tmpfs for this script to use, which is what 'unshare' is for. | |
## 'nr_inodes' must be >= to 'max number of files + 1' | |
mount -t tmpfs -o nosuid,nodev,noexec,size=256M,nr_inodes=3 ppm2png /tmp || | |
pquit "Failed to overmount '/tmp'!\n" | |
# Write the P3 PPM header | |
ppm="/tmp/img.ppm" | |
printf "P3\n%d %d\n255\n" "$width" "$height" >"$ppm" | |
# Write the RGB IDs | |
## Many things worth noting here: | |
## | |
## 1. "A\0B\0C\n" is faster to write than "A\nB\nC\n" due to line buffering, so | |
## avoiding them actually improves the speed even with `sed` afterwards. | |
## 2. `>>file` outside the while-loop is faster since it reuses the file's FD. | |
## 3. Using a background job for `printf` **drastically** slows things down, | |
## because `$RANDOM` is evaluated before the background job starts. | |
count="$groups" | |
while [ "$count" -gt 0 ]; do | |
printf "%d\0%d\0%d\n" \ | |
"$(( RANDOM % 6 + 1 ))" \ | |
"$(( RANDOM % 6 + 8 ))" \ | |
"$(( RANDOM % 6 + 15 ))" | |
count="$(( count - 1 ))" | |
done >>"$ppm" | |
sed -i "s/\o0/\n/g" "$ppm" || pquit "Failed to replace null bytes in '$ppm'!\n" | |
# Write the remaining partial ID groups (1-2 or 1, as opposed to a full 1-2-3) | |
if [ "$remainofgroups" -eq 2 ]; then | |
printf "%d\n%d\n" \ | |
"$(( "$RANDOM" % 6 + 1 ))" \ | |
"$(( "$RANDOM" % 6 + 8 ))" \ | |
>>"$ppm" | |
elif [ "$remainofgroups" -eq 1 ]; then | |
printf "%d\n" "$(( "$RANDOM" % 6 + 1 ))" >>"$ppm" | |
fi | |
# Replace the numeric IDs with the actual RGB colours. | |
sed -i -e "s/^1$/0 255 170/" \ | |
-e "s/^2$/0 223 148/" \ | |
-e "s/^3$/0 191 127/" \ | |
-e "s/^4$/0 159 106/" \ | |
-e "s/^5$/0 127 85/" \ | |
-e "s/^6$/0 96 64/" \ | |
-e "s/^7$/0 64 42/" \ | |
-e "s/^8$/255 187 0/" \ | |
-e "s/^9$/223 164 0/" \ | |
-e "s/^10$/191 140 0/" \ | |
-e "s/^11$/159 117 0/" \ | |
-e "s/^12$/127 93 0/" \ | |
-e "s/^13$/96 70 0/" \ | |
-e "s/^14$/64 47 0/" \ | |
-e "s/^15$/255 76 136/" \ | |
-e "s/^16$/223 66 119/" \ | |
-e "s/^17$/191 57 102/" \ | |
-e "s/^18$/159 48 85/" \ | |
-e "s/^19$/127 38 68/" \ | |
-e "s/^20$/96 29 51/" \ | |
-e "s/^21$/64 19 34/" \ | |
"$ppm" | |
# Write the leftover blank lines | |
printf "%${blank}s" ' ' | sed "s/ /0 0 0\n/g" >>"$ppm" | |
# Convert PPM to PNG | |
tmp_img="/tmp/tmp.png" | |
magick "$ppm" -transparent "#000" "$tmp_img" || | |
pquit "Failed to create '$tmp_img'!\n" | |
# Move any existing output to a new '.old' path | |
mv_exist() { | |
if [ -f "$1" ]; then | |
mv "$1" "${1}.old" || | |
pquit "Failed to move existing '$1' image!\n" | |
fi | |
} | |
mv_exist "header.png" | |
mv_exist "header.webp" | |
# Optimise PNG with 'sisyphus' | |
pstat "Optimising with Sisyphus...\n" | |
## '$tmp_img' exists outside Sisyphus's private tmpfs, meaning it can't see it. | |
## So, instead of copying '$tmp_img' somewhere, just give it a file descriptor. | |
exec 3<"$tmp_img" | |
sisyphus --all-oxi true /proc/self/fd/3 "header" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment