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 | ||
92bda632 | 19 | #include "imager.h" |
13c9a303 | 20 | #include "imageri.h" |
faa9b3e7 TC |
21 | #include <math.h> /* for floor() */ |
22 | ||
23 | i_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) { | |
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) { | |
8d14daab TC |
83 | i_img_dim tx, txstart, txinc; |
84 | i_img_dim ty, tystart, tyinc; | |
faa9b3e7 TC |
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 */ |
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); | |
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 */ | |
191 | static 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 | ||
8d14daab | 225 | i_img *i_matrix_transform_bg(i_img *src, i_img_dim xsize, i_img_dim ysize, const double *matrix, |
97ac0a96 | 226 | const i_color *backp, const i_fcolor *fbackp) { |
faa9b3e7 | 227 | i_img *result = i_sametype(src, xsize, ysize); |
8d14daab | 228 | i_img_dim x, y; |
faa9b3e7 | 229 | int ch; |
8d14daab | 230 | i_img_dim i, j; |
faa9b3e7 | 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 | ||
8d14daab TC |
272 | if (sx != (i_img_dim)sx) { |
273 | if (sy != (i_img_dim)sy) { | |
faa9b3e7 TC |
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 { | |
8d14daab | 293 | if (sy != (i_img_dim)sy) { |
faa9b3e7 TC |
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 | ||
8d14daab TC |
350 | if (sx != (i_img_dim)sx) { |
351 | if (sy != (i_img_dim)sy) { | |
faa9b3e7 TC |
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 { | |
8d14daab | 371 | if (sy != (i_img_dim)sy) { |
faa9b3e7 TC |
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; |
8d14daab | 400 | i_img_dim 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 */ | |
8d14daab TC |
454 | ix = (i_img_dim)(sx+0.5); |
455 | iy = (i_img_dim)(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 | ||
8d14daab | 471 | i_img *i_matrix_transform(i_img *src, i_img_dim xsize, i_img_dim ysize, const double *matrix) { |
0d3b936e TC |
472 | return i_matrix_transform_bg(src, xsize, ysize, matrix, NULL, NULL); |
473 | } | |
474 | ||
35891892 | 475 | static void |
97ac0a96 | 476 | i_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 | 491 | i_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]; | |
8d14daab | 497 | i_img_dim x1, x2, y1, y2, newxsize, newysize; |
faa9b3e7 TC |
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 | ||
8d14daab TC |
517 | x1 = ceil(i_abs(src->xsize * rotate[0] + src->ysize * rotate[1])); |
518 | x2 = ceil(i_abs(src->xsize * rotate[0] - src->ysize * rotate[1])); | |
519 | y1 = ceil(i_abs(src->xsize * rotate[3] + src->ysize * rotate[4])); | |
520 | y2 = ceil(i_abs(src->xsize * rotate[3] - src->ysize * rotate[4])); | |
faa9b3e7 TC |
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 |
535 | i_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 | ||
545 | Tony Cook <tony@develop-help.com> | |
546 | ||
547 | =head1 SEE ALSO | |
548 | ||
549 | Imager(3) | |
550 | ||
551 | =cut | |
552 | */ |