/dev/trouble
Eric Roller's Development Blog

Bash Script to Scale AppIcons

- Posted in iOS by

The Old Automator Flow

In a previous post, I described an Automator workflow to rescale iOS app icons. While that has worked well, it had potential for improvement:

  1. The workflow document is not easily edited, at least not on a slow machine like mine;

  2. When saved, the workflow document contains a preview image which blows up the file to about 1 GB. The preview image can be removed, but it adds another manual hack that is required.

Because of these shortfalls, I became increasingly reluctant to use it and found myself resizing images manually again.

Welcome to sips

I don't know how long it has been there, but it turns out macOS contains a command-line utility to convert images: sips (scriptable image processing system). You can check if it is installed on your machine using:

$ which sips
/usr/bin/sips

or

$ sips -h
sips - scriptable image processing system.
This tool is used to query or modify raster image files and ColorSync ICC profiles.
Its functionality can also be used through the "Image Events" AppleScript suite.

  Usages:
    sips [image-functions] imagefile ... 
    sips [profile-functions] profile ... 

[...]

Using sips

With some tips from this post, I have now set up the following script, saved in a file like ~/bin/resizeAppIcons, (i.e. in a directory listed in your $path variable):

#!/bin/sh
help=0
errs=0
pngquant=/Applications/ImageAlpha.app/Contents/MacOS/pngquant
colours=256

args=$(getopt h: *)     # do not use "$@" to work with 'set' below.
if [ $? != 0 ]; then ((help++)); fi

set -- $args
for i ; do
    case "$i" in
    -h)
        ((help++))
        shift;;
    --)
        shift; break;;
    esac
done

if [ $help -gt 0 ]; then
    echo "USAGE: $(basename $0) [-h] [pngFiles...]"
    exit 2
fi

if [ ! -x $pngquant ]; then
    echo "Warning: ImageAlpha.app is not installed"
fi

for img in $* ; do
    echo "$img"

    # Get the current image size: ( width height )
    size=( $(sips -g pixelWidth -g pixelHeight "$img" | grep -o '[0-9]*$') )

    # We expect 1024 x 1024.
    if [[ ${size[0]} -ne ${size[1]} || ${size[0]} -ne 1024 ]]; then
        echo "Error: Image size should be 1024 x 1024 (not ${size[0]} x ${size[1]})."
        ((errs++))
    fi

    # Split the file path:            path/to/filename.png
    dirn=$(dirname "$img")          # path/to
    base=$(basename "${img%.*}")    #         filename
    extn=${img##*.};                 #                  png

    if [[ $extn != "png" ]]; then
        echo "Error: Expected image format is PNG (not $extn)."
        ((errs++))
    fi

    if (( $errs )); then
        echo "Too many errors; giving up"
        exit 1
    fi

    # Using all the known image sizes:
    for width in 16 18 19 32 36 38 40 48 55 58 60 64 80 87 88 100 120 128 152 167 172 180 196 216 256 512 1024 ; do
        outfile="$dirn/$base-$width.$extn"

        # Do not overwrite existing files
        if [[ -f "$outfile" ]]; then continue; fi

        echo "--> $outfile"

        # Copy or resize the image
        if [ $width -eq 1024 ]; then
            cp "$img" "$outfile"
        else
            sips -Z $width "$img" --out "$outfile" $gt; /dev/null
        fi

        # Use exec in ImageAlpha to reduce colours and size.
        if [[ -x $pngquant ]]; then
            $pngquant -f $colours -o "$outfile" "$outfile"
        fi
    done
done

exit 0

Feel free to adjust the for width in ... line to use the image sizes that you need.

Usage

The script requires a 1024 x 1024 pixel PNG image as input. It creates the scaled images in the same directory:

$ resizeAppIcons ./work/export/AppIcon.png
./work/export/AppIcon.png
--> ./work/export/AppIcon-16.png
[...]
--> ./work/export/AppIcon-512.png
--> ./work/export/AppIcon-1024.png

Image Alpha

Finally, I can recommend the use of ImageAlpha, which will optimize your images and reduce the colours. Once installed in /Applications, the script will pick it up.

For my simple icons, I can get away with just 256 colours. If your icons are more complex, adjust the $colour variable as needed.