shithub: imgtools

ref: cd326e72db3f6609dd2093c6934f9c4b8a8abc71
dir: /resample.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#define STBIR_MALLOC(x,u) malloc(x)
#define STBIR_FREE(x,u) free(x)
#define STBIR_ASSERT(x) assert(x)
#define NULL nil
typedef uintptr size_t;
#include "stb_image_resize.h"

enum {
	Stpixels,
};

static char *filters[] = {
    [STBIR_FILTER_BOX] = "box",
    [STBIR_FILTER_TRIANGLE] = "triangle",
    [STBIR_FILTER_CUBICBSPLINE] = "cubicbspline",
    [STBIR_FILTER_CATMULLROM] = "catmullrom",
    [STBIR_FILTER_MITCHELL] = "mitchell",
};

static char *cspaces[] = {
	[STBIR_COLORSPACE_LINEAR] = "linear",
	[STBIR_COLORSPACE_SRGB] = "srgb",
};

static void
usage(void)
{
	fprint(2, "usage: %s [-P] [-x size] [-y size] [-r x0,y0,x1,y1] [-f filter] [-c colorspace]\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	char *flts, *s, *csps, *e;
	int n, bp, to, stmode;
	int alphai, flags;
	int w, h, ow, oh, iw, ih;
	int wp, hp, f, c;
	Memimage *a, *b;
	u8int *in, *out;
	double st[4];

	ow = oh = 0;
	wp = hp = 0;
	alphai = STBIR_ALPHA_CHANNEL_NONE;
	flags = STBIR_FLAG_ALPHA_PREMULTIPLIED;
	flts = filters[STBIR_FILTER_MITCHELL];
	csps = cspaces[STBIR_COLORSPACE_LINEAR];
	st[0] = 0;
	st[1] = 0;
	st[2] = 1;
	st[3] = 1;
	stmode = -1;
	ARGBEGIN{
	case 'x':
		s = EARGF(usage());
		wp = (ow = atoi(s)) > 0 && s[strlen(s)-1] == '%';
		break;
	case 'y':
		s = EARGF(usage());
		hp = (oh = atoi(s)) > 0 && s[strlen(s)-1] == '%';
		break;
	case 'f':
		flts = EARGF(usage());
		break;
	case 'c':
		csps = EARGF(usage());
		break;
	case 'P':
		flags &= ~STBIR_FLAG_ALPHA_PREMULTIPLIED;
		break;
	case 'r':
		s = EARGF(usage());
		stmode = Stpixels;
		e = s;
		for(n = 0; n < 4; n++, e++){
			st[n] = strtod(e, &e);
			if(*e != ',')
				break;
		}
		if(n != 3 || st[2] <= st[0] || st[3] <= st[1])
			sysfatal("invalid rect %s", s);
		break;
	default:
		usage();
	}ARGEND

	for(f = 0; f < nelem(filters) && (filters[f] == nil || strcmp(flts, filters[f]) != 0); f++);
	if(f >= nelem(filters))
		sysfatal("invalid filter %s", flts);
	for(c = 0; c < nelem(cspaces) && strcmp(csps, cspaces[c]) != 0; c++);
	if(c >= nelem(cspaces))
		sysfatal("invalid colorspace %s", csps);

	if(wp && oh == 0){
		oh = ow;
		hp = 1;
	}else if(hp && ow == 0){
		ow = oh;
		wp = 1;
	}

	memimageinit();
	if((a = readmemimage(0)) == nil)
		sysfatal("memory");
	w = Dx(a->r);
	h = Dy(a->r);

	if(stmode == Stpixels){
		iw = st[2]-st[0];
		ih = st[3]-st[1];
		st[0] /= (double)w;
		st[1] /= (double)h;
		st[2] /= (double)w;
		st[3] /= (double)h;
	}else{
		iw = w;
		ih = h;
	}

	if(ow == 0 && oh == 0){
		ow = iw;
		oh = ih;
	}
	if(wp)
		ow = iw*ow/100.0;
	if(hp)
		oh = ih*oh/100.0;
	if(oh == 0)
		oh = ow*ih/iw;
	if(ow == 0)
		ow = oh*iw/ih;
	if(ow < 1 || oh < 1)
		sysfatal("invalid size: %dx%d", ow, oh);

	bp = 0;
again:
	switch(a->chan){
	case GREY8:
		bp = 1;
		break;
	case RGB24:
		bp = 3;
		break;
	case RGBA32:
		bp = 4;
		alphai = 3;
		break;
	case ARGB32:
	case XRGB32:
		bp = 4;
		alphai = 0;
		break;

	case GREY1:
	case GREY2:
	case GREY4:
		to = GREY8;
		goto convert;
	case CMAP8:
	case RGB15:
	case RGB16:
		to = RGB24;
		goto convert;
	case BGR24:
		to = RGB24;
		goto convert;
	case ABGR32:
		to = ARGB32;
		goto convert;
	case XBGR32:
		to = XRGB32;
convert:
		if((b = allocmemimage(a->r, to)) == nil)
			sysfatal("memory");
		memimagedraw(b, a->r, a, ZP, nil, ZP, S);
		freememimage(a);
		a = b;
		goto again;
	default:
		sysfatal("invalid chan %#lux", a->chan);
	}

	n = w*h*bp;
	if((in = malloc(w*h*bp)) == nil)
		sysfatal("%r");
	if(unloadmemimage(a, a->r, in, n) < 0)
		sysfatal("%r");
	if((out = malloc(ow*oh*bp)) == nil)
		sysfatal("memory");
	stbir_resize_region(
		in, w, h, w*bp,
		out, ow, oh, ow*bp,
		STBIR_TYPE_UINT8,
		bp, alphai, flags,
		STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP,
		f, f,
		c, nil,
		st[0], st[1], st[2], st[3]
	);
	free(in);
	if((b = allocmemimage(Rect(0,0,ow,oh), a->chan)) == nil)
		sysfatal("%r");
	loadmemimage(b, b->r, out, ow*oh*bp);
	writememimage(1, b);

	exits(nil);
}