xref: /plan9/sys/src/cmd/page/rotate.c (revision 5316891f166e5e3ab41efa6be642f9ef412c5323)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * rotate an image 180° in O(log Dx + log Dy) /dev/draw writes,
37dd7cddfSDavid du Colombier  * using an extra buffer same size as the image.
47dd7cddfSDavid du Colombier  *
57dd7cddfSDavid du Colombier  * the basic concept is that you can invert an array by inverting
67dd7cddfSDavid du Colombier  * the top half, inverting the bottom half, and then swapping them.
77dd7cddfSDavid du Colombier  * the code does this slightly backwards to ensure O(log n) runtime.
87dd7cddfSDavid du Colombier  * (If you do it wrong, you can get O(log² n) runtime.)
97dd7cddfSDavid du Colombier  *
107dd7cddfSDavid du Colombier  * This is usually overkill, but it speeds up slow remote
117dd7cddfSDavid du Colombier  * connections quite a bit.
127dd7cddfSDavid du Colombier  */
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier #include <u.h>
157dd7cddfSDavid du Colombier #include <libc.h>
167dd7cddfSDavid du Colombier #include <bio.h>
177dd7cddfSDavid du Colombier #include <draw.h>
187dd7cddfSDavid du Colombier #include <event.h>
197dd7cddfSDavid du Colombier #include "page.h"
207dd7cddfSDavid du Colombier 
217dd7cddfSDavid du Colombier int ndraw = 0;
227dd7cddfSDavid du Colombier enum {
237dd7cddfSDavid du Colombier 	Xaxis = 0,
247dd7cddfSDavid du Colombier 	Yaxis = 1,
257dd7cddfSDavid du Colombier };
267dd7cddfSDavid du Colombier 
277dd7cddfSDavid du Colombier Image *mtmp;
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier void
307dd7cddfSDavid du Colombier writefile(char *name, Image *im, int gran)
317dd7cddfSDavid du Colombier {
327dd7cddfSDavid du Colombier 	static int c = 100;
337dd7cddfSDavid du Colombier 	int fd;
347dd7cddfSDavid du Colombier 	char buf[200];
357dd7cddfSDavid du Colombier 
367dd7cddfSDavid du Colombier 	snprint(buf, sizeof buf, "%d%s%d", c++, name, gran);
377dd7cddfSDavid du Colombier 	fd = create(buf, OWRITE, 0666);
387dd7cddfSDavid du Colombier 	if(fd < 0)
397dd7cddfSDavid du Colombier 		return;
407dd7cddfSDavid du Colombier 	writeimage(fd, im, 0);
417dd7cddfSDavid du Colombier 	close(fd);
427dd7cddfSDavid du Colombier }
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier void
457dd7cddfSDavid du Colombier moveup(Image *im, Image *tmp, int a, int b, int c, int axis)
467dd7cddfSDavid du Colombier {
477dd7cddfSDavid du Colombier 	Rectangle range;
487dd7cddfSDavid du Colombier 	Rectangle dr0, dr1;
497dd7cddfSDavid du Colombier 	Point p0, p1;
507dd7cddfSDavid du Colombier 
517dd7cddfSDavid du Colombier 	if(a == b || b == c)
527dd7cddfSDavid du Colombier 		return;
537dd7cddfSDavid du Colombier 
546b6b9ac8SDavid du Colombier 	drawop(tmp, tmp->r, im, nil, im->r.min, S);
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier 	switch(axis){
577dd7cddfSDavid du Colombier 	case Xaxis:
587dd7cddfSDavid du Colombier 		range = Rect(a, im->r.min.y,  c, im->r.max.y);
597dd7cddfSDavid du Colombier 		dr0 = range;
607dd7cddfSDavid du Colombier 		dr0.max.x = dr0.min.x+(c-b);
617dd7cddfSDavid du Colombier 		p0 = Pt(b, im->r.min.y);
627dd7cddfSDavid du Colombier 
637dd7cddfSDavid du Colombier 		dr1 = range;
647dd7cddfSDavid du Colombier 		dr1.min.x = dr1.max.x-(b-a);
657dd7cddfSDavid du Colombier 		p1 = Pt(a, im->r.min.y);
667dd7cddfSDavid du Colombier 		break;
677dd7cddfSDavid du Colombier 	case Yaxis:
687dd7cddfSDavid du Colombier 		range = Rect(im->r.min.x, a,  im->r.max.x, c);
697dd7cddfSDavid du Colombier 		dr0 = range;
707dd7cddfSDavid du Colombier 		dr0.max.y = dr0.min.y+(c-b);
717dd7cddfSDavid du Colombier 		p0 = Pt(im->r.min.x, b);
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier 		dr1 = range;
747dd7cddfSDavid du Colombier 		dr1.min.y = dr1.max.y-(b-a);
757dd7cddfSDavid du Colombier 		p1 = Pt(im->r.min.x, a);
767dd7cddfSDavid du Colombier 		break;
777dd7cddfSDavid du Colombier 	}
786b6b9ac8SDavid du Colombier 	drawop(im, dr0, tmp, nil, p0, S);
796b6b9ac8SDavid du Colombier 	drawop(im, dr1, tmp, nil, p1, S);
807dd7cddfSDavid du Colombier }
817dd7cddfSDavid du Colombier 
827dd7cddfSDavid du Colombier void
837dd7cddfSDavid du Colombier interlace(Image *im, Image *tmp, int axis, int n, Image *mask, int gran)
847dd7cddfSDavid du Colombier {
857dd7cddfSDavid du Colombier 	Point p0, p1;
867dd7cddfSDavid du Colombier 	Rectangle r0, r1;
877dd7cddfSDavid du Colombier 
887dd7cddfSDavid du Colombier 	r0 = im->r;
897dd7cddfSDavid du Colombier 	r1 = im->r;
907dd7cddfSDavid du Colombier 	switch(axis) {
917dd7cddfSDavid du Colombier 	case Xaxis:
927dd7cddfSDavid du Colombier 		r0.max.x = n;
937dd7cddfSDavid du Colombier 		r1.min.x = n;
947dd7cddfSDavid du Colombier 		p0 = (Point){gran, 0};
957dd7cddfSDavid du Colombier 		p1 = (Point){-gran, 0};
967dd7cddfSDavid du Colombier 		break;
977dd7cddfSDavid du Colombier 	case Yaxis:
987dd7cddfSDavid du Colombier 		r0.max.y = n;
997dd7cddfSDavid du Colombier 		r1.min.y = n;
1007dd7cddfSDavid du Colombier 		p0 = (Point){0, gran};
1017dd7cddfSDavid du Colombier 		p1 = (Point){0, -gran};
1027dd7cddfSDavid du Colombier 		break;
1037dd7cddfSDavid du Colombier 	}
1047dd7cddfSDavid du Colombier 
1056b6b9ac8SDavid du Colombier 	drawop(tmp, im->r, im, display->opaque, im->r.min, S);
1066b6b9ac8SDavid du Colombier 	gendrawop(im, r0, tmp, p0, mask, mask->r.min, S);
1076b6b9ac8SDavid du Colombier 	gendrawop(im, r0, tmp, p1, mask, p1, S);
1087dd7cddfSDavid du Colombier }
1097dd7cddfSDavid du Colombier 
1107dd7cddfSDavid du Colombier /*
1117dd7cddfSDavid du Colombier  * Halve the grating period in the mask.
1127dd7cddfSDavid du Colombier  * The grating currently looks like
1137dd7cddfSDavid du Colombier  * ####____####____####____####____
1147dd7cddfSDavid du Colombier  * where #### is opacity.
1157dd7cddfSDavid du Colombier  *
1167dd7cddfSDavid du Colombier  * We want
1177dd7cddfSDavid du Colombier  * ##__##__##__##__##__##__##__##__
1187dd7cddfSDavid du Colombier  * which is achieved by shifting the mask
1197dd7cddfSDavid du Colombier  * and drawing on itself through itself.
1207dd7cddfSDavid du Colombier  * Draw doesn't actually allow this, so
1217dd7cddfSDavid du Colombier  * we have to copy it first.
1227dd7cddfSDavid du Colombier  *
1237dd7cddfSDavid du Colombier  *     ####____####____####____####____ (dst)
1247dd7cddfSDavid du Colombier  * +   ____####____####____####____#### (src)
1257dd7cddfSDavid du Colombier  * in  __####____####____####____####__ (mask)
1267dd7cddfSDavid du Colombier  * ===========================================
1277dd7cddfSDavid du Colombier  *     ##__##__##__##__##__##__##__##__
1287dd7cddfSDavid du Colombier  */
1297dd7cddfSDavid du Colombier int
1307dd7cddfSDavid du Colombier nextmask(Image *mask, int axis, int maskdim)
1317dd7cddfSDavid du Colombier {
1327dd7cddfSDavid du Colombier 	Point δ;
1337dd7cddfSDavid du Colombier 
1347dd7cddfSDavid du Colombier 	δ = axis==Xaxis ? Pt(maskdim,0) : Pt(0,maskdim);
1356b6b9ac8SDavid du Colombier 	drawop(mtmp, mtmp->r, mask, nil, mask->r.min, S);
1366b6b9ac8SDavid du Colombier 	gendrawop(mask, mask->r, mtmp, δ, mtmp, divpt(δ,-2), S);
1377dd7cddfSDavid du Colombier //	writefile("mask", mask, maskdim/2);
1387dd7cddfSDavid du Colombier 	return maskdim/2;
1397dd7cddfSDavid du Colombier }
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier void
1427dd7cddfSDavid du Colombier shuffle(Image *im, Image *tmp, int axis, int n, Image *mask, int gran,
1437dd7cddfSDavid du Colombier 	int lastnn)
1447dd7cddfSDavid du Colombier {
1457dd7cddfSDavid du Colombier 	int nn, left;
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier 	if(gran == 0)
1487dd7cddfSDavid du Colombier 		return;
1497dd7cddfSDavid du Colombier 	left = n%(2*gran);
1507dd7cddfSDavid du Colombier 	nn = n - left;
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 	interlace(im, tmp, axis, nn, mask, gran);
1537dd7cddfSDavid du Colombier //	writefile("interlace", im, gran);
1547dd7cddfSDavid du Colombier 
1557dd7cddfSDavid du Colombier 	gran = nextmask(mask, axis, gran);
1567dd7cddfSDavid du Colombier 	shuffle(im, tmp, axis, n, mask, gran, nn);
1577dd7cddfSDavid du Colombier //	writefile("shuffle", im, gran);
1587dd7cddfSDavid du Colombier 	moveup(im, tmp, lastnn, nn, n, axis);
1597dd7cddfSDavid du Colombier //	writefile("move", im, gran);
1607dd7cddfSDavid du Colombier }
1617dd7cddfSDavid du Colombier 
1627dd7cddfSDavid du Colombier void
1637dd7cddfSDavid du Colombier rot180(Image *im)
1647dd7cddfSDavid du Colombier {
1655d459b5aSDavid du Colombier 	Image *tmp, *tmp0;
1667dd7cddfSDavid du Colombier 	Image *mask;
1677dd7cddfSDavid du Colombier 	Rectangle rmask;
1687dd7cddfSDavid du Colombier 	int gran;
1697dd7cddfSDavid du Colombier 
1705d459b5aSDavid du Colombier 	if(chantodepth(im->chan) < 8){
1715d459b5aSDavid du Colombier 		/* this speeds things up dramatically; draw is too slow on sub-byte pixel sizes */
1725d459b5aSDavid du Colombier 		tmp0 = xallocimage(display, im->r, CMAP8, 0, DNofill);
1736b6b9ac8SDavid du Colombier 		drawop(tmp0, tmp0->r, im, nil, im->r.min, S);
1745d459b5aSDavid du Colombier 	}else
1755d459b5aSDavid du Colombier 		tmp0 = im;
1767dd7cddfSDavid du Colombier 
1775d459b5aSDavid du Colombier 	tmp = xallocimage(display, tmp0->r, tmp0->chan, 0, DNofill);
1785d459b5aSDavid du Colombier 	if(tmp == nil){
1795d459b5aSDavid du Colombier 		if(tmp0 != im)
1805d459b5aSDavid du Colombier 			freeimage(tmp0);
1815d459b5aSDavid du Colombier 		return;
1825d459b5aSDavid du Colombier 	}
1837dd7cddfSDavid du Colombier 	for(gran=1; gran<Dx(im->r); gran *= 2)
1847dd7cddfSDavid du Colombier 		;
1857dd7cddfSDavid du Colombier 	gran /= 4;
1867dd7cddfSDavid du Colombier 
1877dd7cddfSDavid du Colombier 	rmask.min = ZP;
1887dd7cddfSDavid du Colombier 	rmask.max = (Point){2*gran, 100};
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier 	mask = xallocimage(display, rmask, GREY1, 1, DTransparent);
1917dd7cddfSDavid du Colombier 	mtmp = xallocimage(display, rmask, GREY1, 1, DTransparent);
192*5316891fSDavid du Colombier 	if(mask == nil || mtmp == nil) {
193*5316891fSDavid du Colombier 		fprint(2, "out of memory during rot180: %r\n");
194*5316891fSDavid du Colombier 		wexits("memory");
195*5316891fSDavid du Colombier 	}
1967dd7cddfSDavid du Colombier 	rmask.max.x = gran;
1976b6b9ac8SDavid du Colombier 	drawop(mask, rmask, display->opaque, nil, ZP, S);
1987dd7cddfSDavid du Colombier //	writefile("mask", mask, gran);
1997dd7cddfSDavid du Colombier 	shuffle(im, tmp, Xaxis, Dx(im->r), mask, gran, 0);
2007dd7cddfSDavid du Colombier 	freeimage(mask);
2017dd7cddfSDavid du Colombier 	freeimage(mtmp);
2027dd7cddfSDavid du Colombier 
2037dd7cddfSDavid du Colombier 	for(gran=1; gran<Dy(im->r); gran *= 2)
2047dd7cddfSDavid du Colombier 		;
2057dd7cddfSDavid du Colombier 	gran /= 4;
2067dd7cddfSDavid du Colombier 	rmask.max = (Point){100, 2*gran};
2077dd7cddfSDavid du Colombier 	mask = xallocimage(display, rmask, GREY1, 1, DTransparent);
2087dd7cddfSDavid du Colombier 	mtmp = xallocimage(display, rmask, GREY1, 1, DTransparent);
209*5316891fSDavid du Colombier 	if(mask == nil || mtmp == nil) {
210*5316891fSDavid du Colombier 		fprint(2, "out of memory during rot180: %r\n");
211*5316891fSDavid du Colombier 		wexits("memory");
212*5316891fSDavid du Colombier 	}
2137dd7cddfSDavid du Colombier 	rmask.max.y = gran;
2146b6b9ac8SDavid du Colombier 	drawop(mask, rmask, display->opaque, nil, ZP, S);
2157dd7cddfSDavid du Colombier 	shuffle(im, tmp, Yaxis, Dy(im->r), mask, gran, 0);
2167dd7cddfSDavid du Colombier 	freeimage(mask);
2177dd7cddfSDavid du Colombier 	freeimage(mtmp);
2187dd7cddfSDavid du Colombier 	freeimage(tmp);
2195d459b5aSDavid du Colombier 	if(tmp0 != im)
2205d459b5aSDavid du Colombier 		freeimage(tmp0);
2217dd7cddfSDavid du Colombier }
22208fd2d13SDavid du Colombier 
22308fd2d13SDavid du Colombier /* rotates an image 90 degrees clockwise */
22408fd2d13SDavid du Colombier Image *
22508fd2d13SDavid du Colombier rot90(Image *im)
22608fd2d13SDavid du Colombier {
22708fd2d13SDavid du Colombier 	Image *tmp;
22808fd2d13SDavid du Colombier 	int i, j, dx, dy;
22908fd2d13SDavid du Colombier 
23008fd2d13SDavid du Colombier 	dx = Dx(im->r);
23108fd2d13SDavid du Colombier 	dy = Dy(im->r);
23208fd2d13SDavid du Colombier 	tmp = xallocimage(display, Rect(0, 0, dy, dx), im->chan, 0, DCyan);
233*5316891fSDavid du Colombier 	if(tmp == nil) {
234*5316891fSDavid du Colombier 		fprint(2, "out of memory during rot90: %r\n");
235*5316891fSDavid du Colombier 		wexits("memory");
236*5316891fSDavid du Colombier 	}
23708fd2d13SDavid du Colombier 
23808fd2d13SDavid du Colombier 	for(j = 0; j < dx; j++) {
23908fd2d13SDavid du Colombier 		for(i = 0; i < dy; i++) {
240*5316891fSDavid du Colombier 			drawop(tmp, Rect(i, j, i+1, j+1), im, nil, Pt(j, dy-(i+1)), S);
24108fd2d13SDavid du Colombier 		}
24208fd2d13SDavid du Colombier 	}
24308fd2d13SDavid du Colombier 	freeimage(im);
24408fd2d13SDavid du Colombier 
24508fd2d13SDavid du Colombier 	return(tmp);
24608fd2d13SDavid du Colombier }
24708fd2d13SDavid du Colombier 
24808fd2d13SDavid du Colombier /* from resample.c -- resize from → to using interpolation */
24908fd2d13SDavid du Colombier 
25008fd2d13SDavid du Colombier 
25108fd2d13SDavid du Colombier #define K2 7	/* from -.7 to +.7 inclusive, meaning .2 into each adjacent pixel */
25208fd2d13SDavid du Colombier #define NK (2*K2+1)
25308fd2d13SDavid du Colombier double K[NK];
25408fd2d13SDavid du Colombier 
25508fd2d13SDavid du Colombier double
25608fd2d13SDavid du Colombier fac(int L)
25708fd2d13SDavid du Colombier {
25808fd2d13SDavid du Colombier 	int i, f;
25908fd2d13SDavid du Colombier 
26008fd2d13SDavid du Colombier 	f = 1;
26108fd2d13SDavid du Colombier 	for(i=L; i>1; --i)
26208fd2d13SDavid du Colombier 		f *= i;
26308fd2d13SDavid du Colombier 	return f;
26408fd2d13SDavid du Colombier }
26508fd2d13SDavid du Colombier 
26608fd2d13SDavid du Colombier /*
26708fd2d13SDavid du Colombier  * i0(x) is the modified Bessel function, Σ (x/2)^2L / (L!)²
26808fd2d13SDavid du Colombier  * There are faster ways to calculate this, but we precompute
26908fd2d13SDavid du Colombier  * into a table so let's keep it simple.
27008fd2d13SDavid du Colombier  */
27108fd2d13SDavid du Colombier double
27208fd2d13SDavid du Colombier i0(double x)
27308fd2d13SDavid du Colombier {
27408fd2d13SDavid du Colombier 	double v;
27508fd2d13SDavid du Colombier 	int L;
27608fd2d13SDavid du Colombier 
27708fd2d13SDavid du Colombier 	v = 1.0;
27808fd2d13SDavid du Colombier 	for(L=1; L<10; L++)
27908fd2d13SDavid du Colombier 		v += pow(x/2., 2*L)/pow(fac(L), 2);
28008fd2d13SDavid du Colombier 	return v;
28108fd2d13SDavid du Colombier }
28208fd2d13SDavid du Colombier 
28308fd2d13SDavid du Colombier double
28408fd2d13SDavid du Colombier kaiser(double x, double τ, double α)
28508fd2d13SDavid du Colombier {
28608fd2d13SDavid du Colombier 	if(fabs(x) > τ)
28708fd2d13SDavid du Colombier 		return 0.;
28808fd2d13SDavid du Colombier 	return i0(α*sqrt(1-(x*x/(τ*τ))))/i0(α);
28908fd2d13SDavid du Colombier }
29008fd2d13SDavid du Colombier 
29108fd2d13SDavid du Colombier 
29208fd2d13SDavid du Colombier void
29308fd2d13SDavid du Colombier resamplex(uchar *in, int off, int d, int inx, uchar *out, int outx)
29408fd2d13SDavid du Colombier {
29508fd2d13SDavid du Colombier 	int i, x, k;
29608fd2d13SDavid du Colombier 	double X, xx, v, rat;
29708fd2d13SDavid du Colombier 
29808fd2d13SDavid du Colombier 
29908fd2d13SDavid du Colombier 	rat = (double)inx/(double)outx;
30008fd2d13SDavid du Colombier 	for(x=0; x<outx; x++){
30108fd2d13SDavid du Colombier 		if(inx == outx){
30208fd2d13SDavid du Colombier 			/* don't resample if size unchanged */
30308fd2d13SDavid du Colombier 			out[off+x*d] = in[off+x*d];
30408fd2d13SDavid du Colombier 			continue;
30508fd2d13SDavid du Colombier 		}
30608fd2d13SDavid du Colombier 		v = 0.0;
30708fd2d13SDavid du Colombier 		X = x*rat;
30808fd2d13SDavid du Colombier 		for(k=-K2; k<=K2; k++){
30908fd2d13SDavid du Colombier 			xx = X + rat*k/10.;
31008fd2d13SDavid du Colombier 			i = xx;
31108fd2d13SDavid du Colombier 			if(i < 0)
31208fd2d13SDavid du Colombier 				i = 0;
31308fd2d13SDavid du Colombier 			if(i >= inx)
31408fd2d13SDavid du Colombier 				i = inx-1;
31508fd2d13SDavid du Colombier 			v += in[off+i*d] * K[K2+k];
31608fd2d13SDavid du Colombier 		}
31708fd2d13SDavid du Colombier 		out[off+x*d] = v;
31808fd2d13SDavid du Colombier 	}
31908fd2d13SDavid du Colombier }
32008fd2d13SDavid du Colombier 
32108fd2d13SDavid du Colombier void
32208fd2d13SDavid du Colombier resampley(uchar **in, int off, int iny, uchar **out, int outy)
32308fd2d13SDavid du Colombier {
32408fd2d13SDavid du Colombier 	int y, i, k;
32508fd2d13SDavid du Colombier 	double Y, yy, v, rat;
32608fd2d13SDavid du Colombier 
32708fd2d13SDavid du Colombier 	rat = (double)iny/(double)outy;
32808fd2d13SDavid du Colombier 	for(y=0; y<outy; y++){
32908fd2d13SDavid du Colombier 		if(iny == outy){
33008fd2d13SDavid du Colombier 			/* don't resample if size unchanged */
33108fd2d13SDavid du Colombier 			out[y][off] = in[y][off];
33208fd2d13SDavid du Colombier 			continue;
33308fd2d13SDavid du Colombier 		}
33408fd2d13SDavid du Colombier 		v = 0.0;
33508fd2d13SDavid du Colombier 		Y = y*rat;
33608fd2d13SDavid du Colombier 		for(k=-K2; k<=K2; k++){
33708fd2d13SDavid du Colombier 			yy = Y + rat*k/10.;
33808fd2d13SDavid du Colombier 			i = yy;
33908fd2d13SDavid du Colombier 			if(i < 0)
34008fd2d13SDavid du Colombier 				i = 0;
34108fd2d13SDavid du Colombier 			if(i >= iny)
34208fd2d13SDavid du Colombier 				i = iny-1;
34308fd2d13SDavid du Colombier 			v += in[i][off] * K[K2+k];
34408fd2d13SDavid du Colombier 		}
34508fd2d13SDavid du Colombier 		out[y][off] = v;
34608fd2d13SDavid du Colombier 	}
34708fd2d13SDavid du Colombier 
34808fd2d13SDavid du Colombier }
34908fd2d13SDavid du Colombier 
35008fd2d13SDavid du Colombier Image*
35108fd2d13SDavid du Colombier resample(Image *from, Image *to)
35208fd2d13SDavid du Colombier {
35308fd2d13SDavid du Colombier 	int i, j, bpl, nchan;
35408fd2d13SDavid du Colombier 	uchar **oscan, **nscan;
35508fd2d13SDavid du Colombier 	char tmp[20];
35608fd2d13SDavid du Colombier 	int xsize, ysize;
35708fd2d13SDavid du Colombier 	double v;
35808fd2d13SDavid du Colombier 	Image *t1, *t2;
35908fd2d13SDavid du Colombier 	ulong tchan;
36008fd2d13SDavid du Colombier 
36108fd2d13SDavid du Colombier 	for(i=-K2; i<=K2; i++){
36208fd2d13SDavid du Colombier 		K[K2+i] = kaiser(i/10., K2/10., 4.);
36308fd2d13SDavid du Colombier 	}
36408fd2d13SDavid du Colombier 
36508fd2d13SDavid du Colombier 	/* normalize */
36608fd2d13SDavid du Colombier 	v = 0.0;
36708fd2d13SDavid du Colombier 	for(i=0; i<NK; i++)
36808fd2d13SDavid du Colombier 		v += K[i];
36908fd2d13SDavid du Colombier 	for(i=0; i<NK; i++)
37008fd2d13SDavid du Colombier 		K[i] /= v;
37108fd2d13SDavid du Colombier 
37208fd2d13SDavid du Colombier 	switch(from->chan){
37308fd2d13SDavid du Colombier 	case GREY8:
37408fd2d13SDavid du Colombier 	case RGB24:
37508fd2d13SDavid du Colombier 	case RGBA32:
37608fd2d13SDavid du Colombier 	case ARGB32:
37708fd2d13SDavid du Colombier 	case XRGB32:
37808fd2d13SDavid du Colombier 		break;
37908fd2d13SDavid du Colombier 
38008fd2d13SDavid du Colombier 	case CMAP8:
38108fd2d13SDavid du Colombier 	case RGB15:
38208fd2d13SDavid du Colombier 	case RGB16:
38308fd2d13SDavid du Colombier 		tchan = RGB24;
38408fd2d13SDavid du Colombier 		goto Convert;
38508fd2d13SDavid du Colombier 
38608fd2d13SDavid du Colombier 	case GREY1:
38708fd2d13SDavid du Colombier 	case GREY2:
38808fd2d13SDavid du Colombier 	case GREY4:
38908fd2d13SDavid du Colombier 		tchan = GREY8;
39008fd2d13SDavid du Colombier 	Convert:
39108fd2d13SDavid du Colombier 		/* use library to convert to byte-per-chan form, then convert back */
392*5316891fSDavid du Colombier 		t1 = xallocimage(display, Rect(0, 0, Dx(from->r), Dy(from->r)), tchan, 0, DNofill);
393*5316891fSDavid du Colombier 		if(t1 == nil) {
394*5316891fSDavid du Colombier 			fprint(2, "out of memory for temp image 1 in resample: %r\n");
395*5316891fSDavid du Colombier 			wexits("memory");
396*5316891fSDavid du Colombier 		}
397*5316891fSDavid du Colombier 		drawop(t1, t1->r, from, nil, ZP, S);
398*5316891fSDavid du Colombier 		t2 = xallocimage(display, to->r, tchan, 0, DNofill);
399*5316891fSDavid du Colombier 		if(t2 == nil) {
400*5316891fSDavid du Colombier 			fprint(2, "out of memory temp image 2 in resample: %r\n");
401*5316891fSDavid du Colombier 			wexits("memory");
402*5316891fSDavid du Colombier 		}
40308fd2d13SDavid du Colombier 		resample(t1, t2);
404*5316891fSDavid du Colombier 		drawop(to, to->r, t2, nil, ZP, S);
40508fd2d13SDavid du Colombier 		freeimage(t1);
40608fd2d13SDavid du Colombier 		freeimage(t2);
40708fd2d13SDavid du Colombier 		return to;
40808fd2d13SDavid du Colombier 
40908fd2d13SDavid du Colombier 	default:
41008fd2d13SDavid du Colombier 		sysfatal("can't handle channel type %s", chantostr(tmp, from->chan));
41108fd2d13SDavid du Colombier 	}
41208fd2d13SDavid du Colombier 
41308fd2d13SDavid du Colombier 	xsize = Dx(to->r);
41408fd2d13SDavid du Colombier 	ysize = Dy(to->r);
41508fd2d13SDavid du Colombier 	oscan = malloc(Dy(from->r)*sizeof(uchar*));
41608fd2d13SDavid du Colombier 	nscan = malloc(max(ysize, Dy(from->r))*sizeof(uchar*));
41708fd2d13SDavid du Colombier 	if(oscan == nil || nscan == nil)
41808fd2d13SDavid du Colombier 		sysfatal("can't allocate: %r");
41908fd2d13SDavid du Colombier 
42008fd2d13SDavid du Colombier 	/* unload original image into scan lines */
42108fd2d13SDavid du Colombier 	bpl = bytesperline(from->r, from->depth);
42208fd2d13SDavid du Colombier 	for(i=0; i<Dy(from->r); i++){
42308fd2d13SDavid du Colombier 		oscan[i] = malloc(bpl);
42408fd2d13SDavid du Colombier 		if(oscan[i] == nil)
42508fd2d13SDavid du Colombier 			sysfatal("can't allocate: %r");
42608fd2d13SDavid du Colombier 		j = unloadimage(from, Rect(from->r.min.x, from->r.min.y+i, from->r.max.x, from->r.min.y+i+1), oscan[i], bpl);
42708fd2d13SDavid du Colombier 		if(j != bpl)
42808fd2d13SDavid du Colombier 			sysfatal("unloadimage");
42908fd2d13SDavid du Colombier 	}
43008fd2d13SDavid du Colombier 
43108fd2d13SDavid du Colombier 	/* allocate scan lines for destination. we do y first, so need at least Dy(from->r) lines */
43208fd2d13SDavid du Colombier 	bpl = bytesperline(Rect(0, 0, xsize, Dy(from->r)), from->depth);
43308fd2d13SDavid du Colombier 	for(i=0; i<max(ysize, Dy(from->r)); i++){
43408fd2d13SDavid du Colombier 		nscan[i] = malloc(bpl);
43508fd2d13SDavid du Colombier 		if(nscan[i] == nil)
43608fd2d13SDavid du Colombier 			sysfatal("can't allocate: %r");
43708fd2d13SDavid du Colombier 	}
43808fd2d13SDavid du Colombier 
43908fd2d13SDavid du Colombier 	/* resample in X */
44008fd2d13SDavid du Colombier 	nchan = from->depth/8;
44108fd2d13SDavid du Colombier 	for(i=0; i<Dy(from->r); i++){
44208fd2d13SDavid du Colombier 		for(j=0; j<nchan; j++){
44308fd2d13SDavid du Colombier 			if(j==0 && from->chan==XRGB32)
44408fd2d13SDavid du Colombier 				continue;
44508fd2d13SDavid du Colombier 			resamplex(oscan[i], j, nchan, Dx(from->r), nscan[i], xsize);
44608fd2d13SDavid du Colombier 		}
44708fd2d13SDavid du Colombier 		free(oscan[i]);
44808fd2d13SDavid du Colombier 		oscan[i] = nscan[i];
44908fd2d13SDavid du Colombier 		nscan[i] = malloc(bpl);
45008fd2d13SDavid du Colombier 		if(nscan[i] == nil)
45108fd2d13SDavid du Colombier 			sysfatal("can't allocate: %r");
45208fd2d13SDavid du Colombier 	}
45308fd2d13SDavid du Colombier 
45408fd2d13SDavid du Colombier 	/* resample in Y */
45508fd2d13SDavid du Colombier 	for(i=0; i<xsize; i++)
45608fd2d13SDavid du Colombier 		for(j=0; j<nchan; j++)
45708fd2d13SDavid du Colombier 			resampley(oscan, nchan*i+j, Dy(from->r), nscan, ysize);
45808fd2d13SDavid du Colombier 
45908fd2d13SDavid du Colombier 	/* pack data into destination */
46008fd2d13SDavid du Colombier 	bpl = bytesperline(to->r, from->depth);
46108fd2d13SDavid du Colombier 	for(i=0; i<ysize; i++){
46208fd2d13SDavid du Colombier 		j = loadimage(to, Rect(0, i, xsize, i+1), nscan[i], bpl);
46308fd2d13SDavid du Colombier 		if(j != bpl)
46408fd2d13SDavid du Colombier 			sysfatal("loadimage: %r");
46508fd2d13SDavid du Colombier 	}
46608fd2d13SDavid du Colombier 
46708fd2d13SDavid du Colombier 	for(i=0; i<Dy(from->r); i++){
46808fd2d13SDavid du Colombier 		free(oscan[i]);
46908fd2d13SDavid du Colombier 		free(nscan[i]);
47008fd2d13SDavid du Colombier 	}
47108fd2d13SDavid du Colombier 	free(oscan);
47208fd2d13SDavid du Colombier 	free(nscan);
47308fd2d13SDavid du Colombier 
47408fd2d13SDavid du Colombier 	return to;
47508fd2d13SDavid du Colombier }
476