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
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;
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;
#include "imager.h"
+#include "imageri.h"
/*
* i_scale_mixing() is based on code contained in pnmscale.c, part of
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;
#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;
#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
/* 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;
+ }
}
}
}
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;
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);
+ }
}
}
}
#!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();
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) = @_;