123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #include <u.h>
- #include <libc.h>
- #include <draw.h>
- #include <memdraw.h>
- #define K2 7 /* from -.7 to +.7 inclusive, meaning .2 into each adjacent pixel */
- #define NK (2*K2+1)
- double K[NK];
- double
- fac(int L)
- {
- int i, f;
- f = 1;
- for(i=L; i>1; --i)
- f *= i;
- return f;
- }
- /*
- * i0(x) is the modified Bessel function, Σ (x/2)^2L / (L!)²
- * There are faster ways to calculate this, but we precompute
- * into a table so let's keep it simple.
- */
- double
- i0(double x)
- {
- double v;
- int L;
- v = 1.0;
- for(L=1; L<10; L++)
- v += pow(x/2., 2*L)/pow(fac(L), 2);
- return v;
- }
- double
- kaiser(double x, double tau, double alpha)
- {
- if(fabs(x) > tau)
- return 0.;
- return i0(alpha*sqrt(1-(x*x/(tau*tau))))/i0(alpha);
- }
- void
- usage(void)
- {
- fprint(2, "usage: resample [-x xsize] [-y ysize] [imagefile]\n");
- fprint(2, "\twhere size is an integer or a percentage in the form 25%%\n");
- exits("usage");
- }
- int
- getint(char *s, int *percent)
- {
- if(s == nil)
- usage();
- *percent = (s[strlen(s)-1] == '%');
- if(*s == '+')
- return atoi(s+1);
- if(*s == '-')
- return -atoi(s+1);
- return atoi(s);
- }
- void
- resamplex(uint8_t *in, int off, int d, int inx, uint8_t *out, int outx)
- {
- int i, x, k;
- double X, xx, v, rat;
- rat = (double)inx/(double)outx;
- for(x=0; x<outx; x++){
- if(inx == outx){
- /* don't resample if size unchanged */
- out[off+x*d] = in[off+x*d];
- continue;
- }
- v = 0.0;
- X = x*rat;
- for(k=-K2; k<=K2; k++){
- xx = X + rat*k/10.;
- i = xx;
- if(i < 0)
- i = 0;
- if(i >= inx)
- i = inx-1;
- v += in[off+i*d] * K[K2+k];
- }
- out[off+x*d] = v;
- }
- }
- void
- resampley(uint8_t **in, int off, int iny, uint8_t **out, int outy)
- {
- int y, i, k;
- double Y, yy, v, rat;
- rat = (double)iny/(double)outy;
- for(y=0; y<outy; y++){
- if(iny == outy){
- /* don't resample if size unchanged */
- out[y][off] = in[y][off];
- continue;
- }
- v = 0.0;
- Y = y*rat;
- for(k=-K2; k<=K2; k++){
- yy = Y + rat*k/10.;
- i = yy;
- if(i < 0)
- i = 0;
- if(i >= iny)
- i = iny-1;
- v += in[i][off] * K[K2+k];
- }
- out[y][off] = v;
- }
- }
- int
- max(int a, int b)
- {
- if(a > b)
- return a;
- return b;
- }
- Memimage*
- resample(int xsize, int ysize, Memimage *m)
- {
- int i, j, bpl, nchan;
- Memimage *new;
- uint8_t **oscan, **nscan;
- new = allocmemimage(Rect(0, 0, xsize, ysize), m->chan);
- if(new == nil)
- sysfatal("can't allocate new image: %r");
- oscan = malloc(Dy(m->r)*sizeof(uint8_t*));
- nscan = malloc(max(ysize, Dy(m->r))*sizeof(uint8_t*));
- if(oscan == nil || nscan == nil)
- sysfatal("can't allocate: %r");
- /* unload original image into scan lines */
- bpl = bytesperline(m->r, m->depth);
- for(i=0; i<Dy(m->r); i++){
- oscan[i] = malloc(bpl);
- if(oscan[i] == nil)
- sysfatal("can't allocate: %r");
- 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);
- if(j != bpl)
- sysfatal("unloadmemimage");
- }
- /* allocate scan lines for destination. we do y first, so need at least Dy(m->r) lines */
- bpl = bytesperline(Rect(0, 0, xsize, Dy(m->r)), m->depth);
- for(i=0; i<max(ysize, Dy(m->r)); i++){
- nscan[i] = malloc(bpl);
- if(nscan[i] == nil)
- sysfatal("can't allocate: %r");
- }
- /* resample in X */
- nchan = m->depth/8;
- for(i=0; i<Dy(m->r); i++){
- for(j=0; j<nchan; j++){
- if(j==0 && m->chan==XRGB32)
- continue;
- resamplex(oscan[i], j, nchan, Dx(m->r), nscan[i], xsize);
- }
- free(oscan[i]);
- oscan[i] = nscan[i];
- nscan[i] = malloc(bpl);
- if(nscan[i] == nil)
- sysfatal("can't allocate: %r");
- }
- /* resample in Y */
- for(i=0; i<xsize; i++)
- for(j=0; j<nchan; j++)
- resampley(oscan, nchan*i+j, Dy(m->r), nscan, ysize);
- /* pack data into destination */
- bpl = bytesperline(new->r, m->depth);
- for(i=0; i<ysize; i++){
- j = loadmemimage(new, Rect(0, i, xsize, i+1), nscan[i], bpl);
- if(j != bpl)
- sysfatal("loadmemimage: %r");
- }
- return new;
- }
- void
- main(int argc, char *argv[])
- {
- int i, fd, xsize, ysize, xpercent, ypercent;
- Rectangle rparam;
- Memimage *m, *new, *t1, *t2;
- char *file;
- uint32_t tchan;
- char tmp[100];
- double v;
- for(i=-K2; i<=K2; i++){
- K[K2+i] = kaiser(i/10., K2/10., 4.);
- // print("%g %g\n", i/10., K[K2+i]);
- }
- /* normalize */
- v = 0.0;
- for(i=0; i<NK; i++)
- v += K[i];
- for(i=0; i<NK; i++)
- K[i] /= v;
- memimageinit();
- memset(&rparam, 0, sizeof rparam);
- xsize = ysize = 0;
- xpercent = ypercent = 0;
- ARGBEGIN{
- case 'a': /* compatibility; equivalent to just -x or -y */
- if(xsize != 0 || ysize != 0)
- usage();
- xsize = getint(ARGF(), &xpercent);
- if(xsize <= 0)
- usage();
- ysize = xsize;
- ypercent = xpercent;
- break;
- case 'x':
- if(xsize != 0)
- usage();
- xsize = getint(ARGF(), &xpercent);
- if(xsize <= 0)
- usage();
- break;
- case 'y':
- if(ysize != 0)
- usage();
- ysize = getint(ARGF(), &ypercent);
- if(ysize <= 0)
- usage();
- break;
- default:
- usage();
- }ARGEND
- if(xsize == 0 && ysize == 0)
- usage();
- file = "<stdin>";
- fd = 0;
- if(argc > 1)
- usage();
- else if(argc == 1){
- file = argv[0];
- fd = open(file, OREAD);
- if(fd < 0)
- sysfatal("can't open %s: %r", file);
- }
- m = readmemimage(fd);
- if(m == nil)
- sysfatal("can't read %s: %r", file);
- if(xpercent)
- xsize = Dx(m->r)*xsize/100;
- if(ypercent)
- ysize = Dy(m->r)*ysize/100;
- if(ysize == 0)
- ysize = (xsize * Dy(m->r)) / Dx(m->r);
- if(xsize == 0)
- xsize = (ysize * Dx(m->r)) / Dy(m->r);
- new = nil;
- switch(m->chan){
- case GREY8:
- case RGB24:
- case RGBA32:
- case ARGB32:
- case XRGB32:
- new = resample(xsize, ysize, m);
- break;
- case CMAP8:
- case RGB15:
- case RGB16:
- tchan = RGB24;
- goto Convert;
- case GREY1:
- case GREY2:
- case GREY4:
- tchan = GREY8;
- Convert:
- /* use library to convert to byte-per-chan form, then convert back */
- t1 = allocmemimage(m->r, tchan);
- if(t1 == nil)
- sysfatal("can't allocate temporary image: %r");
- memimagedraw(t1, t1->r, m, m->r.min, nil, ZP, S);
- t2 = resample(xsize, ysize, t1);
- freememimage(t1);
- new = allocmemimage(Rect(0, 0, xsize, ysize), m->chan);
- if(new == nil)
- sysfatal("can't allocate new image: %r");
- /* should do error diffusion here */
- memimagedraw(new, new->r, t2, t2->r.min, nil, ZP, S);
- freememimage(t2);
- break;
- default:
- sysfatal("can't handle channel type %s", chantostr(tmp, m->chan));
- }
- assert(new);
- if(writememimage(1, new) < 0)
- sysfatal("write error on output: %r");
- exits(nil);
- }
|