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