]> git.imager.perl.org - imager.git/blob - rotate.c
Moved tag handling and general tags into ImageTypes and format
[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(i_img *src, int xsize, int ysize, double *matrix) {
182   i_img *result = i_sametype(src, xsize, ysize);
183   int x, y;
184   int ch;
185   int i, j;
186   double sx, sy, sz;
187   double out[3];
188
189   if (src->type == i_direct_type) {
190     if (src->bits == i_8_bits) {
191       i_color *vals = mymalloc(xsize * sizeof(i_color));
192       i_color black;
193
194       for (ch = 0; ch < src->channels; ++ch)
195         black.channel[ch] = 0;
196
197       for (y = 0; y < ysize; ++y) {
198         for (x = 0; x < xsize; ++x) {
199           /* dividing by sz gives us the ability to do perspective 
200              transforms */
201           sz = x * matrix[6] + y * matrix[7] + matrix[8];
202           if (abs(sz) > 0.0000001) {
203             sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
204             sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
205           }
206
207           /* anything outside these ranges is either a broken co-ordinate
208              or outside the source */
209           if (abs(sz) > 0.0000001 
210               && sx >= -1 && sx < src->xsize
211               && sy >= -1 && sy < src->ysize) {
212
213             if (sx != (int)sx) {
214               if (sy != (int)sy) {
215                 i_color c[2][2]; 
216                 i_color ci2[2];
217                 for (i = 0; i < 2; ++i)
218                   for (j = 0; j < 2; ++j)
219                     if (i_gpix(src, floor(sx)+i, floor(sy)+j, &c[j][i]))
220                       c[j][i] = black;
221                 for (j = 0; j < 2; ++j)
222                   ci2[j] = interp_i_color(c[j][0], c[j][1], sx, src->channels);
223                 vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels);
224               }
225               else {
226                 i_color ci2[2];
227                 for (i = 0; i < 2; ++i)
228                   if (i_gpix(src, floor(sx)+i, sy, ci2+i))
229                     ci2[i] = black;
230                 vals[x] = interp_i_color(ci2[0], ci2[1], sx, src->channels);
231               }
232             }
233             else {
234               if (sy != (int)sy) {
235                 i_color ci2[2];
236                 for (i = 0; i < 2; ++i)
237                   if (i_gpix(src, sx, floor(sy)+i, ci2+i))
238                     ci2[i] = black;
239                 vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels);
240               }
241               else {
242                 /* all the world's an integer */
243                 i_gpix(src, sx, sy, vals+x);
244               }
245             }
246           }
247           else {
248             vals[x] = black;
249           }
250         }
251         i_plin(result, 0, xsize, y, vals);
252       }
253       myfree(vals);
254     }
255     else {
256       i_fcolor *vals = mymalloc(xsize * sizeof(i_fcolor));
257       i_fcolor black;
258
259       for (ch = 0; ch < src->channels; ++ch)
260         black.channel[ch] = 0;
261
262       for (y = 0; y < ysize; ++y) {
263         for (x = 0; x < xsize; ++x) {
264           /* dividing by sz gives us the ability to do perspective 
265              transforms */
266           sz = x * matrix[6] + y * matrix[7] + matrix[8];
267           if (abs(sz) > 0.0000001) {
268             sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
269             sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
270           }
271
272           /* anything outside these ranges is either a broken co-ordinate
273              or outside the source */
274           if (abs(sz) > 0.0000001 
275               && sx >= -1 && sx < src->xsize
276               && sy >= -1 && sy < src->ysize) {
277
278             if (sx != (int)sx) {
279               if (sy != (int)sy) {
280                 i_fcolor c[2][2]; 
281                 i_fcolor ci2[2];
282                 for (i = 0; i < 2; ++i)
283                   for (j = 0; j < 2; ++j)
284                     if (i_gpixf(src, floor(sx)+i, floor(sy)+j, &c[j][i]))
285                       c[j][i] = black;
286                 for (j = 0; j < 2; ++j)
287                   ci2[j] = interp_i_fcolor(c[j][0], c[j][1], sx, src->channels);
288                 vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels);
289               }
290               else {
291                 i_fcolor ci2[2];
292                 for (i = 0; i < 2; ++i)
293                   if (i_gpixf(src, floor(sx)+i, sy, ci2+i))
294                     ci2[i] = black;
295                 vals[x] = interp_i_fcolor(ci2[0], ci2[1], sx, src->channels);
296               }
297             }
298             else {
299               if (sy != (int)sy) {
300                 i_fcolor ci2[2];
301                 for (i = 0; i < 2; ++i)
302                   if (i_gpixf(src, sx, floor(sy)+i, ci2+i))
303                     ci2[i] = black;
304                 vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels);
305               }
306               else {
307                 /* all the world's an integer */
308                 i_gpixf(src, sx, sy, vals+x);
309               }
310             }
311           }
312           else {
313             vals[x] = black;
314           }
315         }
316         i_plinf(result, 0, xsize, y, vals);
317       }
318       myfree(vals);
319     }
320   }
321   else {
322     /* don't interpolate for a palette based image */
323     i_palidx *vals = mymalloc(xsize * sizeof(i_palidx));
324     i_palidx black = 0;
325     i_color min;
326     int minval;
327     int ix, iy;
328     
329     i_getcolors(src, 0, &min, 1);
330     minval = 0;
331     for (ch = 0; ch < src->channels; ++ch) {
332       minval += min.channel[ch];
333     }
334
335     /* find the darkest color */
336     for (i = 1; i < i_colorcount(src); ++i) {
337       i_color temp;
338       int tempval;
339       i_getcolors(src, i, &temp, 1);
340       tempval = 0;
341       for (ch = 0; ch < src->channels; ++ch) {
342         tempval += temp.channel[ch];
343       }
344       if (tempval < minval) {
345         black = i;
346         min = temp;
347         minval = tempval;
348       }
349     }
350
351     for (y = 0; y < ysize; ++y) {
352       for (x = 0; x < xsize; ++x) {
353         /* dividing by sz gives us the ability to do perspective 
354            transforms */
355         sz = x * matrix[6] + y * matrix[7] + matrix[8];
356         if (abs(sz) > 0.0000001) {
357           sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
358           sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
359         }
360         
361         /* anything outside these ranges is either a broken co-ordinate
362            or outside the source */
363         if (abs(sz) > 0.0000001 
364             && sx >= -0.5 && sx < src->xsize-0.5
365             && sy >= -0.5 && sy < src->ysize-0.5) {
366           
367           /* all the world's an integer */
368           ix = (int)(sx+0.5);
369           iy = (int)(sy+0.5);
370           i_gpal(src, ix, ix+1, iy, vals+x);
371         }
372         else {
373           vals[x] = black;
374         }
375       }
376       i_ppal(result, 0, xsize, y, vals);
377     }
378     myfree(vals);
379   }
380
381   return result;
382 }
383
384 i_matrix_mult(double *dest, double *left, double *right) {
385   int i, j, k;
386   double accum;
387   
388   for (i = 0; i < 3; ++i) {
389     for (j = 0; j < 3; ++j) {
390       accum = 0.0;
391       for (k = 0; k < 3; ++k) {
392         accum += left[3*i+k] * right[3*k+j];
393       }
394       dest[3*i+j] = accum;
395     }
396   }
397 }
398
399 i_img *i_rotate_exact(i_img *src, double amount) {
400   double xlate1[9] = { 0 };
401   double rotate[9];
402   double xlate2[9] = { 0 };
403   double temp[9], matrix[9];
404   int x1, x2, y1, y2, newxsize, newysize;
405
406   /* first translate the centre of the image to (0,0) */
407   xlate1[0] = 1;
408   xlate1[2] = src->xsize/2.0;
409   xlate1[4] = 1;
410   xlate1[5] = src->ysize/2.0;
411   xlate1[8] = 1;
412
413   /* rotate around (0.0) */
414   rotate[0] = cos(amount);
415   rotate[1] = sin(amount);
416   rotate[2] = 0;
417   rotate[3] = -rotate[1];
418   rotate[4] = rotate[0];
419   rotate[5] = 0;
420   rotate[6] = 0;
421   rotate[7] = 0;
422   rotate[8] = 1;
423
424   x1 = ceil(abs(src->xsize * rotate[0] + src->ysize * rotate[1]));
425   x2 = ceil(abs(src->xsize * rotate[0] - src->ysize * rotate[1]));
426   y1 = ceil(abs(src->xsize * rotate[3] + src->ysize * rotate[4]));
427   y2 = ceil(abs(src->xsize * rotate[3] - src->ysize * rotate[4]));
428   newxsize = x1 > x2 ? x1 : x2;
429   newysize = y1 > y2 ? y1 : y2;
430   /* translate the centre back to the center of the image */
431   xlate2[0] = 1;
432   xlate2[2] = -newxsize/2;
433   xlate2[4] = 1;
434   xlate2[5] = -newysize/2;
435   xlate2[8] = 1;
436   i_matrix_mult(temp, xlate1, rotate);
437   i_matrix_mult(matrix, temp, xlate2);
438
439   return i_matrix_transform(src, newxsize, newysize, matrix);
440 }
441
442 /*
443 =back
444
445 =head1 AUTHOR
446
447 Tony Cook <tony@develop-help.com>
448
449 =head1 SEE ALSO
450
451 Imager(3)
452
453 =cut
454 */