Automatically cutting out images in Photoshop

2006-02-15

I was working on some home project the other day and I needed some images for a simple 2d game. Once I had made all the graphics in Photoshop I had to save each one to a separate file, then flatten each of those files, copy and paste each one back in to the original, then align the flattened copy with its corresponding part in the original and then look in the info palette and write down its coordinates. Just 10 images ended up taking 45 mins.

I thought there had to be a better way. Photoshop has had the ability to be scripted externally for a while now but it only actually got truly useful as of version 8.0 or Photoshop CS. I looked into it and the result is this script that will automatically cut out all my images and save the relevant information to a file ready for use in a game.

Imagine you are going to make a simple 2d game called Crowhunt. You need the following graphics. A crow, a feather, a crosshair, a hand + gun, the title graphics and a background image.

The script I wrote works by going down the first level of layers and groups in Photoshop and turning each one into a separate file cropped to the size of that layer.

It saves the images by the name of that layer or group.

One other feature I needed was the ability to mark a particular spot in an image as a reference point, usually as a rotation point. Before I would put the Photoshop cursor over that point, look at the info palette and write it down but again, that was a pain in the ass so instead I now create a group, name the group what I want the result saved as, put the layer with the image I want inside the group and then create a new layer inside that group with the name "rotpoint". In that layer I put a single pixel using the pencil tool, any color. The script will save the location of that pixel for me. You can see to the left in my example layer palette the handgun group has a "rotpoint" layer and if you look closely above you can see the 1 pixel yellow dot in the sleeve of the arm. That pixel will not appear in the final file, it just marks the rotation spot.

I also did not want to have to flatten anything and I wanted to be able to use Photoshop's layer effects. If you are a Photoshop user you might notice on the layers on the left the crosshair is a vector based layer and the title is a text based layer and the both have layer effects applied. The script will keep them as you see them there.

Once it's done I save the Photoshop file and run the script. When it's done I'll have one Photoshop file for each layer or group I had in the original, trimmed to its minimum size.

Here are the images as cut from the sample above

The first thing to notice is the handgun has more graphics than it appeared to originally. When you paste something into a layer in Photoshop, Photoshop by default keeps all of it. Later you might decide to move that layer and if you dragged it further into the image you'd be glad that's the way it works. In my case though I just wanted exactly what was on the screen so I did a "Select−>All" and an "Image−>Crop". Because I had all selected it doesn't appear the crop did anything but it actually cropped the stuff from any layer that had parts outside the image. I ran the script again and this time the handgun came out as I needed it.

The script also wrote out this file.

#define CUT_ALLPARTS \
SPRITEINFO(0,HANDGUN,"handgun.psd",120,285,121,286,70,214,184,300) \
SPRITEINFO(1,CROSSHAIR,"crosshair.psd",178,114,348,284,178,114,348,284) \
SPRITEINFO(2,TITLE,"title.psd",15,15,389,112,15,15,389,112) \
SPRITEINFO(3,CROW,"crow.psd",52,101,139,247,52,101,139,247) \
SPRITEINFO(4,FEATHER,"feather.psd",160,128,171,183,160,128,171,183) \
SPRITEINFO(5,BACKGROUND,"background.psd",0,0,400,300,0,0,400,300) \

The values in the macros are as follows. The first number is an index if you want one, followed by an ID and then a filename. The next 4 values are the bounds of the rotpoint or the bounds of the image if no rotpoint existed. The second set are the bounds of the image. The format of those are left, top, right, bottom where right and bottom are 1 pixel off the right and bottom. In other words, the width of an image is (right − left) not (right − left + 1).

To use that info in a game in C++ you might do something like this

#undef SPRITEINFO
#include "cut_allparts.psd.info.cpp"

#undef SPRITEINFO

#define SPRITEINFO(ndx,id,fname,rotleft,rottop,rotright,rotbtm,left,top,right,btm) \
  IMAGEID_ ## id,

enum ImageIds
{
    CUT_ALLPARTS

    IMAGEID_last,
};

struct ImageInfo
{
    const char* filename;
   int xPos;
   int yPos;
   int width;
   int height;
   int centerX;
   int centerY;

};

