bzt ce42ba882e Updated tng format | před 3 měsíci | |
---|---|---|
src | před 3 měsíci | |
LICENSE | před 2 roky | |
README.md | před 3 měsíci | |
sprpack | před 3 měsíci | |
sprpack.exe | před 3 měsíci |
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)
Just download, no dependencies, no install, portable executables.
Just run make
in the src directory. No configure, no dependencies, suckless as it should be.
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 atlasw
, h
: stored area's sizeX
, 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 extensionMeta 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
...
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
).
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:
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);
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]);
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;
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