Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <memdraw.h>
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];
10 double
11 fac(int L)
12 {
13 int i, f;
15 f = 1;
16 for(i=L; i>1; --i)
17 f *= i;
18 return f;
19 }
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;
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 }
38 double
39 kaiser(double x, double tau, double alpha)
40 {
41 if(fabs(x) > tau)
42 return 0.;
43 return i0(alpha*sqrt(1-(x*x/(tau*tau))))/i0(alpha);
44 }
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 }
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 }
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;
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 }
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;
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;
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];
120 out[y][off] = v;
125 int
126 max(int a, int b)
128 if(a > b)
129 return a;
130 return b;
133 Memimage*
134 resample(int xsize, int ysize, Memimage *m)
136 int i, j, bpl, nchan;
137 Memimage *new;
138 uchar **oscan, **nscan;
140 new = allocmemimage(Rect(0, 0, xsize, ysize), m->chan);
141 if(new == nil)
142 sysfatal("can't allocate new image: %r");
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");
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");
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");
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);
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");
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);
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");
195 return new;
198 void
199 main(int argc, char *argv[])
201 int i, fd, xsize, ysize, xpercent, ypercent;
202 Rectangle rparam;
203 Memimage *m, *new, *t1, *t2;
204 char *file;
205 ulong tchan;
206 char tmp[100];
207 double v;
209 for(i=-K2; i<=K2; i++){
210 K[K2+i] = kaiser(i/10., K2/10., 4.);
211 /* print("%g %g\n", i/10., K[K2+i]); */
214 /* normalize */
215 v = 0.0;
216 for(i=0; i<NK; i++)
217 v += K[i];
218 for(i=0; i<NK; i++)
219 K[i] /= v;
221 memimageinit();
222 memset(&rparam, 0, sizeof rparam);
223 xsize = ysize = 0;
224 xpercent = ypercent = 0;
226 ARGBEGIN{
227 case 'a': /* compatibility; equivalent to just -x or -y */
228 if(xsize != 0 || ysize != 0)
229 usage();
230 xsize = getint(ARGF(), &xpercent);
231 if(xsize <= 0)
232 usage();
233 ysize = xsize;
234 ypercent = xpercent;
235 break;
236 case 'x':
237 if(xsize != 0)
238 usage();
239 xsize = getint(ARGF(), &xpercent);
240 if(xsize <= 0)
241 usage();
242 break;
243 case 'y':
244 if(ysize != 0)
245 usage();
246 ysize = getint(ARGF(), &ypercent);
247 if(ysize <= 0)
248 usage();
249 break;
250 default:
251 usage();
252 }ARGEND
254 if(xsize == 0 && ysize == 0)
255 usage();
257 file = "<stdin>";
258 fd = 0;
259 if(argc > 1)
260 usage();
261 else if(argc == 1){
262 file = argv[0];
263 fd = open(file, OREAD);
264 if(fd < 0)
265 sysfatal("can't open %s: %r", file);
268 m = readmemimage(fd);
269 if(m == nil)
270 sysfatal("can't read %s: %r", file);
272 if(xpercent)
273 xsize = Dx(m->r)*xsize/100;
274 if(ypercent)
275 ysize = Dy(m->r)*ysize/100;
276 if(ysize == 0)
277 ysize = (xsize * Dy(m->r)) / Dx(m->r);
278 if(xsize == 0)
279 xsize = (ysize * Dx(m->r)) / Dy(m->r);
281 new = nil;
282 switch(m->chan){
284 case GREY8:
285 case RGB24:
286 case RGBA32:
287 case ARGB32:
288 case XRGB32:
289 new = resample(xsize, ysize, m);
290 break;
292 case CMAP8:
293 case RGB15:
294 case RGB16:
295 tchan = RGB24;
296 goto Convert;
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;
318 default:
319 sysfatal("can't handle channel type %s", chantostr(tmp, m->chan));
322 assert(new);
323 if(writememimage(1, new) < 0)
324 sysfatal("write error on output: %r");
326 exits(nil);