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