Created
March 24, 2023 17:56
-
-
Save acorn1010/2ced01cdb32caa27d34a070f88e0cf10 to your computer and use it in GitHub Desktop.
generate_images
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 bash | |
set -e | |
# Creates webp / avif images for images that don't already exist and places them in the public folder | |
# This script can take a while to run | |
# Install deps | |
# sudo apt-get install -f webp ffmpeg opusenc | |
# MacOS deps | |
# brew install zopfli | |
# brew install ffmpeg | |
# brew install joedrago/repo/avifenc | |
# Ensure deps are executable | |
chmod +x ./avifenc | |
chmod +x ./file_utils | |
chmod +x ./zopflipng | |
chmod +x ./magick | |
chmod +x ./packTexture.sh | |
# Path variables for conversion | |
DESIGN_IMG_PATH=design/images | |
ITEM_DESIGN_IMG_PATH=${DESIGN_IMG_PATH}/items | |
CURSOR_DESIGN_IMG_PATH=${DESIGN_IMG_PATH}/games/account/items/Cursors | |
GAME_DESIGN_IMG_PATH=${DESIGN_IMG_PATH}/games | |
SITE_DESIGN_IMG_PATH=${DESIGN_IMG_PATH}/site | |
POST_DESIGN_IMG_PATH=${DESIGN_IMG_PATH}/site/posts | |
AUDIO_DESIGN_PATH=design/audio | |
PUB_IMG_PATH=client/public/img | |
ITEM_PUB_IMG_PATH=${PUB_IMG_PATH}/items | |
CURSOR_PUB_IMG_PATH=${PUB_IMG_PATH}/games/account/items/Cursors | |
GAME_PUB_IMG_PATH=${PUB_IMG_PATH}/games | |
SITE_PUB_IMG_PATH=${PUB_IMG_PATH} | |
POST_PUB_IMG_PATH=${PUB_IMG_PATH}/posts | |
AUDIO_PUB_PATH=client/public/audio | |
# Increase degree of parallelism | |
NUM_PROCESSES=20 | |
# Convert PNG to WEBP and AVIF. | |
function convert_images() { | |
input_file=${1?} | |
design_path=${2?} | |
pub_path=${3?} | |
image_type=${4?} # icon, cursor, other | |
if [ "${image_type}" == 'icon' ]; then | |
img_basename=$(sed -E "s#${design_path}/(.*)\.png#\1#" <<< ${input_file}) | |
else | |
img_basename=$(sed -E "s#${design_path}/(.*)\..{3}#\1#" <<< ${input_file}) | |
fi | |
png_path=${pub_path}/${img_basename}.png | |
png_path_2x=${pub_path}/${img_basename}.2x.png | |
webp_path=${pub_path}/${img_basename}.webp | |
avif_path=${pub_path}/${img_basename}.avif | |
unset png_source_path[@] | |
unset magick_options[@] | |
case "${image_type}" in | |
'icon') | |
png_source_path[0]=${png_path_2x} | |
png_source_path[1]=${png_path} | |
magick_options[0]="-resize 300x300> -gravity center -trim" | |
magick_options[1]="-resize 150x150> -gravity center -trim" | |
avifenc_options="--min 23 --max 53 --minalpha 23 --maxalpha 53" | |
;; | |
'cursor') | |
png_source_path[0]=${png_path} | |
magick_options[0]="-resize 28x28>" | |
avifenc_options="--min 14 --max 21 --minalpha 14 --maxalpha 21" | |
;; | |
'cursor_pointer') | |
png_source_path[0]=${png_path} | |
# NOTE: We resize first, and THEN rotate. This can leave extra whitespace, but we don't | |
# trim it because this would make the cursor have a different click offset. | |
magick_options[0]="-resize 28x28> -background transparent -rotate 40" | |
avifenc_options="--min 14 --max 21 --minalpha 14 --maxalpha 21" | |
;; | |
*) | |
png_source_path[0]=${png_path} | |
avifenc_options="--min 14 --max 21 --minalpha 14 --maxalpha 21" | |
;; | |
esac | |
# Create paths if it does not exist. | |
mkdir -p "$(dirname "${png_source_path[0]}")" | |
# Move file if necessary. | |
if [ ! -f "${png_source_path[0]}" ]; then | |
index=0 | |
for i in "${png_source_path[@]}"; do | |
./magick convert "${input_file}" -profile ./design/color_profiles/sRGBz.icc ${magick_options[index]} "${png_source_path[index]}" | |
./zopflipng -y --keepchunks=iCCP --lossy_transparent "${png_source_path[index]}" "${png_source_path[index]}" | |
index=$((index+1)) | |
done | |
fi | |
# Convert to WEBP if necssary. | |
if [ ! -f "${webp_path}" ]; then | |
cwebp -metadata icc -quiet -q 90 "${png_source_path[0]}" -o "${webp_path}" | |
fi | |
# Convert to AVIF if necessary. | |
if [ ! -f "${avif_path}" ]; then | |
# Firefox messes up on depth 10. If we use a limited range and depth 8, then we get the correct | |
# sRGB rednering. | |
./avifenc -c aom --speed 0 --jobs 12 --range limited --ignore-icc --yuv 444 ${avifenc_options} "${png_source_path[0]}" "${avif_path}" | |
fi | |
} | |
# export to access in bash subshell | |
export -f convert_images | |
# Generate OpenGraph images for social sharing of the Foony URLs | |
function create_og_image() { | |
input_file=${1?} | |
design_path=${2?} | |
pub_path=${3?} | |
game_id=$(sed -E "s#${design_path}/(.*)#\1#" <<< ${input_file}) | |
echo "input_file = ${input_file}" | |
echo "game_id = ${game_id}" | |
original_og_image_path="${input_file}/og_image.png" | |
hero_image_path="${input_file}/hero_image.png" | |
thumbnail_path="${input_file}/thumbnail.png" | |
og_image_path="${pub_path}/${game_id}/og_image.png" | |
og_image_path_webp="${pub_path}/${game_id}/og_image.webp" | |
# Create paths if it does not exist. | |
mkdir -p "$(dirname "${og_image_path}")" | |
# Check if hero_image.png exists, else use thumbnail.png | |
if [ -f "${original_og_image_path}" ]; then | |
image_source="${original_og_image_path}" | |
elif [ -f "${hero_image_path}" ]; then | |
image_source="${hero_image_path}" | |
elif [ -f "${hero_image_path}" ]; then | |
image_source="${thumbnail_path}" | |
else | |
return 0 # No hero_image or thumbnail for path. TODO(acorn1010): Emit warning if not 'account' gameId? | |
fi | |
# Create og_image.png if it does not exist. | |
if [ ! -f "${og_image_path}" ]; then | |
./magick convert "${image_source}" -profile ./design/color_profiles/sRGBz.icc -resize 1200x630^ -gravity center -extent 1200x630 "${og_image_path}" | |
cwebp -metadata icc -quiet -q 90 "${og_image_path}" -o "${og_image_path_webp}" | |
fi | |
} | |
export -f create_og_image | |
function convert_audio() { | |
input_file=${1?} | |
design_path=${2?} | |
pub_path=${3?} | |
img_basename=$(sed -E "s#${design_path}/(.*)\..{3}#\1#" <<< "${input_file}") | |
full_path=${pub_path}/${img_basename} | |
# Create paths if it does not exist. | |
mkdir -p "$(dirname "${full_path}")" | |
# Create audio file for normal users if necessary | |
if [ ! -f "${full_path}.opus" ]; then | |
opusenc --vbr --bitrate 48 "${input_file}" "${full_path}.opus" | |
fi | |
# Create audio file for special Apple users | |
if [ ! -f "${full_path}.m4a" ]; then | |
ffmpeg -i "${input_file}" -c:a aac -b:a 96k "${full_path}.m4a" | |
fi | |
} | |
export -f convert_audio | |
# Convert GIF to WEBM. | |
function convert_gifs() { | |
input_file=${1?} | |
design_path=${2?} | |
pub_path=${3?} | |
img_basename=$(sed -E "s#${design_path}/(.*)\..{3}#\1#" <<< "${input_file}") | |
full_path=${pub_path}/${img_basename} | |
# Create paths if it does not exist. | |
mkdir -p "$(dirname "${full_path}")" | |
# Move file if necessary. | |
if [ ! -f "${full_path}.webm" ]; then | |
ffmpeg -y -i "${input_file}" -c vp9 -b:v 0 -crf 40 "${full_path}.webm" | |
fi | |
} | |
# export to access in bash subshell | |
export -f convert_gifs | |
# These functions can run in parallel using xargs "-P #" value | |
find "${ITEM_DESIGN_IMG_PATH}" -type f -and -iname "*.png" -print0 | xargs -0 -P ${NUM_PROCESSES} -I {} bash -c 'convert_images "$@"' _ {} "${ITEM_DESIGN_IMG_PATH}" "${ITEM_PUB_IMG_PATH}" icon | |
find "${GAME_DESIGN_IMG_PATH}" -maxdepth 1 -mindepth 1 -type d -print0 | xargs -0 -P ${NUM_PROCESSES} -I {} bash -c 'create_og_image "$@"' _ {} "${GAME_DESIGN_IMG_PATH}" "${GAME_PUB_IMG_PATH}" | |
find "${CURSOR_DESIGN_IMG_PATH}" -type f -and \( -iname "*.png" -o -iname "*.jpg" \) -print0 | xargs -0 -P ${NUM_PROCESSES} -I {} bash -c 'convert_images "$@"' _ {} "${CURSOR_DESIGN_IMG_PATH}" "${CURSOR_PUB_IMG_PATH}" cursor | |
find "${CURSOR_DESIGN_IMG_PATH}" -type f -and \( -iname "*.png" -o -iname "*.jpg" \) -print0 | xargs -0 -P ${NUM_PROCESSES} -I {} bash -c 'convert_images "$@"' _ {} "${CURSOR_DESIGN_IMG_PATH}" "${CURSOR_PUB_IMG_PATH}Pointer" cursor_pointer | |
find "${GAME_DESIGN_IMG_PATH}" -type f -and \( -iname "*.png" -o -iname "*.jpg" \) -print0 | xargs -0 -P ${NUM_PROCESSES} -I {} bash -c 'convert_images "$@"' _ {} "${GAME_DESIGN_IMG_PATH}" "${GAME_PUB_IMG_PATH}" other | |
find "${SITE_DESIGN_IMG_PATH}" -type f -and \( -iname "*.png" -o -iname "*.jpg" \) -print0 | xargs -0 -P ${NUM_PROCESSES} -I {} bash -c 'convert_images "$@"' _ {} "${SITE_DESIGN_IMG_PATH}" "${SITE_PUB_IMG_PATH}" other | |
find "${POST_DESIGN_IMG_PATH}" -type f -and -iname "*.gif" -print0 | xargs -0 -P ${NUM_PROCESSES} -I {} bash -c 'convert_gifs "$@"' _ {} "${POST_DESIGN_IMG_PATH}" "${POST_PUB_IMG_PATH}" | |
find "${AUDIO_DESIGN_PATH}" -type f -and -iname "*.wav" -print0 | xargs -0 -P ${NUM_PROCESSES} -I {} bash -c 'convert_audio "$@"' _ {} "${AUDIO_DESIGN_PATH}" "${AUDIO_PUB_PATH}" | |
./file_utils image "client/public" "client/src/images/SiteImages.ts"; | |
./file_utils audio "client/public/audio" "client/src/audio/AudioFiles.ts"; | |
./file_utils src "client/src" "client/src/io/TypeScriptFiles.ts"; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks!