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