- correctly blend a rotated (or matrix_transformed()) image when
authorTony Cook <tony@develop=help.com>
Mon, 5 Nov 2007 03:02:51 +0000 (03:02 +0000)
committerTony Cook <tony@develop=help.com>
Mon, 5 Nov 2007 03:02:51 +0000 (03:02 +0000)
   performing interpolation in the presence of an alpha channel.

Changes
imageri.h
rotate.c
t/t64copyflip.t

diff --git a/Changes b/Changes
index 803fd98..bab343f 100644 (file)
--- a/Changes
+++ b/Changes
@@ -58,6 +58,9 @@ Bug fixes:
  - correctly generate the author key in META.yml
    http://rt.cpan.org/Ticket/Display.html?id=30377
 
+ - correctly blend a rotated (or matrix_transformed()) image when
+   performing interpolation in the presence of an alpha channel.
+
 Imager 0.60 - 30 August 2007
 ===========
 
index 98cfa2c..df471cc 100644 (file)
--- a/imageri.h
+++ b/imageri.h
@@ -90,4 +90,7 @@ extern void i_int_hlines_destroy(i_int_hlines *hlines);
 extern void i_int_hlines_fill_color(i_img *im, i_int_hlines *hlines, const i_color *val);
 extern void i_int_hlines_fill_fill(i_img *im, i_int_hlines *hlines, i_fill_t *fill);
 
+#define I_LIMIT_8(x) ((x) < 0 ? 0 : (x) > 255 ? 255 : (x))
+#define I_LIMIT_DOUBLE(x) ((x) < 0.0 ? 0.0 : (x) > 1.0 ? 1.0 : (x))
+
 #endif
index 5beba66..bec2b8a 100644 (file)
--- a/rotate.c
+++ b/rotate.c
@@ -17,6 +17,7 @@ Other rotations will be added as tuits become available.
 */
 
 #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,8 +194,30 @@ 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;
 }
@@ -478,9 +522,9 @@ i_img *i_rotate_exact_bg(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);
index 330d766..bc9a65d 100644 (file)
@@ -1,7 +1,8 @@
 #!perl -w
 use strict;
-use Test::More tests => 62;
+use Test::More tests => 65;
 use Imager;
+use Imager::Test qw(is_color3);
 
 #$Imager::DEBUG=1;
 
@@ -170,3 +171,18 @@ sub rot_test {
   my $diff = Imager::i_img_diff($out->{IMG}, $blank->{IMG});
   ok($diff, "RT#29936 - check non-blank output");
 }
+
+{
+  my $im = Imager->new(xsize => 10, ysize => 10, channels => 4);
+  $im->box(filled => 1, color => 'FF0000');
+  my $back = Imager::Color->new(0, 0, 0, 0);
+  my $rot = $im->rotate(degrees => 10, back => $back);
+  # drop the alpha and make sure there's only 2 colors used
+  my $work = $rot->convert(preset => 'noalpha');
+  my $im_pal = $work->to_paletted(make_colors => 'mediancut');
+  my @colors = $im_pal->getcolors;
+  is(@colors, 2, "should be only 2 colors");
+  @colors = sort { ($a->rgba)[0] <=> ($b->rgba)[0] } @colors;
+  is_color3($colors[0], 0, 0, 0, "check we got black");
+  is_color3($colors[1], 255, 0, 0, "and red");
+}