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