Sprite atlas generator

Sprite atlas generator

Postby bzt » 23 May 2022, 23:54

Hi,

As a twin brother to the sheet generator, here's an atlas generator too.

https://gitlab.com/bztsrc/spratlas

Features:
  • Input can be in any image format that stb_image understands (png, jpg, tga, bmp, pbm, etc.)
  • If input is a directory, then it is recursively parsed for image files
  • Uses Sean Barrett's rectangle packer, which in turn uses the Skyline algorithm (decently compact results at good speed)
  • Output atlas is automatically saved as an indexed PNG if it uses no more than 256 colors (lot smaller and more efficient)
  • Meta info is saved as a JSON string, into the atlas image (no more loosing the meta info file!)
  • Works with any PNG library, example code on how to retrieve the meta info provided for libpng, lodepng and stb_image as well.
Licensed under MIT, in the hope that it will be useful.

The code is written in ANSI C and dependency-free, meaning it should compile on any POSIX-compliant systems as-is without any trouble (just run make), but I've also provided pre-compiled portable executables for Linux and Windows (no .so/.dll required, no installation necessary, just download and use).

Cheers,
bzt
User avatar
bzt
 
Posts: 332
Joined: 23 May 2021, 21:46

Re: Sprite atlas generator

Postby deca9 » 24 May 2022, 01:40

The result is perfect:
http://decapode314.free.fr/games/shmup_ ... eet-71.png

In my previous messages, I was talking about 50 ships from Commander@OGA, I just realised that it was in fact more than 70 ships.
This tool just runs in a second, and gives a perfect result.
deca9
 
Posts: 18
Joined: 16 May 2022, 01:22

Re: Sprite atlas generator

Postby bzt » 24 May 2022, 02:18

You're welcome! And I see the meta info is stored perfectly in the comment too :-)

deca9 {l Wrote}:I just realised that it was in fact more than 70 ships.
No worries, spratlas can easily handle sprites in magnitude of hundreds of thousands (only limit is how much free RAM you have).

