No Description

bzt ce42ba882e Updated tng format 3 months ago
src ce42ba882e Updated tng format 3 months ago
LICENSE 7010a97e29 initial release 2 years ago
README.md ce42ba882e Updated tng format 3 months ago
sprpack ce42ba882e Updated tng format 3 months ago
sprpack.exe ce42ba882e Updated tng format 3 months ago

README.md

Sprite Atlas Packer / Unpacker

HINT: If you're looking for a sprite sheet generator, take a look at sprsheet.

This is a little dependency free tool to pack and unpack sprites into and from an atlas. Its big advantage is that it saves the atlas meta info right into the resulting PNG image as a comment, so no additional file created (unless you explicitly want it). It uses stb_rect_pack.h (which in turn is based on the Skyline rectangle packing algorithm), so it does not necessarily provide the most compact atlas, but the results are good enough in almost every case and it is fast.

sprpack by bzt Copyright (C) 2022 MIT license
 https://gitlab.com/bztsrc/spratlas

./sprpack [-s <w>[,<h>]] [-c] [-j|-x|-t|-T|-e|-h] [-f] <output png> <inputs>
./sprpack [-u|-l] <atlas png>

 -s <w>,<h>      specify the output image's size (defaults to optimal)
 -c              crop output image to contents
 -d              do not crop input images
 -j              save meta info in JSON format (default)
 -x              save meta info in XML format
 -t              save meta info in tab-text format
 -T              save meta info in TirNanoG Atlas format
 -e              save meta info in S-Expression format
 -h              save meta info in C/C++ header format (implies -f)
 -f              save meta info into separate file too
 <output png>    output image, always (indexed or truecolor) png with alpha
 <inputs>        input image(s) (png, jpg, gif, tga, bmp, etc.) or directories
 -u <atlas png>  unpack an atlas
 -l <atlas png>  list sprites in an atlas (same format as stored)

Installation

Just download, no dependencies, no install, portable executables.

Compilation

Just run make in the src directory. No configure, no dependencies, suckless as it should be.

Packing Image Files into an Atlas

sprpack <output png> <input #1> [input #2 [input #3...]]

Inputs can be image files or directories. Images should be PNG, but other formats are supported too (anything that stb_image understands). If the input is a directory, then it is recursively walked through looking for further image files.

KNOWN ISSUE: the Linux version handles UTF-8 filenames well, but the Windows version does not support UNICODE characters in filenames. Use ASCII image names only for portability.

The resulting atlas image's size can be specified with the -s flag, and defaults to a calculated optimal size. If only one number is given, like -s 1024, then that will be both the width and height, otherwise you can specify them separately with a comma, like -s 1024,768. When -c is given, then the output is cropped to contents (final image size depends on the sprites packed into the atlas).

The input sprites are cropped (unless -d is given), and the following data is stored in the atlas meta info:

  • x, y: coordinates on atlas
  • w, h: stored area's size
  • X, Y: stored area's position on sprite (always 0 with -d)
  • W, H: sprite's original width and height (same as w, h with -d)
  • name: sprite's filename, without extension

Meta info by default is saved in JSON:

[
{ "x": x, "y": y, "w": w, "h": h, "X": X, "Y": Y, "W": W, "H": H, "name": "filename without extension" }
...
]

With the -x flag, the info will use XML:

<sprites>
<sprite x="x" y="y" w="w" h="h" X="X" Y="Y" W="W" H="H" name="filename without extension" />
...
</sprites>

Similarily, the -t uses tab-text (here the spaces are actually '\t', U+0009 characters, and line terminated by '\n', U+000A or optionally with a '\r' U+000D too):

x   y   w   h   X   Y   W   H   filename without extension

The -e will save S-Expressions, looks like:

(sprites
(sprite x y w h X Y W H "filename without extension")
...
)

The -h saves in C/C++ header format, and implies -f, saving into separate file.

#ifndef SPRPACKT
#define SPRPACKT
typedef struct { int x, y, w, h, X, Y, W, H; const char *name; } sprpack_t;
#endif
sprpack_t (atlasname)[] = {
 { x, y, w, h, X, Y, W, H, "filename without extension" }
 ...
};

