5 * i_scale_mixing() is based on code contained in pnmscale.c, part of
6 * the netpbm distribution. No code was copied from pnmscale but
7 * the algorthm was and for this I thank the netpbm crew.
12 /* pnmscale.c - read a portable anymap and scale it
14 ** Copyright (C) 1989, 1991 by Jef Poskanzer.
16 ** Permission to use, copy, modify, and distribute this software and its
17 ** documentation for any purpose and without fee is hereby granted, provided
18 ** that the above copyright notice appear in all copies and that both that
19 ** copyright notice and this permission notice appear in supporting
20 ** documentation. This software is provided "as is" without express or
27 zero_row(i_fcolor *row, i_img_dim width, int channels);
31 IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
32 i_img_dim width, int channels);
34 IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width,
35 i_fcolor const *in, i_img_dim in_width,
42 Returns a new image scaled to the given size.
44 Unlike i_scale_axis() this does a simple coverage of pixels from
45 source to target and doesn't resample.
47 Adapted from pnmscale.
52 i_scale_mixing(i_img *src, i_img_dim x_out, i_img_dim y_out) {
54 i_fcolor *accum_row = NULL;
57 size_t accum_row_bytes;
58 double rowsleft, fracrowtofill;
62 mm_log((1, "i_scale_mixing(src %p, out(" i_DFp "))\n",
63 src, i_DFcp(x_out, y_out)));
68 i_push_errorf(0, "output width %" i_DF " invalid", i_DFc(x_out));
72 i_push_errorf(0, "output height %" i_DF " invalid", i_DFc(y_out));
76 if (x_out == src->xsize && y_out == src->ysize) {
80 y_scale = y_out / (double)src->ysize;
82 result = i_sametype_chans(src, x_out, y_out, src->channels);
86 accum_row_bytes = sizeof(i_fcolor) * src->xsize;
87 if (accum_row_bytes / sizeof(i_fcolor) != src->xsize) {
88 i_push_error(0, "integer overflow allocating accumulator row buffer");
92 accum_row = mymalloc(accum_row_bytes);
95 IM_COLOR *in_row = NULL;
96 IM_COLOR *xscale_row = NULL;
97 size_t in_row_bytes, out_row_bytes;
99 in_row_bytes = sizeof(IM_COLOR) * src->xsize;
100 if (in_row_bytes / sizeof(IM_COLOR) != src->xsize) {
101 i_push_error(0, "integer overflow allocating input row buffer");
104 out_row_bytes = sizeof(IM_COLOR) * x_out;
105 if (out_row_bytes / sizeof(IM_COLOR) != x_out) {
106 i_push_error(0, "integer overflow allocating output row buffer");
110 in_row = mymalloc(in_row_bytes);
111 xscale_row = mymalloc(out_row_bytes);
115 for (y = 0; y < y_out; ++y) {
116 if (y_out == src->ysize) {
117 /* no vertical scaling, just load it */
121 /* load and convert to doubles */
122 IM_GLIN(src, 0, src->xsize, y, in_row);
123 for (x = 0; x < src->xsize; ++x) {
124 for (ch = 0; ch < src->channels; ++ch) {
125 accum_row[x].channel[ch] = in_row[x].channel[ch];
129 IM_GLIN(src, 0, src->xsize, y, accum_row);
131 /* alpha adjust if needed */
132 if (src->channels == 2 || src->channels == 4) {
133 for (x = 0; x < src->xsize; ++x) {
134 for (ch = 0; ch < src->channels-1; ++ch) {
135 accum_row[x].channel[ch] *=
136 accum_row[x].channel[src->channels-1] / IM_SAMPLE_MAX;
143 zero_row(accum_row, src->xsize, src->channels);
144 while (fracrowtofill > 0) {
146 if (rowsread < src->ysize) {
147 IM_GLIN(src, 0, src->xsize, rowsread, in_row);
150 /* else just use the last row read */
154 if (rowsleft < fracrowtofill) {
155 IM_SUFFIX(accum_output_row)(accum_row, rowsleft, in_row,
156 src->xsize, src->channels);
157 fracrowtofill -= rowsleft;
161 IM_SUFFIX(accum_output_row)(accum_row, fracrowtofill, in_row,
162 src->xsize, src->channels);
163 rowsleft -= fracrowtofill;
168 /* we've accumulated a vertically scaled row */
169 if (x_out == src->xsize) {
173 /* no need to scale, but we need to convert it */
174 if (result->channels == 2 || result->channels == 4) {
175 int alpha_chan = result->channels - 1;
176 for (x = 0; x < x_out; ++x) {
177 double alpha = accum_row[x].channel[alpha_chan] / IM_SAMPLE_MAX;
179 for (ch = 0; ch < alpha_chan; ++ch) {
180 int val = accum_row[x].channel[ch] / alpha + 0.5;
181 xscale_row[x].channel[ch] = IM_LIMIT(val);
185 /* rather than leaving any color data as whatever was
186 originally in the buffer, set it to black. This isn't
187 any more correct, but it gives us more compressible
191 for (ch = 0; ch < alpha_chan; ++ch) {
192 xscale_row[x].channel[ch] = 0;
195 xscale_row[x].channel[alpha_chan] = IM_LIMIT(accum_row[x].channel[alpha_chan]+0.5);
199 for (x = 0; x < x_out; ++x) {
200 for (ch = 0; ch < result->channels; ++ch)
201 xscale_row[x].channel[ch] = IM_LIMIT(accum_row[x].channel[ch]+0.5);
204 IM_PLIN(result, 0, x_out, y, xscale_row);
206 IM_PLIN(result, 0, x_out, y, accum_row);
210 IM_SUFFIX(horizontal_scale)(xscale_row, x_out, accum_row,
211 src->xsize, src->channels);
212 IM_PLIN(result, 0, x_out, y, xscale_row);
224 zero_row(i_fcolor *row, i_img_dim width, int channels) {
228 /* with IEEE floats we could just use memset() but that's not
229 safe in general under ANSI C.
230 memset() is slightly faster.
232 for (x = 0; x < width; ++x) {
233 for (ch = 0; ch < channels; ++ch)
234 row[x].channel[ch] = 0.0;
241 IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
242 i_img_dim width, int channels) {
246 /* it's tempting to change this into a pointer iteration loop but
247 modern CPUs do the indexing as part of the instruction */
248 if (channels == 2 || channels == 4) {
249 for (x = 0; x < width; ++x) {
250 for (ch = 0; ch < channels-1; ++ch) {
251 accum[x].channel[ch] += in[x].channel[ch] * fraction * in[x].channel[channels-1] / IM_SAMPLE_MAX;
253 accum[x].channel[channels-1] += in[x].channel[channels-1] * fraction;
257 for (x = 0; x < width; ++x) {
258 for (ch = 0; ch < channels; ++ch) {
259 accum[x].channel[ch] += in[x].channel[ch] * fraction;
266 IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width,
267 i_fcolor const *in, i_img_dim in_width,
269 double frac_col_to_fill, frac_col_left;
272 double x_scale = (double)out_width / in_width;
274 double accum[MAXCHANNELS] = { 0 };
276 frac_col_to_fill = 1.0;
278 for (in_x = 0; in_x < in_width; ++in_x) {
279 frac_col_left = x_scale;
280 while (frac_col_left >= frac_col_to_fill) {
281 for (ch = 0; ch < channels; ++ch)
282 accum[ch] += frac_col_to_fill * in[in_x].channel[ch];
284 if (channels == 2 || channels == 4) {
285 int alpha_chan = channels - 1;
286 double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
288 for (ch = 0; ch < alpha_chan; ++ch) {
289 IM_WORK_T val = IM_ROUND(accum[ch] / alpha);
290 out[out_x].channel[ch] = IM_LIMIT(val);
294 for (ch = 0; ch < alpha_chan; ++ch) {
295 /* See RT #32324 (and mention above) */
296 out[out_x].channel[ch] = 0;
299 out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
302 for (ch = 0; ch < channels; ++ch) {
303 IM_WORK_T val = IM_ROUND(accum[ch]);
304 out[out_x].channel[ch] = IM_LIMIT(val);
307 for (ch = 0; ch < channels; ++ch)
309 frac_col_left -= frac_col_to_fill;
310 frac_col_to_fill = 1.0;
314 if (frac_col_left > 0) {
315 for (ch = 0; ch < channels; ++ch) {
316 accum[ch] += frac_col_left * in[in_x].channel[ch];
318 frac_col_to_fill -= frac_col_left;
322 if (out_x < out_width-1 || out_x > out_width) {
323 i_fatal(3, "Internal error: out_x %d out of range (width %d)", out_x, out_width);
326 if (out_x < out_width) {
327 for (ch = 0; ch < channels; ++ch) {
328 accum[ch] += frac_col_to_fill * in[in_width-1].channel[ch];
330 if (channels == 2 || channels == 4) {
331 int alpha_chan = channels - 1;
332 double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
334 for (ch = 0; ch < alpha_chan; ++ch) {
335 IM_WORK_T val = IM_ROUND(accum[ch] / alpha);
336 out[out_x].channel[ch] = IM_LIMIT(val);
340 for (ch = 0; ch < alpha_chan; ++ch) {
341 /* See RT #32324 (and mention above) */
342 out[out_x].channel[ch] = 0;
345 out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
348 for (ch = 0; ch < channels; ++ch) {
349 IM_WORK_T val = IM_ROUND(accum[ch]);
350 out[out_x].channel[ch] = IM_LIMIT(val);