alpha channel fixes for mixing scaling
authorTony Cook <tony@develop=help.com>
Mon, 12 Nov 2007 10:26:25 +0000 (10:26 +0000)
committerTony Cook <tony@develop=help.com>
Mon, 12 Nov 2007 10:26:25 +0000 (10:26 +0000)
Changes
TODO
imtoc.perl
scale.im
t/t40scale.t

diff --git a/Changes b/Changes
index 74fb7e0..d75e79e 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,15 @@
 Imager release history.  Older releases can be found in Changes.old
 
+Imager 0.62 - not yet release
+
+Bug fixes:
+
+ - Imager::Fountain couldn't read GIMP gradient files with 10 or more
+   segments
+
+ - the scale() method with qtype mixing now handles images with an
+   alpha channel correctly.
+
 Imager 0.61 - 5 November 2007
 ===========
 
diff --git a/TODO b/TODO
index ec4a3a9..d6ba762 100644 (file)
--- a/TODO
+++ b/TODO
@@ -30,7 +30,7 @@ both contig and non-contiguous
 
 Makefile.PL --incpath and --libpath should expand ~/ paths (#29484)
 
-scaling alpha channel image issues
+scaling alpha channel image issues (done)
 
 For 0.61:
 
index 0332de7..285cfce 100644 (file)
@@ -16,7 +16,12 @@ my $code_line;
 my @out;
 my $failed;
 
-push @out, "#line 1 \"$src\"\n";
+push @out, 
+  "#define IM_ROUND_8(x) ((int)((x)+0.5))\n",
+  "#define IM_ROUND_double(x) (x)\n",
+  "#define IM_LIMIT_8(x) ((x) < 0 ? 0 : (x) > 255 ? 255 : (x))\n",
+  "#define IM_LIMIT_double(x) ((x) < 0.0 ? 0.0 : (x) > 1.0 ? 1.0 : (x))\n",
+  "#line 1 \"$src\"\n";
 while (defined(my $line = <SRC>)) {
   if ($line =~ /^\#code\s+(\S.+)$/) {
     $save_code
@@ -99,6 +104,8 @@ sub byte_samples {
     s/\bIM_Sf\b/"%d"/g;
     s/\bIM_Wf\b/"%d"/g;
     s/\bIM_SUFFIX\((\w+)\)/$1_8/g;
+    s/\bIM_ROUND\(/IM_ROUND_8(/g;
+    s/\bIM_LIMIT\(/IM_LIMIT_8(/g;
   }
 
   @lines;
@@ -121,6 +128,8 @@ sub double_samples {
     s/\bIM_Sf\b/"%f"/g;
     s/\bIM_Wf\b/"%f"/g;
     s/\bIM_SUFFIX\((\w+)\)/$1_double/g;
+    s/\bIM_ROUND\(/IM_ROUND_double(/g;
+    s/\bIM_LIMIT\(/IM_LIMIT_double(/g;
   }
 
   @lines;
index 7f8ed76..dbfb2d1 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
@@ -51,7 +52,7 @@ i_img *
 i_scale_mixing(i_img *src, int x_out, int y_out) {
   i_img *result;
   i_fcolor *accum_row = NULL;
-  int y;
+  int x, y, ch;
   int accum_row_bytes;
   double rowsleft, fracrowtofill;
   int rowsread;
@@ -125,6 +126,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;
@@ -158,9 +168,24 @@ i_scale_mixing(i_img *src, int x_out, int y_out) {
 #if IM_EIGHT_BIT
       int x, 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);
+           }
+         }
+         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
@@ -205,9 +230,19 @@ IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in
 
   /* 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;
+      }
     }
   }
 }
@@ -231,10 +266,25 @@ 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);
+         }
+       }
+       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 +305,23 @@ 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);
+       }
+      }
+      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);
+      }
     }
   }
 }
index b6819f0..5658eb2 100644 (file)
@@ -1,10 +1,11 @@
 #!perl -w
 use strict;
-use Test::More tests => 223;
+use Test::More tests => 224;
 
 BEGIN { use_ok(Imager=>':all') }
+use Imager::Test qw(is_image);
 
-require "t/testtools.pl";
+#require "t/testtools.pl";
 
 Imager::init('log'=>'testout/t40scale.log');
 my $img=Imager->new();
@@ -181,6 +182,20 @@ SKIP:
              pixels => 144);
 }
 
+{ # check proper alpha handling
+  my $im = Imager->new(xsize => 40, ysize => 40, channels => 4);
+  $im->box(filled => 1, color => 'C0C0C0');
+  my $rot = $im->rotate(degrees => -4)
+    or die;
+  $rot = $rot->to_rgb16;
+  my $sc = $rot->scale(qtype => 'mixing', xpixels => 40);
+  my $out = Imager->new(xsize => $sc->getwidth, ysize => $sc->getheight);
+  $out->box(filled => 1, color => 'C0C0C0');
+  my $cmp = $out->copy;
+  $out->rubthrough(src => $sc);
+  is_image($out, $cmp, "check we get the right image after scaling");
+}
+
 sub scale_test {
   my ($in, $method, $exp_width, $exp_height, $note, @parms) = @_;