Friday, May 3, 2013

Trimming Transparency Follow-up: Cropping

Just a really quick follow-up to my recent post about trimming your static images.  I got some great thoughtful comments on my G+ account regarding this and it got me thinking - I needed to clarify how you could use this same technique not just for static images but also for animation sprite sheets.

I posted an update section on that recent post, but I'll repeat it here:
Update: I realised that this post could confuse people into thinking that your sprite sheets of animations could be processed this way. This won't work. I know I said these images are coming out of my animation program but that is because they are stills that I use for the various static poses, and since I use AnimeStudio for all my character art I have to get the sprites looking consistent, so that means whether animated or still I need to use the same program.
Why not? When you trim off the transparency close to the colored pixel content that means the position of the character in the frame will change from frame to frame giving a jittery effect. Definitely not what you want for your animations. But for static poses this technique works just fine.
OK - so how could you use ImageMagick to trim your animated sprite frames?  There's two comments to make before we dive into the script I came up with to help with this:

  • First - do you really need to trim them?  TexturePacker makes a good job of ridding yourself of these extra transparent pixels, and if you already have a way of hit-testing that won't be compromised by the extra alpha may not be a problem.  Thanks to +Krzysztof Bielawski for highlighting this.
  • Second - lets use the word crop to refer to the process of cutting off unwanted parts of the image to precise pixel dimensions
    • Trimming refers to getting rid of all alpha transparency regardless of where that happens to be pixel-addressing-wise.
If your game doesn't require hit testing your animated sprites in that way - maybe you just use a simple radius test - then there's no point in worrying about this trimming probably.

If we do decide to go ahead with this lets get terminology right - ImageMagick uses these terms and from my graphics work I follow the same approach.  Trim means cutting away unwanted stuff (no matter what the dimensions) and cropping is like guillotine - once you position the bounds to the pixel that is where the cut falls.

OK - so we're going to crop?  Here is how to do it:

convert my_sprite_0001.png -crop \
    '309x518+65+238' +repage my_sprite_cropped_0001.png

By referring to my recent post, you can find out how to install ImageMagick, and what the "convert" command-line here is doing.  As before the repage is recalculating the page meta-data after the crop command.

What's new is the -crop and the strange expression in quotes.  '309x518+65+238'

The first two numbers (seperated by the "x" character) are the width and height of the area to be cropped out.  This area needs to be as large as the union of the bounding rectangles of all the sprites in your animation sequence.

The second two numbers represent the offset into your source image (my_sprite_0001.png in the above) as an x, y coordinate pair - with the top-left of the image as the origin.

Note that Cocos2D and many other OpenGL based game kits use the bottom-left as the origin - and mathematical co-ordinate systems do this too.  Take a bit of care to make sure this is right.

Wait: what?  Union of the bounding rectangles?  Yup - this just means that whatever rectangle you choose as your crop is going to be the same size and position for every crop operation done on your folder of images.  This means that in order to avoid cutting off any part of one of the animation frames it will need to be equal to all of the rectangles that would have been used in a trim operation, all overlaid over each other - this "rectangle summing" is called the "union".

In practice I found the easiest way to do this is to find a large image, say the "contact pose" - see my early "rushy animation tut" post - and put a box around that with my image editor (I use GiMP, but you might use photoshop) and check the dimensions and offset.

Then I run my crop script using those dimensions, and check the result using Preview - if an image in the sequence got some part cropped off I can open that in my image editor and adjust those dimensions.

Checking the crop by using the Preview program on Mac
Here's an example of the script running, copied straight out of my Terminal session. Just after the script does its processing I run Mac's Preview program on all the images by using the "open" command:

~/Documents/Art/animations sez$ ./cropdir.sh walk-cycle-side-12fps-fresh 309 518 65 238
About to make cropped copies of the files in walk-cycle-side-12fps-fresh and place into walk-cycle-side-12fps-fresh-converted-1367637222
The resulting cropped copies will be 309w x 518h and at offset 65, 238 in
the image.

Continue (y/n) ?  [N]: >Y
   walk-cycle-side-12fps-fresh/walk-side_00001.png    -- cropped to -->   walk-cycle-side-12fps-fresh-converted-1367637222/walk-side_00001.png
   walk-cycle-side-12fps-fresh/walk-side_00003.png    -- cropped to -->   walk-cycle-side-12fps-fresh-converted-1367637222/walk-side_00003.png
   //.... more
   walk-cycle-side-12fps-fresh/walk-side_00047.png    -- cropped to -->   walk-cycle-side-12fps-fresh-converted-1367637222/walk-side_00047.png
   walk-cycle-side-12fps-fresh/walk-side_00049.png    -- cropped to -->   walk-cycle-side-12fps-fresh-converted-1367637222/walk-side_00049.png
~/Documents/Art/animations sez$ 
~/Documents/Art/animations sez$ open walk-cycle-side-12fps-fresh-converted-1367637222/*

And this is a complete listing of the script I use. As before - caveat emptor, backup before use, all care, no responsibility.

#!/bin/bash

# Copyright Sarah Smith - http://indiegamecodingconfessions.blogspot.com

# ImageMagick is from http://www.imagemagick.org

# This script is placed in the public domain - feel free to use it how you
# want, though a link to this blog would be nice.  :-)

if [ $# != 5 ]; then
    echo "Usage: $0 <dirname> <width> <height> <x-offset> <y-offset>"
    exit 1
fi

# Halt execution the second a command fails
set -e

# Uncomment this line to get debugging info
# set -x

UNIQ_NM=$(date "+%s")
DESTDIR="$1-converted-$UNIQ_NM"

echo "About to make cropped copies of the files in $1 and place into $DESTDIR"
echo "The resulting cropped copies will be $2w x $3h and at offset $4, $5 in"
echo "the image."
echo ""
echo -n "Continue (y/n) ?  [N]: >"

read goahead

if [ "x$goahead"="xy" -o "x$goahead"="xY" ]; then

    mkdir $DESTDIR
    GEOMETRY="$2x$3+$4+$5"

    for fn in $1/*; do
        DESTNAME=$DESTDIR/$(basename $fn)
        convert $fn -crop $GEOMETRY +repage $DESTNAME
        echo "   $fn    -- cropped to -->   $DESTNAME"
    done

fi

And you can download this script from my Dropbox.  As in my previous post, just chmod it to executable and run it from the command line as above.

Happy image hacking!