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