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