]> git.imager.perl.org - imager.git/blobdiff - rotate.c
separate no png support tests into a separate test file to avoid the
[imager.git] / rotate.c
index 21ca27a127e23259c049c84937f1ecd6ec7f127b..bec2b8a647bad7e6bcbecbbb15c04a43b3fd8832 100644 (file)
--- a/rotate.c
+++ b/rotate.c
@@ -16,7 +16,8 @@ Other rotations will be added as tuits become available.
 =cut
 */
 
-#include "image.h"
+#include "imager.h"
+#include "imageri.h"
 #include <math.h> /* for floor() */
 
 i_img *i_rotate90(i_img *src, int degrees) {
@@ -150,7 +151,6 @@ i_img *i_rotate90(i_img *src, int degrees) {
   }
 }
 
-/* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
 /* linear interpolation */
 static i_color interp_i_color(i_color before, i_color after, double pos,
                               int channels) {
@@ -158,8 +158,30 @@ static i_color interp_i_color(i_color before, i_color after, double pos,
   int ch;
 
   pos -= floor(pos);
-  for (ch = 0; ch < channels; ++ch)
-    out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
+  if (channels == 1 || channels == 3) {
+    for (ch = 0; ch < channels; ++ch)
+      out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
+  }
+  else {
+    int total_cover = (1-pos) * before.channel[channels-1]
+      + pos * after.channel[channels-1];
+
+    total_cover = I_LIMIT_8(total_cover);
+    if (total_cover) {
+      double before_alpha = before.channel[channels-1] / 255.0;
+      double after_alpha = after.channel[channels-1] / 255.0;
+      double total_alpha = before_alpha * (1-pos) + after_alpha * pos;
+
+      for (ch = 0; ch < channels-1; ++ch) {
+       int out_level = ((1-pos) * before.channel[ch] * before_alpha + 
+                        pos * after.channel[ch] * after_alpha + 0.5) / total_alpha;
+
+       out.channel[ch] = I_LIMIT_8(out_level);
+      }
+    }
+
+    out.channel[channels-1] = total_cover;
+  }
 
   return out;
 }
@@ -172,41 +194,78 @@ static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
   int ch;
 
   pos -= floor(pos);
-  for (ch = 0; ch < channels; ++ch)
-    out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
+  if (channels == 1 || channels == 3) {
+    for (ch = 0; ch < channels; ++ch)
+      out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
+  }
+  else {
+    double total_cover = (1-pos) * before.channel[channels-1]
+      + pos * after.channel[channels-1];
+
+    total_cover = I_LIMIT_DOUBLE(total_cover);
+    if (total_cover) {
+      double before_alpha = before.channel[channels-1];
+      double after_alpha = after.channel[channels-1];
+      double total_alpha = before_alpha * (1-pos) + after_alpha * pos;
+
+      for (ch = 0; ch < channels-1; ++ch) {
+       double out_level = ((1-pos) * before.channel[ch] * before_alpha + 
+                        pos * after.channel[ch] * after_alpha) / total_alpha;
+
+       out.channel[ch] = I_LIMIT_DOUBLE(out_level);
+      }
+    }
+
+    out.channel[channels-1] = total_cover;
+  }
 
   return out;
 }
 
