ref: 530f54915ce55bec17d7580453437173efe20374
dir: /atlas.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <memdraw.h> #define NULL nil #define STBRP_STATIC #define STBRP_LARGE_RECTS #define STBRP_SORT qsort #define STBRP_ASSERT assert #define STBRP__NOTUSED USED #define STB_RECT_PACK_IMPLEMENTATION #include "stb_rect_pack.h" static struct { u32int c; char *s; }chans[] = { {GREY1, "grey1"}, {GREY2, "grey2"}, {GREY4, "grey4"}, {GREY8, "grey8"}, {CMAP8, "cmap8"}, {RGB15, "rgb15"}, {RGB16, "rgb16"}, {RGB24, "rgb24"}, {RGBA32, "rgba32"}, {ARGB32, "argb32"}, {XRGB32, "xrgb32"}, {BGR24, "bgr24"}, {ABGR32, "abgr32"}, {XBGR32, "xbgr32"}, }; static Memimage * load(char *path) { Memimage *im; Waitmsg *msg; char *e; int p[2], pid; if ((e = strrchr(path, '.')) != nil) { if (strcmp(e, ".png") == 0) e = "/bin/png"; else if (strcmp(e, ".tga") == 0) e = "/bin/tga"; else if (strcmp(e, ".bmp") == 0) e = "/bin/bmp"; else if (strcmp(e, ".jpg") == 0) e = "/bin/jpg"; else e = nil; } if (e == nil) { werrstr("unsupported format"); return nil; } pipe(p); if ((pid = rfork(RFPROC|RFFDG|RFREND|RFNOTEG)) == 0) { close(0); close(p[0]); dup(p[1], 1); close(p[1]); dup(open("/dev/null", OWRITE), 2); execl("/bin/png", "png", "-9t", path, nil); sysfatal("load: %r"); } close(p[1]); im = nil; if (pid > 0) im = readmemimage(p[0]); msg = wait(); if (msg->msg[0] != 0) { im = nil; werrstr(msg->msg); } free(msg); close(p[0]); return im; } static void usage(void) { int i; print("usage: atlas [-o offsets] [-c chan] [-b backcolor] [file ...]\n\n"); print("For each image of the atlas a line of format \"filename x y w h\"\n"); print("is written to file specified with -o. Each line describes where\n"); print("the image is located on the final atlas image.\n"); print("The atlas itself is written to stdout.\n"); print("-b sets the background color in rgba format, default is 0x00000000 (transparent).\n"); print("Default chan is rgba32; supported values:"); for (i = 0; i < nelem(chans); i++) print(" %s", chans[i].s); print(".\n"); exits("usage"); } void main(int argc, char **argv) { char *o, *c; stbrp_rect *rects, r; stbrp_node *nodes; Memimage **im, *b; stbrp_context ctx; int i, w, h, n, fd, chan, delta; u32int backcolor; o = nil; chan = RGBA32; backcolor = 0; ARGBEGIN { case 'o': o = EARGF(usage()); break; case 'b': backcolor = strtoul(EARGF(usage()), &c, 0); if (*c != 0) { fprint(2, "bad color \"%s\"\n", c); usage(); } break; case 'c': c = EARGF(usage()); for (i = 0; i < nelem(chans); i++) { if (strcmp(chans[i].s, c) == 0) { chan = chans[i].c; break; } } if (i >= nelem(chans)) { fprint(2, "unknown chan \%s\"\n", c); usage(); } break; default: usage(); } ARGEND; if (argc < 1) usage(); if (memimageinit() != 0) sysfatal("initdraw: %r"); im = calloc(argc, sizeof(*im)); rects = calloc(argc, sizeof(*rects)); w = h = n = 0; delta = 99999; for (i = 0; i < argc; i++) { if ((im[i] = load(argv[i])) == nil) sysfatal("%s: %r", argv[i]); rects[i].id = i; rects[i].w = Dx(im[i]->r); rects[i].h = Dy(im[i]->r); if (rects[i].w <= 0 || rects[i].h <= 0) sysfatal("%s: zero width/height", argv[i]); if (w < rects[i].w) w = rects[i].w; if (h < rects[i].h) h = rects[i].h; if (delta > w) delta = w; if (delta > h) delta = h; n += rects[i].w; } nodes = calloc(n, sizeof(*nodes)); for (;;) { stbrp_init_target(&ctx, w, h, nodes, n); if (stbrp_pack_rects(&ctx, rects, argc) == 1) break; w += delta; h += delta; } if ((fd = o == nil ? open("/dev/null", OWRITE) : create(o, OWRITE, 0664)) < 0) sysfatal("can't write: %r"); w = h = 0; for (i = 0; i < argc; i++) { r = rects[i]; if (w < r.x+r.w) w = r.x+r.w; if (h < r.y+r.h) h = r.y+r.h; if (o != nil) fprint(fd, "%s\t%d\t%d\t%d\t%d\n", argv[r.id], r.x, r.y, r.w, r.h); } fprint(fd, "atlas_dimensions\t0\t0\t%d\t%d\n", w, h); close(fd); if ((b = allocmemimage(Rect(0, 0, w, h), chan)) == nil) sysfatal("failed to allocate %dx%d image", w, h); memfillcolor(b, backcolor); for (i = 0; i < argc; i++) { r = rects[i]; memimagedraw(b, Rect(r.x, r.y, r.x+r.w, r.y+r.h), im[rects[i].id], ZP, nil, ZP, SoverD); } writememimage(1, b); exits(nil); }