From: Tony Cook Date: Mon, 12 Nov 2007 10:26:25 +0000 (+0000) Subject: alpha channel fixes for mixing scaling X-Git-Tag: Imager-0.63~54 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/874c55db7c148f8995a27beb75a5279c054c0ffe alpha channel fixes for mixing scaling --- diff --git a/Changes b/Changes index 74fb7e0d..d75e79e1 100644 --- 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 ec4a3a94..d6ba7629 100644 --- 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: diff --git a/imtoc.perl b/imtoc.perl index 0332de70..285cfce2 100644 --- a/imtoc.perl +++ b/imtoc.perl @@ -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 = )) { 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; diff --git a/scale.im b/scale.im index 7f8ed769..dbfb2d11 100644 --- 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); + } } } } diff --git a/t/t40scale.t b/t/t40scale.t index b6819f08..5658eb29 100644 --- a/t/t40scale.t +++ b/t/t40scale.t @@ -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) = @_;