#undef SPRITEINFO
#define SPRITEINFO(                           \
            id,                               \
            label,                            \
            filename,                         \
            centerLeft,                       \
            centerTop,                        \
            centerRight,                      \
            centerBottom,                     \
            cutLeft,                          \
            cutTop,                           \
            cutRight,                         \
            cutBottom)                        \
    { _T("media/images/") _T(filename),       \
    (centerRight + centerLeft) / 2,           \
    (centerBottom + centerTop) / 2,           \
    cutRight - cutLeft,                       \
    cutBottom - cutTop,                       \
    (centerRight + centerLeft) / 2 - cutLeft, \
    (centerBottom + centerTop) / 2 - cutTop,  \
    },

ImageInfo ImageTable[] =
{
   CUT_ALLPARTS
};

Unfortunately, I know very few engines that can directly load Photoshop files. For my purposes I needed targa (.TGA) files. While it *should* be possible to get Photoshop to save targas the problem is I wanted alpha channels as well and I wanted them to have alpha that included the layer effects from Photoshop.

That's not trivial in Photoshop. The steps are something like:

  1. create a new empty layer
  2. merge the visible layers. This will remove the effects and bake them into the image.
  3. select the transparency of the layer
  4. set the background color to black and the foreground color to white
  5. go to the channel palette and create a new channel
  6. fill the selection with white

You'd now have the correct alpha. Unfortunately those steps are not easily accessible from scripting in Photoshop. While it would be possible, under the current script engine it requires using actions. I tried it once, Photoshop comes with a "ScriptListener" which will save all everything you do in Photoshop to a script which you can then edit later. I tried it but it was not easy to understand and as I already had a different solution that worked I decided to use that instead.

My different solution is a tool that is part of the libraries I use for games development written back in 1995 that will load Photoshop files and save them as targas (among other things). Photoshop by default saves a preview image inside its files that is the result off all the layers together as well as the transparency so converting that to a targa through my old tools gives me exactly what I need.

Here you can see the result. You can see the glow around the title and the shadow and border around the crosshair as well as the correct alpha for each of them.

To use the Photoshop cutsprite script manually you'll need at least Photoshop CS. Make a Photoshop file following the rules described above with each image in a layer or group and give a name to each layer or group. Save it. Then, Pick File−>Scripts−>Browse... from the menus and choose openandcutsprites.jsx. An open file dialog will appear. Select the file you saved. The script will then follow these steps

Warning: There is NO check for overwriting files so be careful!

I also wrote a script that will go through a tree of folders finding any Photoshop file that begins with "cut_" and will cut that file automatically. To run that you'll need to install ActivePerl (free). I could have written the script in VBScript or JScript but I'm far more comfortable with Perl so I did it there. Perl supports Microsoft's "Windows Scripting Host" which is basically a way for any language to be used as a scripting language for Windows. Microsoft supplies VBScript and JScript. There is also PerlScript and PythonScript.

The script will start from the current folder and look in every subfolder for any file that starts with "cut_" and ends in ".psd". If it sees such a file it will tell Photoshop to run the script above on that file. The easiest way to use it is just to copy it and the rest of the files to the base folder of whatever project you are working on. Double click on cutallsprites.wsf and it should run. Oh, before you do that you should open a command line and type

cscript //H:CScript

capitalization is important! This will set Windows Script Host to default to using the command line version.

Also included is another Perl program called processPSDtoTGA.pl. This program will walk all the folders from the current folder down and any ".PSD" file it finds it will create a corresponding ".TGA" file. Like the previous tool it's easiest if you just copy this and all the other files to the base folder of your current project and double click it to run it.

I hope you find this useful. The code is released under a BSD license so feel free to modify any of these to suit your own needs. You can download it from the link below

cutsprites.1.4.zip (666k)

Maya Support

I added a Maya Ascii scene file generator to the cutallsprites.wsf script so once the Photoshop file has been cut you'll find a ".ma" maya ascii file there. Load it up in Maya and it should reproduce the original setup along with points of origin in the correct spots.

Often many 3d engines have limited 2d abilities. They'll have a full on animated textures, materials, translation, rotation and scale for 3d but nothing for 2d. Well, just setup an orthographic camera and use your 3d engine for your 2d. Much simpler than writing a completely new system.

Version History:

Comments
Taxing Virtual Items
Language Selection Screens - Respect