avoid various compiler warnings
[imager.git] / rotate.c
CommitLineData
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
12Implements basic 90 degree rotations of an image.
13
14Other rotations will be added as tuits become available.
15
16=cut
17*/
18
92bda632 19#include "imager.h"
faa9b3e7
TC
20#include <math.h> /* for floor() */
21
22i_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 */
155static 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 */
169static 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
97ac0a96
TC
181i_img *i_matrix_transform_bg(i_img *src, int xsize, int ysize, const double *matrix,
182 const i_color *backp, const 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 }
b07bc64b
TC
218 else {
219 sx = sy = 0;
220 }
faa9b3e7
TC
221
222 /* anything outside these ranges is either a broken co-ordinate
223 or outside the source */
224 if (abs(sz) > 0.0000001
225 && sx >= -1 && sx < src->xsize
226 && sy >= -1 && sy < src->ysize) {
227
228 if (sx != (int)sx) {
229 if (sy != (int)sy) {
230 i_color c[2][2];
231 i_color ci2[2];
232 for (i = 0; i < 2; ++i)
233 for (j = 0; j < 2; ++j)
234 if (i_gpix(src, floor(sx)+i, floor(sy)+j, &c[j][i]))
0d3b936e 235 c[j][i] = back;
faa9b3e7
TC
236 for (j = 0; j < 2; ++j)
237 ci2[j] = interp_i_color(c[j][0], c[j][1], sx, src->channels);
238 vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels);
239 }
240 else {
241 i_color ci2[2];
242 for (i = 0; i < 2; ++i)
243 if (i_gpix(src, floor(sx)+i, sy, ci2+i))
0d3b936e 244 ci2[i] = back;
faa9b3e7
TC
245 vals[x] = interp_i_color(ci2[0], ci2[1], sx, src->channels);
246 }
247 }
248 else {
249 if (sy != (int)sy) {
250 i_color ci2[2];
251 for (i = 0; i < 2; ++i)
252 if (i_gpix(src, sx, floor(sy)+i, ci2+i))
0d3b936e 253 ci2[i] = back;
faa9b3e7
TC
254 vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels);
255 }
256 else {
257 /* all the world's an integer */
0d3b936e
TC
258 if (i_gpix(src, sx, sy, vals+x))
259 vals[x] = back;
faa9b3e7
TC
260 }
261 }
262 }
263 else {
0d3b936e 264 vals[x] = back;
faa9b3e7
TC
265 }
266 }
267 i_plin(result, 0, xsize, y, vals);
268 }
269 myfree(vals);
270 }
271 else {
272 i_fcolor *vals = mymalloc(xsize * sizeof(i_fcolor));
0d3b936e 273 i_fcolor back;
faa9b3e7 274
0d3b936e
TC
275 if (fbackp) {
276 back = *fbackp;
277 }
278 else if (backp) {
279 for (ch = 0; ch < src->channels; ++ch)
280 back.channel[ch] = backp->channel[ch] / 255.0;
281 }
282 else {
283 for (ch = 0; ch < src->channels; ++ch)
284 back.channel[ch] = 0;
285 }
faa9b3e7
TC
286
287 for (y = 0; y < ysize; ++y) {
288 for (x = 0; x < xsize; ++x) {
289 /* dividing by sz gives us the ability to do perspective
290 transforms */
291 sz = x * matrix[6] + y * matrix[7] + matrix[8];
292 if (abs(sz) > 0.0000001) {
293 sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
294 sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
295 }
296
297 /* anything outside these ranges is either a broken co-ordinate
298 or outside the source */
299 if (abs(sz) > 0.0000001
300 && sx >= -1 && sx < src->xsize
301 && sy >= -1 && sy < src->ysize) {
302
303 if (sx != (int)sx) {
304 if (sy != (int)sy) {
305 i_fcolor c[2][2];
306 i_fcolor ci2[2];
307 for (i = 0; i < 2; ++i)
308 for (j = 0; j < 2; ++j)
309 if (i_gpixf(src, floor(sx)+i, floor(sy)+j, &c[j][i]))
0d3b936e 310 c[j][i] = back;
faa9b3e7
TC
311 for (j = 0; j < 2; ++j)
312 ci2[j] = interp_i_fcolor(c[j][0], c[j][1], sx, src->channels);
313 vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels);
314 }
315 else {
316 i_fcolor ci2[2];
317 for (i = 0; i < 2; ++i)
318 if (i_gpixf(src, floor(sx)+i, sy, ci2+i))
0d3b936e 319 ci2[i] = back;
faa9b3e7
TC
320 vals[x] = interp_i_fcolor(ci2[0], ci2[1], sx, src->channels);
321 }
322 }
323 else {
324 if (sy != (int)sy) {
325 i_fcolor ci2[2];
326 for (i = 0; i < 2; ++i)
327 if (i_gpixf(src, sx, floor(sy)+i, ci2+i))
0d3b936e 328 ci2[i] = back;
faa9b3e7
TC
329 vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels);
330 }
331 else {
332 /* all the world's an integer */
0d3b936e
TC
333 if (i_gpixf(src, sx, sy, vals+x))
334 vals[x] = back;
faa9b3e7
TC
335 }
336 }
337 }
338 else {
0d3b936e 339 vals[x] = back;
faa9b3e7
TC
340 }
341 }
342 i_plinf(result, 0, xsize, y, vals);
343 }
344 myfree(vals);
345 }
346 }
347 else {
348 /* don't interpolate for a palette based image */
349 i_palidx *vals = mymalloc(xsize * sizeof(i_palidx));
0d3b936e 350 i_palidx back = 0;
faa9b3e7 351 i_color min;
0d3b936e 352 int minval = 256 * 4;
faa9b3e7 353 int ix, iy;
0d3b936e
TC
354 i_color want_back;
355 i_fsample_t fsamp;
faa9b3e7 356
0d3b936e
TC
357 if (backp) {
358 want_back = *backp;
359 }
360 else if (fbackp) {
361 for (ch = 0; ch < src->channels; ++ch) {
362 fsamp = fbackp->channel[ch];
363 want_back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255;
364 }
365 }
366 else {
367 for (ch = 0; ch < src->channels; ++ch)
368 want_back.channel[ch] = 0;
369 }
370
371 /* find the closest color */
372 for (i = 0; i < i_colorcount(src); ++i) {
faa9b3e7
TC
373 i_color temp;
374 int tempval;
375 i_getcolors(src, i, &temp, 1);
376 tempval = 0;
377 for (ch = 0; ch < src->channels; ++ch) {
0d3b936e 378 tempval += abs(want_back.channel[ch] - temp.channel[ch]);
faa9b3e7
TC
379 }
380 if (tempval < minval) {
0d3b936e 381 back = i;
faa9b3e7
TC
382 min = temp;
383 minval = tempval;
384 }
385 }
386
387 for (y = 0; y < ysize; ++y) {
388 for (x = 0; x < xsize; ++x) {
389 /* dividing by sz gives us the ability to do perspective
390 transforms */
391 sz = x * matrix[6] + y * matrix[7] + matrix[8];
392 if (abs(sz) > 0.0000001) {
393 sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
394 sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
395 }
396
397 /* anything outside these ranges is either a broken co-ordinate
398 or outside the source */
399 if (abs(sz) > 0.0000001
400 && sx >= -0.5 && sx < src->xsize-0.5
401 && sy >= -0.5 && sy < src->ysize-0.5) {
402
403 /* all the world's an integer */
404 ix = (int)(sx+0.5);
405 iy = (int)(sy+0.5);
0d3b936e
TC
406 if (!i_gpal(src, ix, ix+1, iy, vals+x))
407 vals[i] = back;
faa9b3e7
TC
408 }
409 else {
0d3b936e 410 vals[x] = back;
faa9b3e7
TC
411 }
412 }
413 i_ppal(result, 0, xsize, y, vals);
414 }
415 myfree(vals);
416 }
417
418 return result;
419}
420
97ac0a96 421i_img *i_matrix_transform(i_img *src, int xsize, int ysize, const double *matrix) {
0d3b936e
TC
422 return i_matrix_transform_bg(src, xsize, ysize, matrix, NULL, NULL);
423}
424
35891892 425static void
97ac0a96 426i_matrix_mult(double *dest, const double *left, const double *right) {
faa9b3e7
TC
427 int i, j, k;
428 double accum;
429
430 for (i = 0; i < 3; ++i) {
431 for (j = 0; j < 3; ++j) {
432 accum = 0.0;
433 for (k = 0; k < 3; ++k) {
434 accum += left[3*i+k] * right[3*k+j];
435 }
436 dest[3*i+j] = accum;
437 }
438 }
439}
440
0d3b936e 441i_img *i_rotate_exact_bg(i_img *src, double amount,
97ac0a96 442 const i_color *backp, const i_fcolor *fbackp) {
faa9b3e7
TC
443 double xlate1[9] = { 0 };
444 double rotate[9];
445 double xlate2[9] = { 0 };
446 double temp[9], matrix[9];
447 int x1, x2, y1, y2, newxsize, newysize;
448
449 /* first translate the centre of the image to (0,0) */
450 xlate1[0] = 1;
451 xlate1[2] = src->xsize/2.0;
452 xlate1[4] = 1;
453 xlate1[5] = src->ysize/2.0;
454 xlate1[8] = 1;
455
456 /* rotate around (0.0) */
457 rotate[0] = cos(amount);
458 rotate[1] = sin(amount);
459 rotate[2] = 0;
460 rotate[3] = -rotate[1];
461 rotate[4] = rotate[0];
462 rotate[5] = 0;
463 rotate[6] = 0;
464 rotate[7] = 0;
465 rotate[8] = 1;
466
467 x1 = ceil(abs(src->xsize * rotate[0] + src->ysize * rotate[1]));
468 x2 = ceil(abs(src->xsize * rotate[0] - src->ysize * rotate[1]));
469 y1 = ceil(abs(src->xsize * rotate[3] + src->ysize * rotate[4]));
470 y2 = ceil(abs(src->xsize * rotate[3] - src->ysize * rotate[4]));
471 newxsize = x1 > x2 ? x1 : x2;
472 newysize = y1 > y2 ? y1 : y2;
473 /* translate the centre back to the center of the image */
474 xlate2[0] = 1;
475 xlate2[2] = -newxsize/2;
476 xlate2[4] = 1;
477 xlate2[5] = -newysize/2;
478 xlate2[8] = 1;
479 i_matrix_mult(temp, xlate1, rotate);
480 i_matrix_mult(matrix, temp, xlate2);
481
0d3b936e 482 return i_matrix_transform_bg(src, newxsize, newysize, matrix, backp, fbackp);
faa9b3e7 483}
b8c2033e 484
0d3b936e
TC
485i_img *i_rotate_exact(i_img *src, double amount) {
486 return i_rotate_exact_bg(src, amount, NULL, NULL);
487}
488
489
b8c2033e
AMH
490/*
491=back
492
493=head1 AUTHOR
494
495Tony Cook <tony@develop-help.com>
496
497=head1 SEE ALSO
498
499Imager(3)
500
501=cut
502*/