Commit | Line | Data |
---|---|---|
faa9b3e7 TC |
1 | /* |
2 | =head1 NAME | |
3 | ||
4 | rotate.c - implements image rotations | |
5 | ||
6 | =head1 SYNOPSIS | |
7 | ||
8 | i_img *i_rotate90(i_img *src, int degrees) | |
9 | ||
10 | =head1 DESCRIPTION | |
11 | ||
12 | Implements basic 90 degree rotations of an image. | |
13 | ||
14 | Other rotations will be added as tuits become available. | |
15 | ||
16 | =cut | |
17 | */ | |
18 | ||
19 | #include "image.h" | |
20 | #include <math.h> /* for floor() */ | |
21 | ||
22 | i_img *i_rotate90(i_img *src, int degrees) { | |
23 | i_img *targ; | |
24 | int x, y; | |
25 | ||
26 | i_clear_error(); | |
27 | ||
28 | if (degrees == 180) { | |
29 | /* essentially the same as flipxy(..., 2) except that it's not | |
30 | done in place */ | |
31 | targ = i_sametype(src, src->xsize, src->ysize); | |
32 | if (src->type == i_direct_type) { | |
33 | if (src->bits == i_8_bits) { | |
34 | i_color *vals = mymalloc(src->xsize * sizeof(i_color)); | |
35 | for (y = 0; y < src->ysize; ++y) { | |
36 | i_color tmp; | |
37 | i_glin(src, 0, src->xsize, y, vals); | |
38 | for (x = 0; x < src->xsize/2; ++x) { | |
39 | tmp = vals[x]; | |
40 | vals[x] = vals[src->xsize - x - 1]; | |
41 | vals[src->xsize - x - 1] = tmp; | |
42 | } | |
43 | i_plin(targ, 0, src->xsize, src->ysize - y - 1, vals); | |
44 | } | |
45 | myfree(vals); | |
46 | } | |
47 | else { | |
48 | i_fcolor *vals = mymalloc(src->xsize * sizeof(i_fcolor)); | |
49 | for (y = 0; y < src->ysize; ++y) { | |
50 | i_fcolor tmp; | |
51 | i_glinf(src, 0, src->xsize, y, vals); | |
52 | for (x = 0; x < src->xsize/2; ++x) { | |
53 | tmp = vals[x]; | |
54 | vals[x] = vals[src->xsize - x - 1]; | |
55 | vals[src->xsize - x - 1] = tmp; | |
56 | } | |
57 | i_plinf(targ, 0, src->xsize, src->ysize - y - 1, vals); | |
58 | } | |
59 | myfree(vals); | |
60 | } | |
61 | } | |
62 | else { | |
63 | i_palidx *vals = mymalloc(src->xsize * sizeof(i_palidx)); | |
64 | ||
65 | for (y = 0; y < src->ysize; ++y) { | |
66 | i_palidx tmp; | |
67 | i_gpal(src, 0, src->xsize, y, vals); | |
68 | for (x = 0; x < src->xsize/2; ++x) { | |
69 | tmp = vals[x]; | |
70 | vals[x] = vals[src->xsize - x - 1]; | |
71 | vals[src->xsize - x - 1] = tmp; | |
72 | } | |
73 | i_ppal(targ, 0, src->xsize, src->ysize - y - 1, vals); | |
74 | } | |
75 | ||
76 | myfree(vals); | |
77 | } | |
78 | ||
79 | return targ; | |
80 | } | |
81 | else if (degrees == 270 || degrees == 90) { | |
82 | int tx, txstart, txinc; | |
83 | int ty, tystart, tyinc; | |
84 | ||
85 | if (degrees == 270) { | |
86 | txstart = 0; | |
87 | txinc = 1; | |
88 | tystart = src->xsize-1; | |
89 | tyinc = -1; | |
90 | } | |
91 | else { | |
92 | txstart = src->ysize-1; | |
93 | txinc = -1; | |
94 | tystart = 0; | |
95 | tyinc = 1; | |
96 | } | |
97 | targ = i_sametype(src, src->ysize, src->xsize); | |
98 | if (src->type == i_direct_type) { | |
99 | if (src->bits == i_8_bits) { | |
100 | i_color *vals = mymalloc(src->xsize * sizeof(i_color)); | |
101 | ||
102 | tx = txstart; | |
103 | for (y = 0; y < src->ysize; ++y) { | |
104 | i_glin(src, 0, src->xsize, y, vals); | |
105 | ty = tystart; | |
106 | for (x = 0; x < src->xsize; ++x) { | |
107 | i_ppix(targ, tx, ty, vals+x); | |
108 | ty += tyinc; | |
109 | } | |
110 | tx += txinc; | |
111 | } | |
112 | myfree(vals); | |
113 | } | |
114 | else { | |
115 | i_fcolor *vals = mymalloc(src->xsize * sizeof(i_fcolor)); | |
116 | ||
117 | tx = txstart; | |
118 | for (y = 0; y < src->ysize; ++y) { | |
119 | i_glinf(src, 0, src->xsize, y, vals); | |
120 | ty = tystart; | |
121 | for (x = 0; x < src->xsize; ++x) { | |
122 | i_ppixf(targ, tx, ty, vals+x); | |
123 | ty += tyinc; | |
124 | } | |
125 | tx += txinc; | |
126 | } | |
127 | myfree(vals); | |
128 | } | |
129 | } | |
130 | else { | |
131 | i_palidx *vals = mymalloc(src->xsize * sizeof(i_palidx)); | |
132 | ||
133 | tx = txstart; | |
134 | for (y = 0; y < src->ysize; ++y) { | |
135 | i_gpal(src, 0, src->xsize, y, vals); | |
136 | ty = tystart; | |
137 | for (x = 0; x < src->xsize; ++x) { | |
138 | i_ppal(targ, tx, tx+1, ty, vals+x); | |
139 | ty += tyinc; | |
140 | } | |
141 | tx += txinc; | |
142 | } | |
143 | myfree(vals); | |
144 | } | |
145 | return targ; | |
146 | } | |
147 | else { | |
148 | i_push_error(0, "i_rotate90() only rotates at 90, 180, or 270 degrees"); | |
149 | return NULL; | |
150 | } | |
151 | } | |
152 | ||
153 | /* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */ | |
154 | /* linear interpolation */ | |
155 | static i_color interp_i_color(i_color before, i_color after, double pos, | |
156 | int channels) { | |
157 | i_color out; | |
158 | int ch; | |
159 | ||
160 | pos -= floor(pos); | |
161 | for (ch = 0; ch < channels; ++ch) | |
162 | out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch]; | |
163 | ||
164 | return out; | |
165 | } | |
166 | ||
167 | /* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */ | |
168 | /* linear interpolation */ | |
169 | static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos, | |
170 | int channels) { | |
171 | i_fcolor out; | |
172 | int ch; | |
173 | ||
174 | pos -= floor(pos); | |
175 | for (ch = 0; ch < channels; ++ch) | |
176 | out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch]; | |
177 | ||
178 | return out; | |
179 | } | |
180 | ||
0d3b936e TC |
181 | i_img *i_matrix_transform_bg(i_img *src, int xsize, int ysize, double *matrix, |
182 | i_color *backp, i_fcolor *fbackp) { | |
faa9b3e7 TC |
183 | i_img *result = i_sametype(src, xsize, ysize); |
184 | int x, y; | |
185 | int ch; | |
186 | int i, j; | |
187 | double sx, sy, sz; | |
faa9b3e7 TC |
188 | |
189 | if (src->type == i_direct_type) { | |
190 | if (src->bits == i_8_bits) { | |
191 | i_color *vals = mymalloc(xsize * sizeof(i_color)); | |
0d3b936e TC |
192 | i_color back; |
193 | i_fsample_t fsamp; | |
faa9b3e7 | 194 | |
0d3b936e TC |
195 | if (backp) { |
196 | back = *backp; | |
197 | } | |
198 | else if (fbackp) { | |
199 | for (ch = 0; ch < src->channels; ++ch) { | |
200 | fsamp = fbackp->channel[ch]; | |
201 | back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255; | |
202 | } | |
203 | } | |
204 | else { | |
205 | for (ch = 0; ch < src->channels; ++ch) | |
206 | back.channel[ch] = 0; | |
207 | } | |
faa9b3e7 TC |
208 | |
209 | for (y = 0; y < ysize; ++y) { | |
210 | for (x = 0; x < xsize; ++x) { | |
211 | /* dividing by sz gives us the ability to do perspective | |
212 | transforms */ | |
213 | sz = x * matrix[6] + y * matrix[7] + matrix[8]; | |
214 | if (abs(sz) > 0.0000001) { | |
215 | sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz; | |
216 | sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz; | |
217 | } | |
218 | ||
219 | /* anything outside these ranges is either a broken co-ordinate | |
220 | or outside the source */ | |
221 | if (abs(sz) > 0.0000001 | |
222 | && sx >= -1 && sx < src->xsize | |
223 | && sy >= -1 && sy < src->ysize) { | |
224 | ||
225 | if (sx != (int)sx) { | |
226 | if (sy != (int)sy) { | |
227 | i_color c[2][2]; | |
228 | i_color ci2[2]; | |
229 | for (i = 0; i < 2; ++i) | |
230 | for (j = 0; j < 2; ++j) | |
231 | if (i_gpix(src, floor(sx)+i, floor(sy)+j, &c[j][i])) | |
0d3b936e | 232 | c[j][i] = back; |
faa9b3e7 TC |
233 | for (j = 0; j < 2; ++j) |
234 | ci2[j] = interp_i_color(c[j][0], c[j][1], sx, src->channels); | |
235 | vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels); | |
236 | } | |
237 | else { | |
238 | i_color ci2[2]; | |
239 | for (i = 0; i < 2; ++i) | |
240 | if (i_gpix(src, floor(sx)+i, sy, ci2+i)) | |
0d3b936e | 241 | ci2[i] = back; |
faa9b3e7 TC |
242 | vals[x] = interp_i_color(ci2[0], ci2[1], sx, src->channels); |
243 | } | |
244 | } | |
245 | else { | |
246 | if (sy != (int)sy) { | |
247 | i_color ci2[2]; | |
248 | for (i = 0; i < 2; ++i) | |
249 | if (i_gpix(src, sx, floor(sy)+i, ci2+i)) | |
0d3b936e | 250 | ci2[i] = back; |
faa9b3e7 TC |
251 | vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels); |
252 | } | |
253 | else { | |
254 | /* all the world's an integer */ | |
0d3b936e TC |
255 | if (i_gpix(src, sx, sy, vals+x)) |
256 | vals[x] = back; | |
faa9b3e7 TC |
257 | } |
258 | } | |
259 | } | |
260 | else { | |
0d3b936e | 261 | vals[x] = back; |
faa9b3e7 TC |
262 | } |
263 | } | |
264 | i_plin(result, 0, xsize, y, vals); | |
265 | } | |
266 | myfree(vals); | |
267 | } | |
268 | else { | |
269 | i_fcolor *vals = mymalloc(xsize * sizeof(i_fcolor)); | |
0d3b936e | 270 | i_fcolor back; |
faa9b3e7 | 271 | |
0d3b936e TC |
272 | if (fbackp) { |
273 | back = *fbackp; | |
274 | } | |
275 | else if (backp) { | |
276 | for (ch = 0; ch < src->channels; ++ch) | |
277 | back.channel[ch] = backp->channel[ch] / 255.0; | |
278 | } | |
279 | else { | |
280 | for (ch = 0; ch < src->channels; ++ch) | |
281 | back.channel[ch] = 0; | |
282 | } | |
faa9b3e7 TC |
283 | |
284 | for (y = 0; y < ysize; ++y) { | |
285 | for (x = 0; x < xsize; ++x) { | |
286 | /* dividing by sz gives us the ability to do perspective | |
287 | transforms */ | |
288 | sz = x * matrix[6] + y * matrix[7] + matrix[8]; | |
289 | if (abs(sz) > 0.0000001) { | |
290 | sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz; | |
291 | sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz; | |
292 | } | |
293 | ||
294 | /* anything outside these ranges is either a broken co-ordinate | |
295 | or outside the source */ | |
296 | if (abs(sz) > 0.0000001 | |
297 | && sx >= -1 && sx < src->xsize | |
298 | && sy >= -1 && sy < src->ysize) { | |
299 | ||
300 | if (sx != (int)sx) { | |
301 | if (sy != (int)sy) { | |
302 | i_fcolor c[2][2]; | |
303 | i_fcolor ci2[2]; | |
304 | for (i = 0; i < 2; ++i) | |
305 | for (j = 0; j < 2; ++j) | |
306 | if (i_gpixf(src, floor(sx)+i, floor(sy)+j, &c[j][i])) | |
0d3b936e | 307 | c[j][i] = back; |
faa9b3e7 TC |
308 | for (j = 0; j < 2; ++j) |
309 | ci2[j] = interp_i_fcolor(c[j][0], c[j][1], sx, src->channels); | |
310 | vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels); | |
311 | } | |
312 | else { | |
313 | i_fcolor ci2[2]; | |
314 | for (i = 0; i < 2; ++i) | |
315 | if (i_gpixf(src, floor(sx)+i, sy, ci2+i)) | |
0d3b936e | 316 | ci2[i] = back; |
faa9b3e7 TC |
317 | vals[x] = interp_i_fcolor(ci2[0], ci2[1], sx, src->channels); |
318 | } | |
319 | } | |
320 | else { | |
321 | if (sy != (int)sy) { | |
322 | i_fcolor ci2[2]; | |
323 | for (i = 0; i < 2; ++i) | |
324 | if (i_gpixf(src, sx, floor(sy)+i, ci2+i)) | |
0d3b936e | 325 | ci2[i] = back; |
faa9b3e7 TC |
326 | vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels); |
327 | } | |
328 | else { | |
329 | /* all the world's an integer */ | |
0d3b936e TC |
330 | if (i_gpixf(src, sx, sy, vals+x)) |
331 | vals[x] = back; | |
faa9b3e7 TC |
332 | } |
333 | } | |
334 | } | |
335 | else { | |
0d3b936e | 336 | vals[x] = back; |
faa9b3e7 TC |
337 | } |
338 | } | |
339 | i_plinf(result, 0, xsize, y, vals); | |
340 | } | |
341 | myfree(vals); | |
342 | } | |
343 | } | |
344 | else { | |
345 | /* don't interpolate for a palette based image */ | |
346 | i_palidx *vals = mymalloc(xsize * sizeof(i_palidx)); | |
0d3b936e | 347 | i_palidx back = 0; |
faa9b3e7 | 348 | i_color min; |
0d3b936e | 349 | int minval = 256 * 4; |
faa9b3e7 | 350 | int ix, iy; |
0d3b936e TC |
351 | i_color want_back; |
352 | i_fsample_t fsamp; | |
faa9b3e7 | 353 | |
0d3b936e TC |
354 | if (backp) { |
355 | want_back = *backp; | |
356 | } | |
357 | else if (fbackp) { | |
358 | for (ch = 0; ch < src->channels; ++ch) { | |
359 | fsamp = fbackp->channel[ch]; | |
360 | want_back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255; | |
361 | } | |
362 | } | |
363 | else { | |
364 | for (ch = 0; ch < src->channels; ++ch) | |
365 | want_back.channel[ch] = 0; | |
366 | } | |
367 | ||
368 | /* find the closest color */ | |
369 | for (i = 0; i < i_colorcount(src); ++i) { | |
faa9b3e7 TC |
370 | i_color temp; |
371 | int tempval; | |
372 | i_getcolors(src, i, &temp, 1); | |
373 | tempval = 0; | |
374 | for (ch = 0; ch < src->channels; ++ch) { | |
0d3b936e | 375 | tempval += abs(want_back.channel[ch] - temp.channel[ch]); |
faa9b3e7 TC |
376 | } |
377 | if (tempval < minval) { | |
0d3b936e | 378 | back = i; |
faa9b3e7 TC |
379 | min = temp; |
380 | minval = tempval; | |
381 | } | |
382 | } | |
383 | ||
384 | for (y = 0; y < ysize; ++y) { | |
385 | for (x = 0; x < xsize; ++x) { | |
386 | /* dividing by sz gives us the ability to do perspective | |
387 | transforms */ | |
388 | sz = x * matrix[6] + y * matrix[7] + matrix[8]; | |
389 | if (abs(sz) > 0.0000001) { | |
390 | sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz; | |
391 | sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz; | |
392 | } | |
393 | ||
394 | /* anything outside these ranges is either a broken co-ordinate | |
395 | or outside the source */ | |
396 | if (abs(sz) > 0.0000001 | |
397 | && sx >= -0.5 && sx < src->xsize-0.5 | |
398 | && sy >= -0.5 && sy < src->ysize-0.5) { | |
399 | ||
400 | /* all the world's an integer */ | |
401 | ix = (int)(sx+0.5); | |
402 | iy = (int)(sy+0.5); | |
0d3b936e TC |
403 | if (!i_gpal(src, ix, ix+1, iy, vals+x)) |
404 | vals[i] = back; | |
faa9b3e7 TC |
405 | } |
406 | else { | |
0d3b936e | 407 | vals[x] = back; |
faa9b3e7 TC |
408 | } |
409 | } | |
410 | i_ppal(result, 0, xsize, y, vals); | |
411 | } | |
412 | myfree(vals); | |
413 | } | |
414 | ||
415 | return result; | |
416 | } | |
417 | ||
0d3b936e TC |
418 | i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) { |
419 | return i_matrix_transform_bg(src, xsize, ysize, matrix, NULL, NULL); | |
420 | } | |
421 | ||
35891892 | 422 | static void |
faa9b3e7 TC |
423 | i_matrix_mult(double *dest, double *left, double *right) { |
424 | int i, j, k; | |
425 | double accum; | |
426 | ||
427 | for (i = 0; i < 3; ++i) { | |
428 | for (j = 0; j < 3; ++j) { | |
429 | accum = 0.0; | |
430 | for (k = 0; k < 3; ++k) { | |
431 | accum += left[3*i+k] * right[3*k+j]; | |
432 | } | |
433 | dest[3*i+j] = accum; | |
434 | } | |
435 | } | |
436 | } | |
437 | ||
0d3b936e TC |
438 | i_img *i_rotate_exact_bg(i_img *src, double amount, |
439 | i_color *backp, i_fcolor *fbackp) { | |
faa9b3e7 TC |
440 | double xlate1[9] = { 0 }; |
441 | double rotate[9]; | |
442 | double xlate2[9] = { 0 }; | |
443 | double temp[9], matrix[9]; | |
444 | int x1, x2, y1, y2, newxsize, newysize; | |
445 | ||
446 | /* first translate the centre of the image to (0,0) */ | |
447 | xlate1[0] = 1; | |
448 | xlate1[2] = src->xsize/2.0; | |
449 | xlate1[4] = 1; | |
450 | xlate1[5] = src->ysize/2.0; | |
451 | xlate1[8] = 1; | |
452 | ||
453 | /* rotate around (0.0) */ | |
454 | rotate[0] = cos(amount); | |
455 | rotate[1] = sin(amount); | |
456 | rotate[2] = 0; | |
457 | rotate[3] = -rotate[1]; | |
458 | rotate[4] = rotate[0]; | |
459 | rotate[5] = 0; | |
460 | rotate[6] = 0; | |
461 | rotate[7] = 0; | |
462 | rotate[8] = 1; | |
463 | ||
464 | x1 = ceil(abs(src->xsize * rotate[0] + src->ysize * rotate[1])); | |
465 | x2 = ceil(abs(src->xsize * rotate[0] - src->ysize * rotate[1])); | |
466 | y1 = ceil(abs(src->xsize * rotate[3] + src->ysize * rotate[4])); | |
467 | y2 = ceil(abs(src->xsize * rotate[3] - src->ysize * rotate[4])); | |
468 | newxsize = x1 > x2 ? x1 : x2; | |
469 | newysize = y1 > y2 ? y1 : y2; | |
470 | /* translate the centre back to the center of the image */ | |
471 | xlate2[0] = 1; | |
472 | xlate2[2] = -newxsize/2; | |
473 | xlate2[4] = 1; | |
474 | xlate2[5] = -newysize/2; | |
475 | xlate2[8] = 1; | |
476 | i_matrix_mult(temp, xlate1, rotate); | |
477 | i_matrix_mult(matrix, temp, xlate2); | |
478 | ||
0d3b936e | 479 | return i_matrix_transform_bg(src, newxsize, newysize, matrix, backp, fbackp); |
faa9b3e7 | 480 | } |
b8c2033e | 481 | |
0d3b936e TC |
482 | i_img *i_rotate_exact(i_img *src, double amount) { |
483 | return i_rotate_exact_bg(src, amount, NULL, NULL); | |
484 | } | |
485 | ||
486 | ||
b8c2033e AMH |
487 | /* |
488 | =back | |
489 | ||
490 | =head1 AUTHOR | |
491 | ||
492 | Tony Cook <tony@develop-help.com> | |
493 | ||
494 | =head1 SEE ALSO | |
495 | ||
496 | Imager(3) | |
497 | ||
498 | =cut | |
499 | */ |