File Coverage

rotate.im
Criterion Covered Total %
statement 205 237 86.5
branch 166 238 69.7
condition n/a
subroutine n/a
pod n/a
total 371 475 78.1


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