-i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
+i_img *i_matrix_transform_bg(i_img *src, int xsize, int ysize, const double *matrix,
+                            const i_color *backp, const i_fcolor *fbackp) {
   i_img *result = i_sametype(src, xsize, ysize);
   int x, y;
   int ch;
   int i, j;
   double sx, sy, sz;
-  double out[3];
 
   if (src->type == i_direct_type) {
     if (src->bits == i_8_bits) {
       i_color *vals = mymalloc(xsize * sizeof(i_color));
-      i_color black;
+      i_color back;
+      i_fsample_t fsamp;
 
-      for (ch = 0; ch < src->channels; ++ch)
-        black.channel[ch] = 0;
+      if (backp) {
+       back = *backp;
+      }
+      else if (fbackp) {
+       for (ch = 0; ch < src->channels; ++ch) {
+         fsamp = fbackp->channel[ch];
+         back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255;
+       }
+      }
+      else {
+       for (ch = 0; ch < src->channels; ++ch)
+         back.channel[ch] = 0;
+      }
 
       for (y = 0; y < ysize; ++y) {
         for (x = 0; x < xsize; ++x) {
           /* dividing by sz gives us the ability to do perspective 
              transforms */
           sz = x * matrix[6] + y * matrix[7] + matrix[8];
-          if (abs(sz) > 0.0000001) {
+          if (fabs(sz) > 0.0000001) {
             sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
             sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
           }
+         else {
+           sx = sy = 0;
+         }
 
           /* anything outside these ranges is either a broken co-ordinate
              or outside the source */
-          if (abs(sz) > 0.0000001 
+          if (fabs(sz) > 0.0000001 
               && sx >= -1 && sx < src->xsize
               && sy >= -1 && sy < src->ysize) {
 
@@ -217,7 +276,7 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
                 for (i = 0; i < 2; ++i)
                   for (j = 0; j < 2; ++j)
                     if (i_gpix(src, floor(sx)+i, floor(sy)+j, &c[j][i]))
-                      c[j][i] = black;
+                      c[j][i] = back;
                 for (j = 0; j < 2; ++j)
                   ci2[j] = interp_i_color(c[j][0], c[j][1], sx, src->channels);
                 vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels);
@@ -226,7 +285,7 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
                 i_color ci2[2];
                 for (i = 0; i < 2; ++i)
                   if (i_gpix(src, floor(sx)+i, sy, ci2+i))
-                    ci2[i] = black;
+                    ci2[i] = back;
                 vals[x] = interp_i_color(ci2[0], ci2[1], sx, src->channels);
               }
             }
@@ -235,17 +294,18 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
                 i_color ci2[2];
                 for (i = 0; i < 2; ++i)
                   if (i_gpix(src, sx, floor(sy)+i, ci2+i))
-                    ci2[i] = black;
+                    ci2[i] = back;
                 vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels);
               }
               else {
                 /* all the world's an integer */
-                i_gpix(src, sx, sy, vals+x);
+                if (i_gpix(src, sx, sy, vals+x))
+                 vals[x] = back;
               }
             }
           }
           else {
-            vals[x] = black;
+            vals[x] = back;
           }
         }
         i_plin(result, 0, xsize, y, vals);
@@ -254,24 +314,36 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
     }
     else {
       i_fcolor *vals = mymalloc(xsize * sizeof(i_fcolor));
-      i_fcolor black;
+      i_fcolor back;
 
-      for (ch = 0; ch < src->channels; ++ch)
-        black.channel[ch] = 0;
+      if (fbackp) {
+       back = *fbackp;
+      }
+      else if (backp) {
+       for (ch = 0; ch < src->channels; ++ch)
+         back.channel[ch] = backp->channel[ch] / 255.0;
+      }
+      else {
+       for (ch = 0; ch < src->channels; ++ch)
+         back.channel[ch] = 0;
+      }
 
       for (y = 0; y < ysize; ++y) {
         for (x = 0; x < xsize; ++x) {
           /* dividing by sz gives us the ability to do perspective 
              transforms */
           sz = x * matrix[6] + y * matrix[7] + matrix[8];
-          if (abs(sz) > 0.0000001) {
+          if (fabs(sz) > 0.0000001) {
             sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
             sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
           }
+          else {
+            sx = sy = 0;
+          }
 
           /* anything outside these ranges is either a broken co-ordinate
              or outside the source */
-          if (abs(sz) > 0.0000001 
+          if (fabs(sz) > 0.0000001 
               && sx >= -1 && sx < src->xsize
               && sy >= -1 && sy < src->ysize) {
 
@@ -282,7 +354,7 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
                 for (i = 0; i < 2; ++i)
                   for (j = 0; j < 2; ++j)
                     if (i_gpixf(src, floor(sx)+i, floor(sy)+j, &c[j][i]))
-                      c[j][i] = black;
+                      c[j][i] = back;
                 for (j = 0; j < 2; ++j)
                   ci2[j] = interp_i_fcolor(c[j][0], c[j][1], sx, src->channels);
                 vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels);
@@ -291,7 +363,7 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
                 i_fcolor ci2[2];
                 for (i = 0; i < 2; ++i)
                   if (i_gpixf(src, floor(sx)+i, sy, ci2+i))
-                    ci2[i] = black;
+                    ci2[i] = back;
                 vals[x] = interp_i_fcolor(ci2[0], ci2[1], sx, src->channels);
               }
             }
@@ -300,17 +372,18 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
                 i_fcolor ci2[2];
                 for (i = 0; i < 2; ++i)
                   if (i_gpixf(src, sx, floor(sy)+i, ci2+i))
-                    ci2[i] = black;
+                    ci2[i] = back;
                 vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels);
               }
               else {
                 /* all the world's an integer */
-                i_gpixf(src, sx, sy, vals+x);
+                if (i_gpixf(src, sx, sy, vals+x)) 
+                 vals[x] = back;
               }
             }
           }
           else {
-            vals[x] = black;
+            vals[x] = back;
           }
         }
         i_plinf(result, 0, xsize, y, vals);
