|
#!/usr/bin/env bash |
|
|
|
contains() { |
|
local e |
|
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done |
|
return 1 |
|
} |
|
|
|
usage() { |
|
if [ "$1" != "" ]; then |
|
echo -e "Error: $1\n" >&2 |
|
fi |
|
echo "Usage: $0 [options] <message>" |
|
echo |
|
echo "Options:" |
|
echo |
|
echo " -h Show this help message" |
|
echo |
|
echo " -o <output.gif> Path to output gif. Default: output.<random>.gif" |
|
echo |
|
echo " -c <color> Choose color set, valid options are:" |
|
echo " red, orange, yellow, green, blue, purple," |
|
echo " pink, black, white. Default: black" |
|
echo |
|
echo " -d <delay> Delay between frames. Default: 6" |
|
echo |
|
echo " -s <step> How many pixels to step each frame. Default: 10" |
|
echo |
|
echo " -H <height> Image height (and also the width). Default: 128" |
|
echo |
|
echo " -f <font name> The font name. Default: helvetica" |
|
echo |
|
echo " -w Force white text color" |
|
echo |
|
echo " -t Force transparent background" |
|
echo |
|
echo " -r CREATE RAINBOW ANIMATION! (only stacking multiple" |
|
echo " single color GIFs into one; incl. black + white)" |
|
exit 1 |
|
} |
|
|
|
# Make a "unique" prefix for this run |
|
PREFIX="$(head -c 32 /dev/urandom | shasum | cut -b 1-10)" |
|
|
|
VALID_COLORS=("red" "orange" "yellow" "green" "blue" "purple" "pink" "black" "white") |
|
COLOR="black" |
|
OUTPUT="output.$PREFIX.gif" |
|
FONT_NAME="helvetica" |
|
FORCE_WHITE_TEXT=0 |
|
FORCE_TRANSPARENT_BACKGROUND=0 |
|
SPEED=16 |
|
DELAY=6 |
|
HEIGHT=128 |
|
SKIP_OUTPUT_OPEN=0 |
|
RAINBOW_MODE=0 |
|
|
|
while getopts ":c:o:f:s:d:H:hwtrn" opt; do |
|
case $opt in |
|
h) |
|
usage |
|
;; |
|
n) |
|
SKIP_OUTPUT_OPEN=1 |
|
;; |
|
r) |
|
RAINBOW_MODE=1 |
|
;; |
|
w) |
|
FORCE_WHITE_TEXT=1 |
|
;; |
|
t) |
|
FORCE_TRANSPARENT_BACKGROUND=1 |
|
;; |
|
c) |
|
if contains "$OPTARG" "${VALID_COLORS[@]}"; then |
|
COLOR="$OPTARG" |
|
else |
|
usage "Invalid color: $OPTARG" |
|
fi |
|
;; |
|
o) |
|
OUTPUT="$OPTARG" |
|
;; |
|
H) |
|
HEIGHT="$OPTARG" |
|
;; |
|
f) |
|
FONT_NAME="$OPTARG" |
|
;; |
|
d) |
|
DELAY="$OPTARG" |
|
;; |
|
s) |
|
SPEED="$OPTARG" |
|
;; |
|
\?) |
|
usage "Invalid option: -$OPTARG" |
|
;; |
|
:) |
|
usage "Option -$OPTARG requires an argument." |
|
exit 1 |
|
;; |
|
esac |
|
done |
|
|
|
shift $((OPTIND - 1)) |
|
|
|
# Find our font before we do anything. |
|
FONT_PATH="$( fc-match "${FONT_NAME}" -f'%{file}' )" |
|
if [ "$FONT_PATH" == "" ]; then |
|
usage "Could not find '${FONT_NAME}' font. Is it installed?" |
|
else |
|
echo "Using ${FONT_PATH} for ${FONT_NAME} font." |
|
fi |
|
|
|
MESSAGE="$*" |
|
if [ "$MESSAGE" == "" ]; then |
|
usage "A message is required." |
|
fi |
|
|
|
if [ "$RAINBOW_MODE" == 1 ]; then |
|
colors=("red" "orange" "yellow" "green" "blue" "purple" "black" "white") |
|
declare -a files |
|
for color in ${colors[@]}; do |
|
echo "Generate color $color ..." |
|
file="rainbow.step.$color.gif" |
|
files+=($file) |
|
$0 -d "$DELAY" -s "$SPEED" -H "$HEIGHT" -f "$FONT_NAME" -c "$color" -t -n -o $file "$MESSAGE" |
|
done |
|
COMBINED="rainbow.$PREFIX.gif" |
|
convert ${files[@]} $COMBINED |
|
gifsicle -b -O3 -Okeep-empty -k16 $COMBINED |
|
echo "Final rainbow file: $COMBINED" |
|
exit 0 |
|
fi |
|
|
|
case $COLOR in |
|
red) |
|
FILL="#e40303" |
|
BACKGROUND="#4c0000" |
|
;; |
|
orange) |
|
FILL="#ff8c00" |
|
BACKGROUND="#562b00" |
|
;; |
|
yellow) |
|
FILL="#ffed00" |
|
BACKGROUND="#565100" |
|
;; |
|
green) |
|
FILL="#008026" |
|
BACKGROUND="#002705" |
|
;; |
|
blue) |
|
FILL="#004dff" |
|
BACKGROUND="#001456" |
|
;; |
|
purple) |
|
FILL="#750787" |
|
BACKGROUND="#23012a" |
|
;; |
|
pink) |
|
FILL="#851252" |
|
BACKGROUND="#e0368c" |
|
;; |
|
black) |
|
FILL="#111111" |
|
BACKGROUND="#ffffff" |
|
;; |
|
white) |
|
FILL="#ffffff" |
|
BACKGROUND="#111111" |
|
;; |
|
esac |
|
|
|
if [ "$FORCE_WHITE_TEXT" == 1 ]; then |
|
FILL="#ffffff" |
|
fi |
|
|
|
if [ "$FORCE_TRANSPARENT_BACKGROUND" == 1 ]; then |
|
echo "Using transparent background" |
|
BACKGROUND="transparent" |
|
fi |
|
|
|
# Generate image from text input |
|
convert \ |
|
-background "$BACKGROUND" \ |
|
-fill "$FILL" \ |
|
-font "$FONT_PATH" \ |
|
-density 200 \ |
|
-pointsize 100 \ |
|
"label:${MESSAGE}" \ |
|
"/tmp/${PREFIX}_label.png" |
|
|
|
# Resize to 128px high |
|
convert \ |
|
-resize x${HEIGHT} \ |
|
"/tmp/${PREFIX}_label.png" \ |
|
"/tmp/${PREFIX}_sized.png" |
|
|
|
# Add padding |
|
WIDTH="$(identify -format "%[fx:w]" "/tmp/${PREFIX}_sized.png")" |
|
CANVAS_SIZE=$((WIDTH + HEIGHT + HEIGHT + (HEIGHT / 4))) # HEIGHT in front, HEIGHT / 4 in back |
|
# convert "/tmp/${PREFIX}_sized.png" -bordercolor $BACKGROUND -border ${HEIGHT}x0 "/tmp/${PREFIX}_padded.png" |
|
convert \ |
|
"/tmp/${PREFIX}_sized.png" \ |
|
-gravity center \ |
|
-background $BACKGROUND \ |
|
-extent $CANVAS_SIZE \ |
|
"/tmp/${PREFIX}_padded.png" |
|
|
|
# Generate individual frames |
|
|
|
STEP=$((HEIGHT / (64 / SPEED))) |
|
OFFSET=0 |
|
I=0 |
|
LIMIT=$((CANVAS_SIZE - HEIGHT)) |
|
while [[ "$OFFSET" -lt "$LIMIT" ]]; do |
|
convert \ |
|
"/tmp/${PREFIX}_padded.png" \ |
|
-crop "${HEIGHT}x${HEIGHT}+${OFFSET}+0!" \ |
|
"$(printf "/tmp/${PREFIX}_frame_%05d.png" $I)" |
|
I=$((I + 1)) |
|
OFFSET=$((OFFSET + STEP)) |
|
done |
|
|
|
# Compile to gif |
|
convert \ |
|
-delay "$DELAY" \ |
|
-loop 0 \ |
|
-dispose previous \ |
|
"/tmp/${PREFIX}_frame_*.png" \ |
|
"$OUTPUT" |
|
|
|
# Clean up! |
|
rm /tmp/${PREFIX}_*.png |
|
|
|
if [ "$SKIP_OUTPUT_OPEN" == 0 ]; then |
|
# Optimize it |
|
gifsicle -b -O3 -Okeep-empty -k16 "$OUTPUT" |
|
echo "Your GIF is ready: $OUTPUT" |
|
unameOut="$(uname -s)" |
|
case "${unameOut}" in |
|
Linux*) |
|
xdg-open $OUTPUT |
|
;; |
|
Darwin*) |
|
open -a "Safari" "$OUTPUT" |
|
;; |
|
CYGWIN*) |
|
;; |
|
MINGW*) |
|
;; |
|
*) |
|
;; |
|
esac |
|
fi |