avoid an unneeded check in the FT1 has_chars() method implementation
[imager.git] / scale.im
index 26ea790..5ed637b 100644 (file)
--- a/scale.im
+++ b/scale.im
@@ -1,4 +1,5 @@
 #include "imager.h"
+#include "imageri.h"
 
 /*
  * i_scale_mixing() is based on code contained in pnmscale.c, part of
 
 
 static void
-zero_row(i_fcolor *row, int width, int channels);
+zero_row(i_fcolor *row, i_img_dim width, int channels);
 
 #code
 static void
 IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
-                int width, int channels);
+                i_img_dim width, int channels);
 static void
-IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width, 
-                            i_fcolor const *in, int in_width,
+IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width, 
+                            i_fcolor const *in, i_img_dim in_width,
                             int channels);
 #/code
 
@@ -48,26 +49,27 @@ Adapted from pnmscale.
 =cut
 */
 i_img *
-i_scale_mixing(i_img *src, int x_out, int y_out) {
-  i_img *result;
+i_scale_mixing(i_img *src, i_img_dim x_out, i_img_dim y_out) {
+  i_img *result = NULL;
   i_fcolor *accum_row = NULL;
-  int y;
-  int accum_row_bytes;
+  i_img_dim x, y;
+  int ch;
+  size_t accum_row_bytes;
   double rowsleft, fracrowtofill;
-  int rowsread;
+  i_img_dim rowsread;
   double y_scale;
 
-  mm_log((1, "i_scale_mixing(src %p, x_out %d, y_out %d)\n", 
-         src, x_out, y_out));
+  mm_log((1, "i_scale_mixing(src %p, out(" i_DFp "))\n", 
+         src, i_DFcp(x_out, y_out)));
 
   i_clear_error();
 
   if (x_out <= 0) {
-    i_push_errorf(0, "output width %d invalid", x_out);
+    i_push_errorf(0, "output width %" i_DF " invalid", i_DFc(x_out));
     return NULL;
   }
   if (y_out <= 0) {
-    i_push_errorf(0, "output height %d invalid", y_out);
+    i_push_errorf(0, "output height %" i_DF " invalid", i_DFc(y_out));
     return NULL;
   }
 
@@ -77,30 +79,34 @@ i_scale_mixing(i_img *src, int x_out, int y_out) {
 
   y_scale = y_out / (double)src->ysize;
 
-  result = i_sametype_chans(src, x_out, y_out, src->channels);
-  if (!result)
-    return NULL;
-
   accum_row_bytes = sizeof(i_fcolor) * src->xsize;
   if (accum_row_bytes / sizeof(i_fcolor) != src->xsize) {
     i_push_error(0, "integer overflow allocating accumulator row buffer");
     return NULL;
   }
 
+  result = i_sametype_chans(src, x_out, y_out, src->channels);
+  if (!result)
+    return NULL;
+
   accum_row  = mymalloc(accum_row_bytes);
 
 #code src->bits <= 8
   IM_COLOR *in_row = NULL;
   IM_COLOR *xscale_row = NULL;
-  int in_row_bytes, out_row_bytes;
+  size_t in_row_bytes, out_row_bytes;
 
   in_row_bytes = sizeof(IM_COLOR) * src->xsize;
   if (in_row_bytes / sizeof(IM_COLOR) != src->xsize) {
+    myfree(accum_row);
+    i_img_destroy(result);
     i_push_error(0, "integer overflow allocating input row buffer");
     return NULL;
   }
   out_row_bytes = sizeof(IM_COLOR) * x_out;
   if (out_row_bytes / sizeof(IM_COLOR) != x_out) {
+    myfree(accum_row);
+    i_img_destroy(result);
     i_push_error(0, "integer overflow allocating output row buffer");
     return NULL;
   }
@@ -113,8 +119,9 @@ i_scale_mixing(i_img *src, int x_out, int y_out) {
   for (y = 0; y < y_out; ++y) {
     if (y_out == src->ysize) {
       /* no vertical scaling, just load it */
-      int x, ch;
 #ifdef IM_EIGHT_BIT
+      i_img_dim x;
+      int ch;
       /* load and convert to doubles */
       IM_GLIN(src, 0, src->xsize, y, in_row);
       for (x = 0; x < src->xsize; ++x) {
@@ -125,6 +132,15 @@ i_scale_mixing(i_img *src, int x_out, int y_out) {
 #else
       IM_GLIN(src, 0, src->xsize, y, accum_row);
 #endif
+      /* alpha adjust if needed */
+      if (src->channels == 2 || src->channels == 4) {
+       for (x = 0; x < src->xsize; ++x) {
+         for (ch = 0; ch < src->channels-1; ++ch) {
+           accum_row[x].channel[ch] *=
+             accum_row[x].channel[src->channels-1] / IM_SAMPLE_MAX;
+         }
+       }
+      }
     }
     else {
       fracrowtofill = 1.0;
@@ -155,12 +171,39 @@ i_scale_mixing(i_img *src, int x_out, int y_out) {
     }
     /* we've accumulated a vertically scaled row */
     if (x_out == src->xsize) {
-      int x, ch;
 #if IM_EIGHT_BIT
+      i_img_dim x;
+      int ch;
       /* no need to scale, but we need to convert it */
-      for (x = 0; x < x_out; ++x) {
-        for (ch = 0; ch < result->channels; ++ch)
-          xscale_row[x].channel[ch] = accum_row[x].channel[ch];
+      if (result->channels == 2 || result->channels == 4) {
+       int alpha_chan = result->channels - 1;
+       for (x = 0; x < x_out; ++x) {
+         double alpha = accum_row[x].channel[alpha_chan] / IM_SAMPLE_MAX;
+         if (alpha) {
+           for (ch = 0; ch < alpha_chan; ++ch) {
+             int val = accum_row[x].channel[ch] / alpha + 0.5;
+             xscale_row[x].channel[ch] = IM_LIMIT(val);
+           }
+         }
+         else {
+           /* rather than leaving any color data as whatever was
+              originally in the buffer, set it to black.  This isn't
+              any more correct, but it gives us more compressible
+              image data.
+              RT #32324
+           */
+           for (ch = 0; ch < alpha_chan; ++ch) {
+             xscale_row[x].channel[ch] = 0;
+           }
+         }
+         xscale_row[x].channel[alpha_chan] = IM_LIMIT(accum_row[x].channel[alpha_chan]+0.5);
+       }
+      }
+      else {
+       for (x = 0; x < x_out; ++x) {
+         for (ch = 0; ch < result->channels; ++ch)
+           xscale_row[x].channel[ch] = IM_LIMIT(accum_row[x].channel[ch]+0.5);
+       }
       }
       IM_PLIN(result, 0, x_out, y, xscale_row);
 #else
@@ -182,8 +225,8 @@ i_scale_mixing(i_img *src, int x_out, int y_out) {
 }
 
 static void
-zero_row(i_fcolor *row, int width, int channels) {
-  int x;
+zero_row(i_fcolor *row, i_img_dim width, int channels) {
+  i_img_dim x;
   int ch;
 
   /* with IEEE floats we could just use memset() but that's not
@@ -200,25 +243,36 @@ zero_row(i_fcolor *row, int width, int channels) {
 
 static void
 IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
-                int width, int channels) {
-  int x, ch;
+                i_img_dim width, int channels) {
+  i_img_dim x;
+  int ch;
 
   /* it's tempting to change this into a pointer iteration loop but
      modern CPUs do the indexing as part of the instruction */
-  for (x = 0; x < width; ++x) {
-    for (ch = 0; ch < channels; ++ch) {
-      accum[x].channel[ch] += in[x].channel[ch] * fraction;
+  if (channels == 2 || channels == 4) {
+    for (x = 0; x < width; ++x) {
+      for (ch = 0; ch < channels-1; ++ch) {
+       accum[x].channel[ch] += in[x].channel[ch] * fraction * in[x].channel[channels-1] / IM_SAMPLE_MAX;
+      }
+      accum[x].channel[channels-1] += in[x].channel[channels-1] * fraction;
+    }
+  }
+  else {
+    for (x = 0; x < width; ++x) {
+      for (ch = 0; ch < channels; ++ch) {
+       accum[x].channel[ch] += in[x].channel[ch] * fraction;
+      }
     }
   }
 }
 
 static void
-IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width, 
-                i_fcolor const *in, int in_width,
+IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width, 
+                i_fcolor const *in, i_img_dim in_width,
                 int channels) {
   double frac_col_to_fill, frac_col_left;
-  int in_x;
-  int out_x;
+  i_img_dim in_x;
+  i_img_dim out_x;
   double x_scale = (double)out_width / in_width;
   int ch;
   double accum[MAXCHANNELS] = { 0 };
@@ -231,10 +285,31 @@ IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width,
       for (ch = 0; ch < channels; ++ch)
        accum[ch] += frac_col_to_fill * in[in_x].channel[ch];
 
-      for (ch = 0; ch < channels; ++ch) {
-       out[out_x].channel[ch] = accum[ch];
-       accum[ch] = 0;
+      if (channels == 2 || channels == 4) {
+       int alpha_chan = channels - 1;
+       double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
+       if (alpha) {
+         for (ch = 0; ch < alpha_chan; ++ch) {
+           IM_WORK_T val = IM_ROUND(accum[ch] / alpha);
+           out[out_x].channel[ch] = IM_LIMIT(val);
+         }
+       }
+       else {
+         for (ch = 0; ch < alpha_chan; ++ch) {
+           /* See RT #32324 (and mention above) */
+           out[out_x].channel[ch] = 0;
+         }
+       }
+       out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
+      }
+      else {
+       for (ch = 0; ch < channels; ++ch) {
+         IM_WORK_T val = IM_ROUND(accum[ch]);
+         out[out_x].channel[ch] = IM_LIMIT(val);
+       }
       }
+      for (ch = 0; ch < channels; ++ch)
+       accum[ch] = 0;
       frac_col_left -= frac_col_to_fill;
       frac_col_to_fill = 1.0;
       ++out_x;
@@ -255,7 +330,29 @@ IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width,
   if (out_x < out_width) {
     for (ch = 0; ch < channels; ++ch) {
       accum[ch] += frac_col_to_fill * in[in_width-1].channel[ch];
-      out[out_x].channel[ch] = accum[ch];
+    }
+    if (channels == 2 || channels == 4) {
+      int alpha_chan = channels - 1;
+      double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
+      if (alpha) {
+       for (ch = 0; ch < alpha_chan; ++ch) {
+         IM_WORK_T val = IM_ROUND(accum[ch] / alpha);
+         out[out_x].channel[ch] = IM_LIMIT(val);
+       }
+      }
+      else {
+       for (ch = 0; ch < alpha_chan; ++ch) {
+         /* See RT #32324 (and mention above) */
+         out[out_x].channel[ch] = 0;
+       }
+      }
+      out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
+    }
+    else {
+      for (ch = 0; ch < channels; ++ch) {
+       IM_WORK_T val = IM_ROUND(accum[ch]);
+       out[out_x].channel[ch] = IM_LIMIT(val);
+      }
     }
   }
 }