@@ -321,28 +394,38 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
   else {
     /* don't interpolate for a palette based image */
     i_palidx *vals = mymalloc(xsize * sizeof(i_palidx));
-    i_palidx black = 0;
+    i_palidx back = 0;
     i_color min;
-    int minval;
+    int minval = 256 * 4;
     int ix, iy;
-    
-    i_getcolors(src, 0, &min, 1);
-    minval = 0;
-    for (ch = 0; ch < src->channels; ++ch) {
-      minval += min.channel[ch];
-    }
+    i_color want_back;
+    i_fsample_t fsamp;
 
-    /* find the darkest color */
-    for (i = 1; i < i_colorcount(src); ++i) {
+    if (backp) {
+      want_back = *backp;
+    }
+    else if (fbackp) {
+      for (ch = 0; ch < src->channels; ++ch) {
+       fsamp = fbackp->channel[ch];
+       want_back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255;
+      }
+    }
+    else {
+      for (ch = 0; ch < src->channels; ++ch)
+       want_back.channel[ch] = 0;
+    }
+    
+    /* find the closest color */
+    for (i = 0; i < i_colorcount(src); ++i) {
       i_color temp;
       int tempval;
       i_getcolors(src, i, &temp, 1);
       tempval = 0;
       for (ch = 0; ch < src->channels; ++ch) {
-        tempval += temp.channel[ch];
+        tempval += abs(want_back.channel[ch] - temp.channel[ch]);
       }
       if (tempval < minval) {
-        black = i;
+        back = i;
         min = temp;
         minval = tempval;
       }
@@ -357,6 +440,9 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
           sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
           sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
         }
+        else {
+          sx = sy = 0;
+        }
         
         /* anything outside these ranges is either a broken co-ordinate
            or outside the source */
@@ -367,10 +453,11 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
           /* all the world's an integer */
           ix = (int)(sx+0.5);
           iy = (int)(sy+0.5);
-          i_gpal(src, ix, ix+1, iy, vals+x);
+          if (!i_gpal(src, ix, ix+1, iy, vals+x))
+           vals[i] = back;
         }
         else {
-          vals[x] = black;
+          vals[x] = back;
         }
       }
       i_ppal(result, 0, xsize, y, vals);
@@ -381,7 +468,12 @@ i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
   return result;
 }
 
-i_matrix_mult(double *dest, double *left, double *right) {
+i_img *i_matrix_transform(i_img *src, int xsize, int ysize, const double *matrix) {
+  return i_matrix_transform_bg(src, xsize, ysize, matrix, NULL, NULL);
+}
+
+static void
+i_matrix_mult(double *dest, const double *left, const double *right) {
   int i, j, k;
   double accum;
   
@@ -396,7 +488,8 @@ i_matrix_mult(double *dest, double *left, double *right) {
   }
 }
 
-i_img *i_rotate_exact(i_img *src, double amount) {
+i_img *i_rotate_exact_bg(i_img *src, double amount, 
+                        const i_color *backp, const i_fcolor *fbackp) {
   double xlate1[9] = { 0 };
   double rotate[9];
   double xlate2[9] = { 0 };
@@ -429,16 +522,21 @@ i_img *i_rotate_exact(i_img *src, double amount) {
   newysize = y1 > y2 ? y1 : y2;
   /* translate the centre back to the center of the image */
   xlate2[0] = 1;
-  xlate2[2] = -newxsize/2;
+  xlate2[2] = -newxsize/2.0;
   xlate2[4] = 1;
-  xlate2[5] = -newysize/2;
+  xlate2[5] = -newysize/2.0;
   xlate2[8] = 1;
   i_matrix_mult(temp, xlate1, rotate);
   i_matrix_mult(matrix, temp, xlate2);
 
-  return i_matrix_transform(src, newxsize, newysize, matrix);
+  return i_matrix_transform_bg(src, newxsize, newysize, matrix, backp, fbackp);
+}
+
+i_img *i_rotate_exact(i_img *src, double amount) {
+  return i_rotate_exact_bg(src, amount, NULL, NULL);
 }
 
+
 /*
 =back