The -T uses the TirNanoG format, also avaiable with TirNanoG Editor, tnge -p and tnge -u. The only difference is, that the TirNanoG Editor uses a modified best-fit algorithm, not Skyline. This is almost the same as tabtext, but has a header, uses spaces instead of tabs, and there's an additional column before the filename.

TirNanoG Atlas
x y w h X Y W H 0 filename without extension
...

Unpacking Atlas into Multiple Image Files

sprpack -u <input png>

The opposite direction, output images are extracted to the current working directory, and always encoded in PNG format. No need to specify the meta info's format, unpack will autodetect it. The atlas meta is always read from the PNG's comment.

The sprites are cropped on the atlas, so extracting one sprite goes like this:

atlas
+---------------------------+           sprite
|                           |           +--------------------+
|                           |           |                    |
|   (x,y)+------+           |           | (X,Y)+------+      |
|        |      |           |   --->    |      |      |      |
|        +------+(w,h)      |           |      +------+(w,h) |
|                           |           +--------------------+(W,H)
|                           |
+---------------------------+

The final sprite's dimensions are W x H. A rectangular area of w x h size at x, y is copied from the atlas to the sprite to position X, Y. If the sprite is an empty image, then w and h are zero (because nothing is stored on the atlas), however W and H aren't (because the sprite still has valid width and height). When a sprite couldn't be cropped (or -d was given on packing), then and only then w = W, h = H (and X = 0, Y = 0).

Extracting Atlas Info

sprpack -l <input png>

This will list the sprites packed in the atlas in the same format as it is stored in the image.

Of course you would want to get the atlas info in your app programatically too. How to do that, depends on which library you use to decode the PNG files. Basically any library will do which can return comments, here are the most commonly used ones:

libpng

Easy, already returns the comment in the png_info struct.

png_infop info_ptr;

/* ... read in the png file as usual ... */
png_read_info(png_ptr, info_ptr);

printf("Atlas info:\n%s\n", info_ptr->text[0].text);

lodepng

Using lodepng is similar to libpng, comment returned in the LodePNGState's LodePNGInfo struct:

LodePNGState state;

lodepng_decode(pixbuf, &w, &h, &state, pngbuf, pngbuf_len);

printf("Atlas info:\n%s\n", state->info_png.text_strings[0]);

stb_image.h

Finally, stb_image.h does not support comments by default, but a patched version can be found in this repository. To keep the modifications at a very minimum, you have to free the returned comment string yourself if its not NULL. This is how to use it:

img = stbi_load(filename, &w, &h, &n, 4);

if((comment = stbi_comment())) {
    printf("Atlas info:\n%s\n", comment);
    free(comment);
}

The patch itself is really minimal, only 18 SLoC, here it is in its entirety:

485a486
> STBIDEF char *stbi_comment(void);
5020a5022,5024
> static char *stbi__comment = NULL;
> char *stbi_comment(void) { char *old = (char*)stbi__comment; stbi__comment = NULL; return old; }
>
5025c5029
<    stbi_uc palette[1024], pal_img_n=0;
---
>    stbi_uc palette[1024], pal_img_n=0, *png_com;
5174a5179,5193
>          }
>          case STBI__PNG_TYPE('z','T','X','t'): {
>             png_com = STBI_MALLOC(c.length);
>             if (png_com) {
>                 if (stbi__getn(s, png_com, c.length) && !memcmp(png_com, "Comment", 8)) {
>                     if(stbi__comment) STBI_FREE(stbi__comment);
>                     k = 0; stbi__comment = (char *) stbi_zlib_decode_malloc_guesssize_headerflag(
>                         (char *)png_com + 9, c.length - 9, 4096, (int *) &k, 1);
>                     stbi__comment = (char *) STBI_REALLOC(stbi__comment, k + 1);
>                     if(stbi__comment) stbi__comment[k] = 0;
>                 }
>                 free(png_com);
>             } else
>                 stbi__skip(s, c.length);
>             break;

C/C++ header

For completeness, I also mention this version. No need to parse anything, just include the generated header in your code, and you'll be able to access the sprites' data instantly. For example (replace atlasname with your actual atlas' name):

#include <atlasname.h>

for(i = 0; i < sizeof(atlasname) / sizeof(sprpack_t); i++)
    printf("%s (%u x %u)\r\n", atlasname[i].name, atlasname[i].W, atlasname[i].H);

That's all, hope it will be useful.

Cheers, bzt