shithub: atlas

ref: 530f54915ce55bec17d7580453437173efe20374
dir: /atlas.c/

View raw version
#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);
}