From: Tony Cook Date: Mon, 5 Nov 2007 03:02:51 +0000 (+0000) Subject: - correctly blend a rotated (or matrix_transformed()) image when X-Git-Tag: Imager-0.61~2 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/13c9a303a58f5b7a628995b4c8fa1c3a184a4123 - correctly blend a rotated (or matrix_transformed()) image when performing interpolation in the presence of an alpha channel. --- diff --git a/Changes b/Changes index 803fd98a..bab343f9 100644 --- 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 =========== diff --git a/imageri.h b/imageri.h index 98cfa2c9..df471cc8 100644 --- 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 diff --git a/rotate.c b/rotate.c index 5beba668..bec2b8a6 100644 --- 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 /* 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); diff --git a/t/t64copyflip.t b/t/t64copyflip.t index 330d7664..bc9a65d0 100644 --- a/t/t64copyflip.t +++ b/t/t64copyflip.t @@ -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"); +}