]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/resample.c
fix typo
[plan9front.git] / sys / src / cmd / resample.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <memdraw.h>
5
6 #define K2 7    /* from -.7 to +.7 inclusive, meaning .2 into each adjacent pixel */
7 #define NK (2*K2+1)
8 double K[NK];
9
10 double
11 fac(int L)
12 {
13         int i, f;
14
15         f = 1;
16         for(i=L; i>1; --i)
17                 f *= i;
18         return f;
19 }
20
21 /* 
22  * i0(x) is the modified Bessel function, Σ (x/2)^2L / (L!)²
23  * There are faster ways to calculate this, but we precompute
24  * into a table so let's keep it simple.
25  */
26 double
27 i0(double x)
28 {
29         double v;
30         int L;
31
32         v = 1.0;
33         for(L=1; L<10; L++)
34                 v += pow(x/2., 2*L)/pow(fac(L), 2);
35         return v;
36 }
37
38 double
39 kaiser(double x, double τ, double α)
40 {
41         if(fabs(x) > τ)
42                 return 0.;
43         return i0(α*sqrt(1-(x*x/(τ*τ))))/i0(α);
44 }
45
46 void
47 usage(void)
48 {
49         fprint(2, "usage: resample [-x xsize] [-y ysize] [imagefile]\n");
50         fprint(2, "\twhere size is an integer or a percentage in the form 25%%\n");
51         exits("usage");
52 }
53
54 int
55 getint(char *s, int *percent)
56 {
57         if(s == nil)
58                 usage();
59         *percent = (s[strlen(s)-1] == '%');
60         if(*s == '+')
61                 return atoi(s+1);
62         if(*s == '-')
63                 return -atoi(s+1);
64         return atoi(s);
65 }
66
67 void
68 resamplex(uchar *in, int off, int d, int inx, uchar *out, int outx)
69 {
70         int i, x, k;
71         double X, xx, v, rat;
72
73
74         rat = (double)inx/(double)outx;
75         for(x=0; x<outx; x++){
76                 if(inx == outx){
77                         /* don't resample if size unchanged */
78                         out[off+x*d] = in[off+x*d];
79                         continue;
80                 }
81                 v = 0.0;
82                 X = x*rat;
83                 for(k=-K2; k<=K2; k++){
84                         xx = X + rat*k/10.;
85                         i = xx;
86                         if(i < 0)
87                                 i = 0;
88                         if(i >= inx)
89                                 i = inx-1;
90                         v += in[off+i*d] * K[K2+k];
91                 }
92                 out[off+x*d] = v;
93         }
94 }
95
96 void
97 resampley(uchar **in, int off, int iny, uchar **out, int outy)
98 {
99         int y, i, k;
100         double Y, yy, v, rat;
101
102         rat = (double)iny/(double)outy;
103         for(y=0; y<outy; y++){
104                 if(iny == outy){
105                         /* don't resample if size unchanged */
106                         out[y][off] = in[y][off];
107                         continue;
108                 }
109                 v = 0.0;
110                 Y = y*rat;
111                 for(k=-K2; k<=K2; k++){
112                         yy = Y + rat*k/10.;
113                         i = yy;
114                         if(i < 0)
115                                 i = 0;
116                         if(i >= iny)
117                                 i = iny-1;
118                         v += in[i][off] * K[K2+k];
119                 }
120                 out[y][off] = v;
121         }
122
123 }
124
125 int
126 max(int a, int b)
127 {
128         if(a > b)
129                 return a;
130         return b;
131 }
132
133 Memimage*
134 resample(int xsize, int ysize, Memimage *m)
135 {
136         int i, j, bpl, nchan;
137         Memimage *new;
138         uchar **oscan, **nscan;
139
140         new = allocmemimage(Rect(0, 0, xsize, ysize), m->chan);
141         if(new == nil)
142                 sysfatal("can't allocate new image: %r");
143
144         oscan = malloc(Dy(m->r)*sizeof(uchar*));
145         nscan = malloc(max(ysize, Dy(m->r))*sizeof(uchar*));
146         if(oscan == nil || nscan == nil)
147                 sysfatal("can't allocate: %r");
148
149         /* unload original image into scan lines */
150         bpl = bytesperline(m->r, m->depth);
151         for(i=0; i<Dy(m->r); i++){
152                 oscan[i] = malloc(bpl);
153                 if(oscan[i] == nil)
154                         sysfatal("can't allocate: %r");
155                 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);
156                 if(j != bpl)
157                         sysfatal("unloadmemimage");
158         }
159
160         /* allocate scan lines for destination. we do y first, so need at least Dy(m->r) lines */
161         bpl = bytesperline(Rect(0, 0, xsize, Dy(m->r)), m->depth);
162         for(i=0; i<max(ysize, Dy(m->r)); i++){
163                 nscan[i] = malloc(bpl);
164                 if(nscan[i] == nil)
165                         sysfatal("can't allocate: %r");
166         }
167
168         /* resample in X */
169         nchan = m->depth/8;
170         for(i=0; i<Dy(m->r); i++){
171                 for(j=0; j<nchan; j++){
172                         if(j==0 && m->chan==XRGB32)
173                                 continue;
174                         resamplex(oscan[i], j, nchan, Dx(m->r), nscan[i], xsize);
175                 }
176                 free(oscan[i]);
177                 oscan[i] = nscan[i];
178                 nscan[i] = malloc(bpl);
179                 if(nscan[i] == nil)
180                         sysfatal("can't allocate: %r");
181         }
182
183         /* resample in Y */
184         for(i=0; i<xsize; i++)
185                 for(j=0; j<nchan; j++)
186                         resampley(oscan, nchan*i+j, Dy(m->r), nscan, ysize);
187
188         /* pack data into destination */
189         bpl = bytesperline(new->r, m->depth);
190         for(i=0; i<ysize; i++){
191                 j = loadmemimage(new, Rect(0, i, xsize, i+1), nscan[i], bpl);
192                 if(j != bpl)
193                         sysfatal("loadmemimage: %r");
194         }
195         return new;
196 }
197
198 void
199 main(int argc, char *argv[])
200 {
201         int i, fd, xsize, ysize, xpercent, ypercent;
202         Rectangle rparam;
203         Memimage *m, *new, *t1, *t2;
204         char *file;
205         ulong tchan;
206         double v;
207
208         for(i=-K2; i<=K2; i++){
209                 K[K2+i] = kaiser(i/10., K2/10., 4.);
210 //              print("%g %g\n", i/10., K[K2+i]);
211         }
212
213         /* normalize */
214         v = 0.0;
215         for(i=0; i<NK; i++)
216                 v += K[i];
217         for(i=0; i<NK; i++)
218                 K[i] /= v;
219
220         memimageinit();
221         memset(&rparam, 0, sizeof rparam);
222         xsize = ysize = 0;
223         xpercent = ypercent = 0;
224
225         ARGBEGIN{
226         case 'a':       /* compatibility; equivalent to just -x or -y */
227                 if(xsize != 0 || ysize != 0)
228                         usage();
229                 xsize = getint(ARGF(), &xpercent);
230                 if(xsize <= 0)
231                         usage();
232                 ysize = xsize;
233                 ypercent = xpercent;
234                 break;
235         case 'x':
236                 if(xsize != 0)
237                         usage();
238                 xsize = getint(ARGF(), &xpercent);
239                 if(xsize <= 0)
240                         usage();
241                 break;
242         case 'y':
243                 if(ysize != 0)
244                         usage();
245                 ysize = getint(ARGF(), &ypercent);
246                 if(ysize <= 0)
247                         usage();
248                 break;
249         default:
250                 usage();
251         }ARGEND
252
253         if(xsize == 0 && ysize == 0)
254                 usage();
255
256         file = "<stdin>";
257         fd = 0;
258         if(argc > 1)
259                 usage();
260         else if(argc == 1){
261                 file = argv[0];
262                 fd = open(file, OREAD);
263                 if(fd < 0)
264                         sysfatal("can't open %s: %r", file);
265         }
266
267         m = readmemimage(fd);
268         if(m == nil)
269                 sysfatal("can't read %s: %r", file);
270
271         if(xpercent)
272                 xsize = Dx(m->r)*xsize/100;
273         if(ypercent)
274                 ysize = Dy(m->r)*ysize/100;
275         if(ysize == 0)
276                 ysize = (xsize * Dy(m->r)) / Dx(m->r);
277         if(xsize == 0)
278                 xsize = (ysize * Dx(m->r)) / Dy(m->r);
279
280         switch(m->chan){
281         default:
282                 for(tchan = m->chan; tchan; tchan >>= 8)
283                         if(TYPE(tchan) == CAlpha){
284                                 tchan = RGBA32;
285                                 goto Convert;
286                         }
287                 tchan = RGB24;
288                 goto Convert;
289
290         case GREY8:
291         case RGB24:
292         case RGBA32:
293         case ARGB32:
294         case XRGB32:
295                 new = resample(xsize, ysize, m);
296                 break;
297
298         case GREY1:
299         case GREY2:
300         case GREY4:
301                 tchan = GREY8;
302         Convert:
303                 /* use library to convert to byte-per-chan form, then convert back */
304                 t1 = allocmemimage(m->r, tchan);
305                 if(t1 == nil)
306                         sysfatal("can't allocate temporary image: %r");
307                 memimagedraw(t1, t1->r, m, m->r.min, nil, ZP, S);
308                 t2 = resample(xsize, ysize, t1);
309                 freememimage(t1);
310                 new = allocmemimage(Rect(0, 0, xsize, ysize), m->chan);
311                 if(new == nil)
312                         sysfatal("can't allocate new image: %r");
313                 /* should do error diffusion here */
314                 memimagedraw(new, new->r, t2, t2->r.min, nil, ZP, S);
315                 freememimage(t2);
316                 break;
317         }
318
319         assert(new);
320         if(writememimage(1, new) < 0)
321                 sysfatal("write error on output: %r");
322
323         exits(nil);
324 }