ref: af833477cb5910fea2c4eb99e6e911696380af46
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;
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;
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;
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 += 32;
h += 32;
}
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);
}
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);
}