HINT: don't use absolute paths, instead change your working directory to "assets" and run spratlas there. That way only the filenames will be added to the JSON, not the entire paths (that might be problematic when you'll later try to identify which sprite is which from your code). For example:
{l Code}: {l Select All Code}
cd shmups/assets
spratlas -s 2048 -c ../atlas.png *.png
Or something like that. The point is, set the current working directory to the directory with the assets, and then the comment will look like:
{l Code}: {l Select All Code}
[
{ "x": 177, "y": 0, "w": 204, "h": 498, "name": "Commander_deep-space-ships_dipy01b" },
{ "x": 1284, "y": 0, "w": 249, "h": 464, "name": "Commander_deep-space-ships_dipy02" },
{ "x": 0, "y": 0, "w": 177, "h": 534, "name": "Commander_deep-space-ships_dipy03" },
...etc.
With one directory up, it's going to look like:
{l Code}: {l Select All Code}
cd shmups
spratlas -s 2048 -c atlas.png assets/*.png
{l Code}: {l Select All Code}
[
{ "x": 177, "y": 0, "w": 204, "h": 498, "name": "assets_Commander_deep-space-ships_dipy01b" },
{ "x": 1284, "y": 0, "w": 249, "h": 464, "name": "assets_Commander_deep-space-ships_dipy02" },
{ "x": 0, "y": 0, "w": 177, "h": 534, "name": "assets_Commander_deep-space-ships_dipy03" },
...etc.

Cheers,
bzt
Attachments
atlas_meta.png
using GIMP to display the JSON in the atlas
Last edited by bzt on 24 May 2022, 02:29, edited 1 time in total.
User avatar
bzt
 
Posts: 332
Joined: 23 May 2021, 21:46

Re: Sprite atlas generator

Postby deca9 » 24 May 2022, 02:24

Hi,
In case someone want an XML output too...

{l Code}: {l Select All Code}
diff --git a/sprpack b/sprpack
index fe1e4f9..bf62482 100755
Binary files a/sprpack and b/sprpack differ
diff --git a/src/main.c b/src/main.c
index 958a8b5..5a0cb4f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -50,7 +50,7 @@
 #endif
 
 /* command line arguments */
-int width = 4096, height = 4096, unpack = 0, crop = 0;
+int width = 4096, height = 4096, unpack = 0, crop = 0, xml = 0;
 char *comment = NULL;
 
 /* internal variables */
@@ -213,6 +213,7 @@ void usage()
     printf("./sprpack [-u|-l] <atlas png>\r\n\r\n");
     printf(" -s <w>,<h>      specify the output image's size (defaults to 4096 x 4096)\r\n");
     printf(" -c              crop output image to contents\r\n");
+    printf(" -x              outputs xml in stead of json\r\n");
     printf(" <output png>    output image, always (indexed or truecolor) png with alpha\r\n");
     printf(" <input>         input image(s) (png, jpg, gif, tga, bmp, etc.) or directories\r\n");
     printf(" -u <atlas png>  unpack an atlas\r\n");
@@ -237,6 +238,7 @@ int main(int argc, char **argv)
             switch(argv[i][1]) {
                 case 's': if(sscanf(argv[++i], "%d,%d", &width, &height) == 1) height = width; break;
                 case 'c': crop = 1; break;
+                case 'x': xml = 1; break;
                 case 'u': unpack = 1; break;
                 case 'l': unpack = 2; break;
                 default: fprintf(stderr,"Unknown flag '%c'\r\n", argv[i][1]); exit(2);
@@ -270,7 +272,9 @@ int main(int argc, char **argv)
         if(width > 0 && height > 0) {
             json = (char*)malloc(l);
             if(!json) { fprintf(stderr,"Not enough memory\r\n"); exit(1); }
-            memset(json, 0, l); strcpy(json, "[\r\n"); s = json + 3;
+            memset(json, 0, l);
+            if(!xml) { strcpy(json, "[\r\n"); s = json + 3;
+            } else s = json;
             o = (uint8_t*)malloc(width * height * 4);
             if(!o) { fprintf(stderr,"Not enough memory\r\n"); exit(1); }
             memset(o, 0, width * height * 4);
@@ -278,10 +282,14 @@ int main(int argc, char **argv)
                 src = data[i]; dst = o + (width * rects[i].y + rects[i].x) * 4;
                 for(j = 0; j < rects[i].h; j++, dst += width * 4, src += rects[i].w * 4)
                     memcpy(dst, src, rects[i].w * 4);
-                s += sprintf(s, "%s{ \"x\": %u, \"y\": %u, \"w\": %u, \"h\": %u, \"name\": \"%s\" }",
-                    i ? ",\r\n" : "", rects[i].x, rects[i].y, rects[i].w, rects[i].h, files[rects[i].id]);
+                if(xml)
+                  s += sprintf(s, "<sprite x=\"%u\" y=\"%u\" w=\"%u\" h=\"%u\" name=\"%s\" />\r\n",
+                          rects[i].x, rects[i].y, rects[i].w, rects[i].h, files[rects[i].id]);
+                else
+                  s += sprintf(s, "%s{ \"x\": %u, \"y\": %u, \"w\": %u, \"h\": %u, \"name\": \"%s\" }",
+                      i ? ",\r\n" : "", rects[i].x, rects[i].y, rects[i].w, rects[i].h, files[rects[i].id]);
             }
-            strcpy(s, "\r\n]");
+            if(!xml) strcpy(s, "\r\n]");
             image_save(o, width, height, png, json);
             free(o);
             free(json);
deca9
 
Posts: 18
Joined: 16 May 2022, 01:22

Re: Sprite atlas generator

Postby deca9 » 24 May 2022, 02:29

Don't worry for the absolute path, it's only because I usually use /tmp to make tests and tries.
deca9
 
Posts: 18
Joined: 16 May 2022, 01:22

Re: Sprite atlas generator

Postby bzt » 24 May 2022, 07:14

XML format added. And when I was there, I've added tab-text too.

Your patch was just the half of the solution btw, because the meta info has to be read back too. This was implemented too.

Cheers,
bzt
User avatar
bzt
 
Posts: 332
Joined: 23 May 2021, 21:46

Re: Sprite atlas generator

Postby deca9 » 24 May 2022, 15:05

Here is below a first submission on OGA:
https://opengameart.org/content/commander-sheet-93
deca9
 
Posts: 18
Joined: 16 May 2022, 01:22

Re: Sprite atlas generator

Postby bzt » 24 May 2022, 20:37

Nice! Give a star to my repo is you like it ;-)

BTW, I've added S-Expressions and C/C++ header as formats too, and I've implemented input sprite cropping for even better and more compact results.
However that latter changed the format a bit, now there are 8 numbers: x, y, w, h, X, Y, W, H.

Up until now, this was the algorithm to extract sprites from the atlas (with only x,y,w,h):
{l Code}: {l Select All Code}
sprite create (w,h)
copy atlas rect (x,y,w,h) to sprite (0,0,w,h)

+---------------------------+
|                           |
|   (x,y)+------+           |      (0,0)+------+
|        |      |           |   --->    |      |
|        +------+(w,h)      |           +------+(w,h)
|                           |
+---------------------------+

Now this becomes (with the additional X,Y,W,H values):
{l Code}: {l Select All Code}
sprite create (W,H)
copy atlas rect (x,y,w,h) to sprite (X,Y,w,h)

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

Small price for more compact atlases I believe (BTW, if the sprites can't be cropped, then the new meta format is backward compatible with the old one, eg. X=0, Y=0 and w=W, h=H).

Cheers,
bzt
User avatar
bzt
 
Posts: 332
Joined: 23 May 2021, 21:46

Who is online

Users browsing this forum: No registered users and 1 guest