xref: /plan9/sys/src/cmd/resample.c (revision 6a9fc400c33447ef5e1cda7185cb4de2c8e8010e)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <draw.h>
49a747e4fSDavid du Colombier #include <memdraw.h>
59a747e4fSDavid du Colombier 
69a747e4fSDavid du Colombier #define K2 7	/* from -.7 to +.7 inclusive, meaning .2 into each adjacent pixel */
79a747e4fSDavid du Colombier #define NK (2*K2+1)
89a747e4fSDavid du Colombier double K[NK];
99a747e4fSDavid du Colombier 
109a747e4fSDavid du Colombier double
fac(int L)119a747e4fSDavid du Colombier fac(int L)
129a747e4fSDavid du Colombier {
139a747e4fSDavid du Colombier 	int i, f;
149a747e4fSDavid du Colombier 
159a747e4fSDavid du Colombier 	f = 1;
169a747e4fSDavid du Colombier 	for(i=L; i>1; --i)
179a747e4fSDavid du Colombier 		f *= i;
189a747e4fSDavid du Colombier 	return f;
199a747e4fSDavid du Colombier }
209a747e4fSDavid du Colombier 
219a747e4fSDavid du Colombier /*
229a747e4fSDavid du Colombier  * i0(x) is the modified Bessel function, Σ (x/2)^2L / (L!)²
239a747e4fSDavid du Colombier  * There are faster ways to calculate this, but we precompute
249a747e4fSDavid du Colombier  * into a table so let's keep it simple.
259a747e4fSDavid du Colombier  */
269a747e4fSDavid du Colombier double
i0(double x)279a747e4fSDavid du Colombier i0(double x)
289a747e4fSDavid du Colombier {
299a747e4fSDavid du Colombier 	double v;
309a747e4fSDavid du Colombier 	int L;
319a747e4fSDavid du Colombier 
329a747e4fSDavid du Colombier 	v = 1.0;
339a747e4fSDavid du Colombier 	for(L=1; L<10; L++)
349a747e4fSDavid du Colombier 		v += pow(x/2., 2*L)/pow(fac(L), 2);
359a747e4fSDavid du Colombier 	return v;
369a747e4fSDavid du Colombier }
379a747e4fSDavid du Colombier 
389a747e4fSDavid du Colombier double
kaiser(double x,doubleτ,doubleα)399a747e4fSDavid du Colombier kaiser(double x, double τ, double α)
409a747e4fSDavid du Colombier {
419a747e4fSDavid du Colombier 	if(fabs(x) > τ)
429a747e4fSDavid du Colombier 		return 0.;
439a747e4fSDavid du Colombier 	return i0(α*sqrt(1-(x*x/(τ*τ))))/i0(α);
449a747e4fSDavid du Colombier }
459a747e4fSDavid du Colombier 
469a747e4fSDavid du Colombier void
usage(void)479a747e4fSDavid du Colombier usage(void)
489a747e4fSDavid du Colombier {
499a747e4fSDavid du Colombier 	fprint(2, "usage: resample [-x xsize] [-y ysize] [imagefile]\n");
509a747e4fSDavid du Colombier 	fprint(2, "\twhere size is an integer or a percentage in the form 25%%\n");
519a747e4fSDavid du Colombier 	exits("usage");
529a747e4fSDavid du Colombier }
539a747e4fSDavid du Colombier 
549a747e4fSDavid du Colombier int
getint(char * s,int * percent)559a747e4fSDavid du Colombier getint(char *s, int *percent)
569a747e4fSDavid du Colombier {
579a747e4fSDavid du Colombier 	if(s == nil)
589a747e4fSDavid du Colombier 		usage();
599a747e4fSDavid du Colombier 	*percent = (s[strlen(s)-1] == '%');
609a747e4fSDavid du Colombier 	if(*s == '+')
619a747e4fSDavid du Colombier 		return atoi(s+1);
629a747e4fSDavid du Colombier 	if(*s == '-')
639a747e4fSDavid du Colombier 		return -atoi(s+1);
649a747e4fSDavid du Colombier 	return atoi(s);
659a747e4fSDavid du Colombier }
669a747e4fSDavid du Colombier 
679a747e4fSDavid du Colombier void
resamplex(uchar * in,int off,int d,int inx,uchar * out,int outx)689a747e4fSDavid du Colombier resamplex(uchar *in, int off, int d, int inx, uchar *out, int outx)
699a747e4fSDavid du Colombier {
709a747e4fSDavid du Colombier 	int i, x, k;
719a747e4fSDavid du Colombier 	double X, xx, v, rat;
729a747e4fSDavid du Colombier 
739a747e4fSDavid du Colombier 
749a747e4fSDavid du Colombier 	rat = (double)inx/(double)outx;
759a747e4fSDavid du Colombier 	for(x=0; x<outx; x++){
769a747e4fSDavid du Colombier 		if(inx == outx){
779a747e4fSDavid du Colombier 			/* don't resample if size unchanged */
789a747e4fSDavid du Colombier 			out[off+x*d] = in[off+x*d];
799a747e4fSDavid du Colombier 			continue;
809a747e4fSDavid du Colombier 		}
819a747e4fSDavid du Colombier 		v = 0.0;
829a747e4fSDavid du Colombier 		X = x*rat;
839a747e4fSDavid du Colombier 		for(k=-K2; k<=K2; k++){
849a747e4fSDavid du Colombier 			xx = X + rat*k/10.;
859a747e4fSDavid du Colombier 			i = xx;
869a747e4fSDavid du Colombier 			if(i < 0)
879a747e4fSDavid du Colombier 				i = 0;
889a747e4fSDavid du Colombier 			if(i >= inx)
899a747e4fSDavid du Colombier 				i = inx-1;
909a747e4fSDavid du Colombier 			v += in[off+i*d] * K[K2+k];
919a747e4fSDavid du Colombier 		}
929a747e4fSDavid du Colombier 		out[off+x*d] = v;
939a747e4fSDavid du Colombier 	}
949a747e4fSDavid du Colombier }
959a747e4fSDavid du Colombier 
969a747e4fSDavid du Colombier void
resampley(uchar ** in,int off,int iny,uchar ** out,int outy)979a747e4fSDavid du Colombier resampley(uchar **in, int off, int iny, uchar **out, int outy)
989a747e4fSDavid du Colombier {
999a747e4fSDavid du Colombier 	int y, i, k;
1009a747e4fSDavid du Colombier 	double Y, yy, v, rat;
1019a747e4fSDavid du Colombier 
1029a747e4fSDavid du Colombier 	rat = (double)iny/(double)outy;
1039a747e4fSDavid du Colombier 	for(y=0; y<outy; y++){
1049a747e4fSDavid du Colombier 		if(iny == outy){
1059a747e4fSDavid du Colombier 			/* don't resample if size unchanged */
1069a747e4fSDavid du Colombier 			out[y][off] = in[y][off];
1079a747e4fSDavid du Colombier 			continue;
1089a747e4fSDavid du Colombier 		}
1099a747e4fSDavid du Colombier 		v = 0.0;
1109a747e4fSDavid du Colombier 		Y = y*rat;
1119a747e4fSDavid du Colombier 		for(k=-K2; k<=K2; k++){
1129a747e4fSDavid du Colombier 			yy = Y + rat*k/10.;
1139a747e4fSDavid du Colombier 			i = yy;
1149a747e4fSDavid du Colombier 			if(i < 0)
1159a747e4fSDavid du Colombier 				i = 0;
1169a747e4fSDavid du Colombier 			if(i >= iny)
1179a747e4fSDavid du Colombier 				i = iny-1;
1189a747e4fSDavid du Colombier 			v += in[i][off] * K[K2+k];
1199a747e4fSDavid du Colombier 		}
1209a747e4fSDavid du Colombier 		out[y][off] = v;
1219a747e4fSDavid du Colombier 	}
1229a747e4fSDavid du Colombier 
1239a747e4fSDavid du Colombier }
1249a747e4fSDavid du Colombier 
1259a747e4fSDavid du Colombier int
max(int a,int b)1269a747e4fSDavid du Colombier max(int a, int b)
1279a747e4fSDavid du Colombier {
1289a747e4fSDavid du Colombier 	if(a > b)
1299a747e4fSDavid du Colombier 		return a;
1309a747e4fSDavid du Colombier 	return b;
1319a747e4fSDavid du Colombier }
1329a747e4fSDavid du Colombier 
1339a747e4fSDavid du Colombier Memimage*
resample(int xsize,int ysize,Memimage * m)1349a747e4fSDavid du Colombier resample(int xsize, int ysize, Memimage *m)
1359a747e4fSDavid du Colombier {
1369a747e4fSDavid du Colombier 	int i, j, bpl, nchan;
1379a747e4fSDavid du Colombier 	Memimage *new;
1389a747e4fSDavid du Colombier 	uchar **oscan, **nscan;
1399a747e4fSDavid du Colombier 
1409a747e4fSDavid du Colombier 	new = allocmemimage(Rect(0, 0, xsize, ysize), m->chan);
1419a747e4fSDavid du Colombier 	if(new == nil)
1429a747e4fSDavid du Colombier 		sysfatal("can't allocate new image: %r");
1439a747e4fSDavid du Colombier 
1449a747e4fSDavid du Colombier 	oscan = malloc(Dy(m->r)*sizeof(uchar*));
1459a747e4fSDavid du Colombier 	nscan = malloc(max(ysize, Dy(m->r))*sizeof(uchar*));
1469a747e4fSDavid du Colombier 	if(oscan == nil || nscan == nil)
1479a747e4fSDavid du Colombier 		sysfatal("can't allocate: %r");
1489a747e4fSDavid du Colombier 
1499a747e4fSDavid du Colombier 	/* unload original image into scan lines */
1509a747e4fSDavid du Colombier 	bpl = bytesperline(m->r, m->depth);
1519a747e4fSDavid du Colombier 	for(i=0; i<Dy(m->r); i++){
1529a747e4fSDavid du Colombier 		oscan[i] = malloc(bpl);
1539a747e4fSDavid du Colombier 		if(oscan[i] == nil)
1549a747e4fSDavid du Colombier 			sysfatal("can't allocate: %r");
1559a747e4fSDavid du Colombier 		j = unloadmemimage(m, Rect(m->r.min.x, m->r.min.y+i, m->r.max.x, m->r.min.y+i+1), oscan[i], bpl);
1569a747e4fSDavid du Colombier 		if(j != bpl)
1579a747e4fSDavid du Colombier 			sysfatal("unloadmemimage");
1589a747e4fSDavid du Colombier 	}
1599a747e4fSDavid du Colombier 
1609a747e4fSDavid du Colombier 	/* allocate scan lines for destination. we do y first, so need at least Dy(m->r) lines */
1619a747e4fSDavid du Colombier 	bpl = bytesperline(Rect(0, 0, xsize, Dy(m->r)), m->depth);
1629a747e4fSDavid du Colombier 	for(i=0; i<max(ysize, Dy(m->r)); i++){
1639a747e4fSDavid du Colombier 		nscan[i] = malloc(bpl);
1649a747e4fSDavid du Colombier 		if(nscan[i] == nil)
1659a747e4fSDavid du Colombier 			sysfatal("can't allocate: %r");
1669a747e4fSDavid du Colombier 	}
1679a747e4fSDavid du Colombier 
1689a747e4fSDavid du Colombier 	/* resample in X */
1699a747e4fSDavid du Colombier 	nchan = m->depth/8;
1709a747e4fSDavid du Colombier 	for(i=0; i<Dy(m->r); i++){
1719a747e4fSDavid du Colombier 		for(j=0; j<nchan; j++){
1729a747e4fSDavid du Colombier 			if(j==0 && m->chan==XRGB32)
1739a747e4fSDavid du Colombier 				continue;
1749a747e4fSDavid du Colombier 			resamplex(oscan[i], j, nchan, Dx(m->r), nscan[i], xsize);
1759a747e4fSDavid du Colombier 		}
1769a747e4fSDavid du Colombier 		free(oscan[i]);
1779a747e4fSDavid du Colombier 		oscan[i] = nscan[i];
1789a747e4fSDavid du Colombier 		nscan[i] = malloc(bpl);
1799a747e4fSDavid du Colombier 		if(nscan[i] == nil)
1809a747e4fSDavid du Colombier 			sysfatal("can't allocate: %r");
1819a747e4fSDavid du Colombier 	}
1829a747e4fSDavid du Colombier 
1839a747e4fSDavid du Colombier 	/* resample in Y */
1849a747e4fSDavid du Colombier 	for(i=0; i<xsize; i++)
1859a747e4fSDavid du Colombier 		for(j=0; j<nchan; j++)
1869a747e4fSDavid du Colombier 			resampley(oscan, nchan*i+j, Dy(m->r), nscan, ysize);
1879a747e4fSDavid du Colombier 
1889a747e4fSDavid du Colombier 	/* pack data into destination */
1899a747e4fSDavid du Colombier 	bpl = bytesperline(new->r, m->depth);
1909a747e4fSDavid du Colombier 	for(i=0; i<ysize; i++){
1919a747e4fSDavid du Colombier 		j = loadmemimage(new, Rect(0, i, xsize, i+1), nscan[i], bpl);
1929a747e4fSDavid du Colombier 		if(j != bpl)
1939a747e4fSDavid du Colombier 			sysfatal("loadmemimage: %r");
1949a747e4fSDavid du Colombier 	}
1959a747e4fSDavid du Colombier 	return new;
1969a747e4fSDavid du Colombier }
1979a747e4fSDavid du Colombier 
1989a747e4fSDavid du Colombier void
main(int argc,char * argv[])1999a747e4fSDavid du Colombier main(int argc, char *argv[])
2009a747e4fSDavid du Colombier {
2019a747e4fSDavid du Colombier 	int i, fd, xsize, ysize, xpercent, ypercent;
2029a747e4fSDavid du Colombier 	Rectangle rparam;
2039a747e4fSDavid du Colombier 	Memimage *m, *new, *t1, *t2;
2049a747e4fSDavid du Colombier 	char *file;
2059a747e4fSDavid du Colombier 	ulong tchan;
2069a747e4fSDavid du Colombier 	char tmp[100];
2079a747e4fSDavid du Colombier 	double v;
2089a747e4fSDavid du Colombier 
2099a747e4fSDavid du Colombier 	for(i=-K2; i<=K2; i++){
2109a747e4fSDavid du Colombier 		K[K2+i] = kaiser(i/10., K2/10., 4.);
2119a747e4fSDavid du Colombier //		print("%g %g\n", i/10., K[K2+i]);
2129a747e4fSDavid du Colombier 	}
2139a747e4fSDavid du Colombier 
2149a747e4fSDavid du Colombier 	/* normalize */
2159a747e4fSDavid du Colombier 	v = 0.0;
2169a747e4fSDavid du Colombier 	for(i=0; i<NK; i++)
2179a747e4fSDavid du Colombier 		v += K[i];
2189a747e4fSDavid du Colombier 	for(i=0; i<NK; i++)
2199a747e4fSDavid du Colombier 		K[i] /= v;
2209a747e4fSDavid du Colombier 
2219a747e4fSDavid du Colombier 	memimageinit();
2229a747e4fSDavid du Colombier 	memset(&rparam, 0, sizeof rparam);
2239a747e4fSDavid du Colombier 	xsize = ysize = 0;
2249a747e4fSDavid du Colombier 	xpercent = ypercent = 0;
2259a747e4fSDavid du Colombier 
2269a747e4fSDavid du Colombier 	ARGBEGIN{
2279a747e4fSDavid du Colombier 	case 'a':	/* compatibility; equivalent to just -x or -y */
2289a747e4fSDavid du Colombier 		if(xsize != 0 || ysize != 0)
2299a747e4fSDavid du Colombier 			usage();
2309a747e4fSDavid du Colombier 		xsize = getint(ARGF(), &xpercent);
2319a747e4fSDavid du Colombier 		if(xsize <= 0)
2329a747e4fSDavid du Colombier 			usage();
2339a747e4fSDavid du Colombier 		ysize = xsize;
2349a747e4fSDavid du Colombier 		ypercent = xpercent;
2359a747e4fSDavid du Colombier 		break;
2369a747e4fSDavid du Colombier 	case 'x':
2379a747e4fSDavid du Colombier 		if(xsize != 0)
2389a747e4fSDavid du Colombier 			usage();
2399a747e4fSDavid du Colombier 		xsize = getint(ARGF(), &xpercent);
2409a747e4fSDavid du Colombier 		if(xsize <= 0)
2419a747e4fSDavid du Colombier 			usage();
2429a747e4fSDavid du Colombier 		break;
2439a747e4fSDavid du Colombier 	case 'y':
2449a747e4fSDavid du Colombier 		if(ysize != 0)
2459a747e4fSDavid du Colombier 			usage();
2469a747e4fSDavid du Colombier 		ysize = getint(ARGF(), &ypercent);
2479a747e4fSDavid du Colombier 		if(ysize <= 0)
2489a747e4fSDavid du Colombier 			usage();
2499a747e4fSDavid du Colombier 		break;
2509a747e4fSDavid du Colombier 	default:
2519a747e4fSDavid du Colombier 		usage();
2529a747e4fSDavid du Colombier 	}ARGEND
2539a747e4fSDavid du Colombier 
2549a747e4fSDavid du Colombier 	if(xsize == 0 && ysize == 0)
2559a747e4fSDavid du Colombier 		usage();
2569a747e4fSDavid du Colombier 
2579a747e4fSDavid du Colombier 	file = "<stdin>";
2589a747e4fSDavid du Colombier 	fd = 0;
2599a747e4fSDavid du Colombier 	if(argc > 1)
2609a747e4fSDavid du Colombier 		usage();
2619a747e4fSDavid du Colombier 	else if(argc == 1){
2629a747e4fSDavid du Colombier 		file = argv[0];
2639a747e4fSDavid du Colombier 		fd = open(file, OREAD);
2649a747e4fSDavid du Colombier 		if(fd < 0)
2659a747e4fSDavid du Colombier 			sysfatal("can't open %s: %r", file);
2669a747e4fSDavid du Colombier 	}
2679a747e4fSDavid du Colombier 
2689a747e4fSDavid du Colombier 	m = readmemimage(fd);
2699a747e4fSDavid du Colombier 	if(m == nil)
2709a747e4fSDavid du Colombier 		sysfatal("can't read %s: %r", file);
2719a747e4fSDavid du Colombier 
2729a747e4fSDavid du Colombier 	if(xpercent)
2739a747e4fSDavid du Colombier 		xsize = Dx(m->r)*xsize/100;
2749a747e4fSDavid du Colombier 	if(ypercent)
2759a747e4fSDavid du Colombier 		ysize = Dy(m->r)*ysize/100;
2769a747e4fSDavid du Colombier 	if(ysize == 0)
2779a747e4fSDavid du Colombier 		ysize = (xsize * Dy(m->r)) / Dx(m->r);
2789a747e4fSDavid du Colombier 	if(xsize == 0)
2799a747e4fSDavid du Colombier 		xsize = (ysize * Dx(m->r)) / Dy(m->r);
2809a747e4fSDavid du Colombier 
2819a747e4fSDavid du Colombier 	new = nil;
2829a747e4fSDavid du Colombier 	switch(m->chan){
2839a747e4fSDavid du Colombier 
2849a747e4fSDavid du Colombier 	case GREY8:
2859a747e4fSDavid du Colombier 	case RGB24:
2869a747e4fSDavid du Colombier 	case RGBA32:
2879a747e4fSDavid du Colombier 	case ARGB32:
2889a747e4fSDavid du Colombier 	case XRGB32:
2899a747e4fSDavid du Colombier 		new = resample(xsize, ysize, m);
2909a747e4fSDavid du Colombier 		break;
2919a747e4fSDavid du Colombier 
2929a747e4fSDavid du Colombier 	case CMAP8:
2939a747e4fSDavid du Colombier 	case RGB15:
2949a747e4fSDavid du Colombier 	case RGB16:
2959a747e4fSDavid du Colombier 		tchan = RGB24;
2969a747e4fSDavid du Colombier 		goto Convert;
2979a747e4fSDavid du Colombier 
2989a747e4fSDavid du Colombier 	case GREY1:
2999a747e4fSDavid du Colombier 	case GREY2:
3009a747e4fSDavid du Colombier 	case GREY4:
3019a747e4fSDavid du Colombier 		tchan = GREY8;
3029a747e4fSDavid du Colombier 	Convert:
3039a747e4fSDavid du Colombier 		/* use library to convert to byte-per-chan form, then convert back */
3049a747e4fSDavid du Colombier 		t1 = allocmemimage(m->r, tchan);
3059a747e4fSDavid du Colombier 		if(t1 == nil)
3069a747e4fSDavid du Colombier 			sysfatal("can't allocate temporary image: %r");
307*6a9fc400SDavid du Colombier 		memimagedraw(t1, t1->r, m, m->r.min, nil, ZP, S);
3089a747e4fSDavid du Colombier 		t2 = resample(xsize, ysize, t1);
3099a747e4fSDavid du Colombier 		freememimage(t1);
3109a747e4fSDavid du Colombier 		new = allocmemimage(Rect(0, 0, xsize, ysize), m->chan);
3119a747e4fSDavid du Colombier 		if(new == nil)
3129a747e4fSDavid du Colombier 			sysfatal("can't allocate new image: %r");
3139a747e4fSDavid du Colombier 		/* should do error diffusion here */
314*6a9fc400SDavid du Colombier 		memimagedraw(new, new->r, t2, t2->r.min, nil, ZP, S);
3159a747e4fSDavid du Colombier 		freememimage(t2);
3169a747e4fSDavid du Colombier 		break;
3179a747e4fSDavid du Colombier 
3189a747e4fSDavid du Colombier 	default:
3199a747e4fSDavid du Colombier 		sysfatal("can't handle channel type %s", chantostr(tmp, m->chan));
3209a747e4fSDavid du Colombier 	}
3219a747e4fSDavid du Colombier 
3229a747e4fSDavid du Colombier 	assert(new);
3239a747e4fSDavid du Colombier 	if(writememimage(1, new) < 0)
3249a747e4fSDavid du Colombier 		sysfatal("write error on output: %r");
3259a747e4fSDavid du Colombier 
3269a747e4fSDavid du Colombier 	exits(nil);
3279a747e4fSDavid du Colombier }
328