#include "imager.h"
+#include "imageri.h"
/*
* i_scale_mixing() is based on code contained in pnmscale.c, part of
static void
-zero_row(i_fcolor *row, int width, int channels);
+zero_row(i_fcolor *row, i_img_dim width, int channels);
#code
static void
IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
- int width, int channels);
+ i_img_dim width, int channels);
static void
-IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width,
- i_fcolor const *in, int in_width,
+IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width,
+ i_fcolor const *in, i_img_dim in_width,
int channels);
#/code
=cut
*/
i_img *
-i_scale_mixing(i_img *src, int x_out, int y_out) {
+i_scale_mixing(i_img *src, i_img_dim x_out, i_img_dim y_out) {
i_img *result;
i_fcolor *accum_row = NULL;
- int y;
- int accum_row_bytes;
+ i_img_dim x, y;
+ int ch;
+ size_t accum_row_bytes;
double rowsleft, fracrowtofill;
- int rowsread;
+ i_img_dim rowsread;
double y_scale;
- mm_log((1, "i_scale_mixing(src %p, x_out %d, y_out %d)\n",
- src, x_out, y_out));
+ mm_log((1, "i_scale_mixing(src %p, out(" i_DFp "))\n",
+ src, i_DFcp(x_out, y_out)));
i_clear_error();
if (x_out <= 0) {
- i_push_errorf(0, "output width %d invalid", x_out);
+ i_push_errorf(0, "output width %" i_DF " invalid", i_DFc(x_out));
return NULL;
}
if (y_out <= 0) {
- i_push_errorf(0, "output height %d invalid", y_out);
+ i_push_errorf(0, "output height %" i_DF " invalid", i_DFc(y_out));
return NULL;
}
#code src->bits <= 8
IM_COLOR *in_row = NULL;
IM_COLOR *xscale_row = NULL;
- int in_row_bytes, out_row_bytes;
+ size_t in_row_bytes, out_row_bytes;
in_row_bytes = sizeof(IM_COLOR) * src->xsize;
if (in_row_bytes / sizeof(IM_COLOR) != src->xsize) {
for (y = 0; y < y_out; ++y) {
if (y_out == src->ysize) {
/* no vertical scaling, just load it */
- int x, ch;
#ifdef IM_EIGHT_BIT
+ i_img_dim x;
+ int ch;
/* load and convert to doubles */
IM_GLIN(src, 0, src->xsize, y, in_row);
for (x = 0; x < src->xsize; ++x) {
#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;
}
/* we've accumulated a vertically scaled row */
if (x_out == src->xsize) {
- int x, ch;
#if IM_EIGHT_BIT
+ i_img_dim x;
+ int 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);
+ }
+ }
+ else {
+ /* rather than leaving any color data as whatever was
+ originally in the buffer, set it to black. This isn't
+ any more correct, but it gives us more compressible
+ image data.
+ RT #32324
+ */
+ for (ch = 0; ch < alpha_chan; ++ch) {
+ xscale_row[x].channel[ch] = 0;
+ }
+ }
+ 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
}
static void
-zero_row(i_fcolor *row, int width, int channels) {
- int x;
+zero_row(i_fcolor *row, i_img_dim width, int channels) {
+ i_img_dim x;
int ch;
/* with IEEE floats we could just use memset() but that's not
static void
IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
- int width, int channels) {
- int x, ch;
+ i_img_dim width, int channels) {
+ i_img_dim x;
+ int ch;
/* 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;
+ }
}
}
}
static void
-IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width,
- i_fcolor const *in, int in_width,
+IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width,
+ i_fcolor const *in, i_img_dim in_width,
int channels) {
double frac_col_to_fill, frac_col_left;
- int in_x;
- int out_x;
+ i_img_dim in_x;
+ i_img_dim out_x;
double x_scale = (double)out_width / in_width;
int ch;
double accum[MAXCHANNELS] = { 0 };
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);
+ }
+ }
+ else {
+ for (ch = 0; ch < alpha_chan; ++ch) {
+ /* See RT #32324 (and mention above) */
+ out[out_x].channel[ch] = 0;
+ }
+ }
+ 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);
+ }
+ }
+ else {
+ for (ch = 0; ch < alpha_chan; ++ch) {
+ /* See RT #32324 (and mention above) */
+ out[out_x].channel[ch] = 0;
+ }
+ }
+ 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);
+ }
}
}
}