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