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