Introduction

I've decided to open source our in-house texture atlas creation tool,
because I think this will be useful for other independent game
developers.

Features

Built for speed

Loads textures nearly 5 times faster than loading from PNG

One click operation

Streamlines the art pipeline. Creates multiple atlases with multiple formats, all with one click.

Open source

Java source code provided, so you can modify the tool for your own needs.

What is a texture atlas?

A texture atlas bin-packs multiple
images into a single texture. On some platforms OpenGL textures have to be sized
as power of 2, loading the images separately would require a lot of
padding, which means wasted GPU memory. As well as that having all
images in one texture allows draw-call batching: in other words several
sprites can be drawn with one call.

Who is this for?

Although the tool was developed for the iPhone, it is useful for most platforms including Android and PC. The tool is written in Java and runs on Windows, Mac and Linux.

A sample iPhone project is included, that demonstrates loading the texture atlas. The code is c++ and designed to be portable.

How to use

Download the file AtlasMaker_jar_2.2.1.zip and unzip. You will need Java installed on your machine to use it. You may also need to install the PVRTexTool from Imagination Technologies if you want to create PVR compressed textures.

Input folder

The source images for your project must be in PNG format and must be
organised in directories according to file format. Select the location
of the input folder and the directory structure will be shown. Then select
the required file format for each directory

Targa True Color

This is the default option. Choose this for best quality. Note: targa files have RGB bytes in OpenGL order, so they load faster. This means the file will look odd if you open it on your PC, but it will look fine on the device. The file is 32-bit, or 24 bit if there is no alpha channel.

16-bit Targa

Choosing this option uses only 16-bits per pixel, but reduces the number of colours. Textures with subtle colour changes (i.e. gradients) will look bad.

PVR

Compressed PVR format. Uses either 2, or 4-bits per pixel. This format works better with "organic" images with no transparent borders. Note: a padding of 2 pixels is created around each image. This is to prevent pixels appearing from adjacent images (the PVR algorithm draws pixels outside of the image borders).

Output file

Select the name and location of the output file. The texture atlases will be saved to the same directory.

Advanced options

Click on the "Options..." button to open advanced options.

Maximium size

This is the maximum texture size (both width and height) for the device you are targetting.

Power-of-two / Non power-of-two

Power of two restricts the size to power of two dimensions (ie. 32x32, 64x32, etc.). Non power-of-two means there is no restriction at all.

Alpha threshold

Transparent pixels are trimmed off all source images. The alpha threshold indicates what is considered to be transparent.

Location of PVRTexTool

This is the file location where you installed the command line PVRTexTool.

PVR Options

When the PVR file format is selected, first the texture atlas will be created as a PNG, then the PVRTexTool will be called to convert it to PVR. This is a slow operation, so by default "Convert PVRs" is deselected.

Output tabs

A tab is created for each texture. The tab icon hold a preview of the texture. For convenience, clicking anywhere in the texture will display the file name of the image clicked on.

Textures created

The application tries to fit the images in the smallest texture size
possible. In the case of power-of-two textures first it tries to fit in 32x32, then 32x64, then 64x64,
then 128x64... and so on till the maximum. In the case of Non power-of-two it'll choose the minimum arbitary size automatically. If the images don't in the maximum size texture, additional textures are created until everything fits.

If you choose the PVR format the textures will be square and power-of-two.

Each file will be named by the directory and an index number. E.g. if images are in the directory "images" then the following textures may be created: images0.tga, images1.tga, images2.tga, etc.

Output XML file

The application creates an XML file, which contains all data for each image in each texture atlas, example:

First, the "atlas" element tells you total images in atlas, so memory can be pre-allocated when loading.

Then each texture element has the file name, transparency and a list of image elements.

Each image element contains the following data:

x, y, width, height

The position within the texture and size of the image

xOffset, yOffset, transWidth, transHeight

Padding data for sprite animations. If we have sprite animations, each sprite needs to be padded so that the image doesn't "jump" around during animation,
but we don't want to store the blank space in the texture and waste memory.

Why PNG is unsuitable for games

PNG is a complex format and slow to decode. Here are some benchmarks, which show just how slow PNG is:

PNG

Targa 32-bit*

Targa 16-bit

Loading Windows (libpng)

64 ms

9 ms

5 ms

Loading iPhone 3GS*

385 ms

83 ms

Size

1.27 MB

2.42 MB

1.23 MB

Zipped size

1.26 MB

1.25 MB

339 kB

Loading a 1024 x 1024 texture (complex image with transparency)

As you can see on the iPhone texture loading is 4.6 times slower than 32-bit Targa. Although the Targa file is twice as big it's the same size when zipped, so the package size will be the same. On top of that, if your images only need to be 16-bit quality you get double savings. PNG does not have any 16-bit option.

Loading Targa

See the ImageTarga and TargaReader classes for details how the targa files are loaded.

Targa 16 bit is stored as GL_UNSIGNED_SHORT_4_4_4_4. However, if the texture atlas is completely opaque the alpha channel is not saved and the pixels are stored as GL_UNSIGNED_SHORT_5_6_5.

If "Targa True Color" is selected then it's saved as either 32-bit or 24-bit, depending on if it has alpha transparency or not.

Demo iPhone project

Included is a xcode project that opens the generated textures and displays a sprite. Most of the code is c++ for portablity.

The following classes are used in the project:

Game

Main game object.

ResManager

Sets up OpenGL state and initialises all resources

AtlasMaster

Reads atlas xml file and creates the AtlasImage objects and textures and stores them in a map for retrieval.

XParser

Fast XML parser, with cut down functionality. Parses using forward-only method to avoid the overhead of creating a DOM.

TargaReader

Reads 32-bit and 16-bit Targa files

CommonBuffer

All texture files are loaded into a shared buffer, rather than creating and deleting each time

ImagePVR

Loads PVR textures.

AtlasImage

Creates a textured quad for each image.

Note: the project uses boost/unordered_map.hpp. You need to download boost and set the project include headers in xcode to point to where you installed boost. Alternatively, you can change the line: #define fmap boost::unordered_map, to #define fmap std:map in precompile.h

You're hardly going to make a black-and-white game are you? Therefore you can assume we need all images to have 4 channels RGBA. And there is no PNG option to do that with only 16-bits per pixel.Indexing PNG is only going to work if you have a small amount of colours, which is not going to be the case.

By the way, the benchmarks in the article are for pngs created by java.Just out of curiosity I tried benchmarking png's created by GIMP. (GIMP is the only tool I know that gives an option of compression level.) It turns out PNGs created by the gimp are twice as slow to load and it makes no difference what level of compression you use (1-9)

I now understand what you meant by 16 bit not being an option (it wasn't exactly clear to me). PNG doesn't support an indexed type with a 16 bit palette. In an Atlas you probably would run into the 256 color ceiling fairly quickly.

I'm not sure what's going on with gimp--it would have to be checked with TweakPNG to see what 1-9 does. Since the decompression times are unchanged, either decompression is not the bottleneck, or 1-9 just means that gimp tries harder at the same level of compression. An uncompressed PNG should decompress faster than a compressed PNG.

Perhaps these benchmarks, along with the silliness with the pre-multiply that XCode does, is why people often use their own or someone else's PNG lib on iOS.