]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/resample.c
amd64, vmx: support avx/avx2 for host/guest; use *noavx= in plan9.ini to disable
[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         int n;
58
59         if(s == nil)
60                 usage();
61         n = strtol(s, &s, 0);
62         *percent = *s == '%';
63         return n;
64 }
65
66 void
67 resamplex(uchar *in, int off, int d, int inx, uchar *out, int outx)
68 {
69         int i, x, k;
70         double X, xx, v, rat;
71
72
73         rat = (double)inx/(double)outx;
74         for(x=0; x<outx; x++){
75                 if(inx == outx){
76                         /* don't resample if size unchanged */
77                         out[off+x*d] = in[off+x*d];
78                         continue;
79                 }
80                 v = 0.0;
81                 X = x*rat;
82                 for(k=-K2; k<=K2; k++){
83                         xx = X + rat*k/10.;
84                         i = xx;
85                         if(i < 0)
86                                 i = 0;
87                         if(i >= inx)
88                                 i = inx-1;
89                         v += in[off+i*d] * K[K2+k];
90                 }
91                 out[off+x*d] = v;
92         }
93 }
94
95 void
96 resampley(uchar **in, int off, int iny, uchar **out, int outy)
97 {
98         int y, i, k;
99         double Y, yy, v, rat;
100
101         rat = (double)iny/(double)outy;
102         for(y=0; y<outy; y++){
103                 if(iny == outy){
104                         /* don't resample if size unchanged */
105                         out[y][off] = in[y][off];
106                         continue;
107                 }
108                 v = 0.0;
109                 Y = y*rat;
110                 for(k=-K2; k<=K2; k++){
111                         yy = Y + rat*k/10.;
112                         i = yy;
113                         if(i < 0)
114                                 i = 0;
115                         if(i >= iny)
116                                 i = iny-1;
117                         v += in[i][off] * K[K2+k];
118                 }
119                 out[y][off] = v;
120         }
121
122 }
123
124 int
125 max(int a, int b)
126 {
127         if(a > b)
128                 return a;
129         return b;
130 }
131
132 Memimage*
133 resample(int xsize, int ysize, Memimage *m)
134 {
135         int i, j, d, bpl, nchan;
136         Memimage *new;
137         uchar **oscan, **nscan;
138
139         new = allocmemimage(Rect(0, 0, xsize, ysize), m->chan);
140         if(new == nil)
141                 sysfatal("can't allocate new image: %r");
142
143         oscan = malloc(Dy(m->r)*sizeof(uchar*));
144         nscan = malloc(max(ysize, Dy(m->r))*sizeof(uchar*));
145         if(oscan == nil || nscan == nil)
146                 sysfatal("can't allocate: %r");
147
148         /* unload original image into scan lines */
149         bpl = bytesperline(m->r, m->depth);
150         for(i=0; i<Dy(m->r); i++){
151                 oscan[i] = malloc(bpl);
152                 if(oscan[i] == nil)
153                         sysfatal("can't allocate: %r");
154                 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);
155                 if(j != bpl)
156                         sysfatal("unloadmemimage");
157         }
158
159         /* allocate scan lines for destination. we do y first, so need at least Dy(m->r) lines */
160         bpl = bytesperline(Rect(0, 0, xsize, Dy(m->r)), m->depth);
161         for(i=0; i<max(ysize, Dy(m->r)); i++){
162                 nscan[i] = malloc(bpl);
163                 if(nscan[i] == nil)
164                         sysfatal("can't allocate: %r");
165         }
166
167         /* resample in X */
168         nchan = d = m->depth/8;
169         if(m->chan == XRGB32)
170                 nchan--;
171         for(i=0; i<Dy(m->r); i++){
172                 for(j=0; j<nchan; j++)
173                         resamplex(oscan[i], j, d, Dx(m->r), nscan[i], xsize);
174                 free(oscan[i]);
175                 oscan[i] = nscan[i];
176                 nscan[i] = malloc(bpl);
177                 if(nscan[i] == nil)
178                         sysfatal("can't allocate: %r");
179         }
180
181         /* resample in Y */
182         for(i=0; i<xsize; i++)
183                 for(j=0; j<nchan; j++)
184                         resampley(oscan, d*i+j, Dy(m->r), nscan, ysize);
185
186         /* pack data into destination */
187         bpl = bytesperline(new->r, m->depth);
188         for(i=0; i<ysize; i++){
189                 j = loadmemimage(new, Rect(0, i, xsize, i+1), nscan[i], bpl);
190                 if(j != bpl)
191                         sysfatal("loadmemimage: %r");
192         }
193         return new;
194 }
195
196 void
197 main(int argc, char *argv[])
198 {
199         int i, fd, xsize, ysize, xpercent, ypercent;
200         Rectangle rparam;
201         Memimage *m, *new, *t1, *t2;
202         char *file;
203         ulong tchan;
204         double v;
205
206         for(i=-K2; i<=K2; i++){
207                 K[K2+i] = kaiser(i/10., K2/10., 4.);
208 //              print("%g %g\n", i/10., K[K2+i]);
209         }
210
211         /* normalize */
212         v = 0.0;
213         for(i=0; i<NK; i++)
214                 v += K[i];
215         for(i=0; i<NK; i++)
216                 K[i] /= v;
217
218         memimageinit();
219         memset(&rparam, 0, sizeof rparam);
220         xsize = ysize = 0;
221         xpercent = ypercent = 0;
222
223         ARGBEGIN{
224         case 'a':       /* compatibility; equivalent to just -x or -y */
225                 if(xsize != 0 || ysize != 0)
226                         usage();
227                 xsize = getint(ARGF(), &xpercent);
228                 if(xsize <= 0)
229                         usage();
230                 ysize = xsize;
231                 ypercent = xpercent;
232                 break;
233         case 'x':
234                 if(xsize != 0)
235                         usage();
236                 xsize = getint(ARGF(), &xpercent);
237                 if(xsize <= 0)
238                         usage();
239                 break;
240         case 'y':
241                 if(ysize != 0)
242                         usage();
243                 ysize = getint(ARGF(), &ypercent);
244                 if(ysize <= 0)
245                         usage();
246                 break;
247         default:
248                 usage();
249         }ARGEND
250
251         if(xsize == 0 && ysize == 0)
252                 usage();
253
254         file = "<stdin>";
255         fd = 0;
256         if(argc > 1)
257                 usage();
258         else if(argc == 1){
259                 file = argv[0];
260                 fd = open(file, OREAD);
261                 if(fd < 0)
262                         sysfatal("can't open %s: %r", file);
263         }
264
265         m = readmemimage(fd);
266         if(m == nil)
267                 sysfatal("can't read %s: %r", file);
268
269         if(xpercent)
270                 xsize = Dx(m->r)*xsize/100;
271         if(ypercent)
272                 ysize = Dy(m->r)*ysize/100;
273         if(ysize == 0)
274                 ysize = (xsize * Dy(m->r)) / Dx(m->r);
275         if(xsize == 0)
276                 xsize = (ysize * Dx(m->r)) / Dy(m->r);
277
278         switch(m->chan){
279         default:
280                 for(tchan = m->chan; tchan; tchan >>= 8)
281                         if(TYPE(tchan) == CAlpha){
282                                 tchan = RGBA32;
283                                 goto Convert;
284                         }
285                 tchan = RGB24;
286                 goto Convert;
287
288         case GREY8:
289         case RGB24:
290         case RGBA32:
291         case ARGB32:
292         case XRGB32:
293                 new = resample(xsize, ysize, m);
294                 break;
295
296         case GREY1:
297         case GREY2:
298         case GREY4:
299                 tchan = GREY8;
300         Convert:
301                 /* use library to convert to byte-per-chan form, then convert back */
302                 t1 = allocmemimage(m->r, tchan);
303                 if(t1 == nil)
304                         sysfatal("can't allocate temporary image: %r");
305                 memimagedraw(t1, t1->r, m, m->r.min, nil, ZP, S);
306                 t2 = resample(xsize, ysize, t1);
307                 freememimage(t1);
308                 new = allocmemimage(Rect(0, 0, xsize, ysize), m->chan);
309                 if(new == nil)
310                         sysfatal("can't allocate new image: %r");
311                 /* should do error diffusion here */
312                 memimagedraw(new, new->r, t2, t2->r.min, nil, ZP, S);
313                 freememimage(t2);
314                 break;
315         }
316
317         assert(new);
318         if(writememimage(1, new) < 0)
319                 sysfatal("write error on output: %r");
320
321         exits(nil);
322 }