]> git.imager.perl.org - imager.git/blame - scale.im
update Changes
[imager.git] / scale.im
CommitLineData
658f724e 1#include "imager.h"
874c55db 2#include "imageri.h"
658f724e
TC
3
4/*
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.
8 *
9 * Tony
10 */
11
12/* pnmscale.c - read a portable anymap and scale it
13**
14** Copyright (C) 1989, 1991 by Jef Poskanzer.
15**
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
21** implied warranty.
22**
23*/
24
25
26static void
8d14daab 27zero_row(i_fcolor *row, i_img_dim width, int channels);
a10945af
TC
28
29#code
658f724e 30static void
a10945af 31IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
8d14daab 32 i_img_dim width, int channels);
658f724e 33static void
8d14daab
TC
34IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width,
35 i_fcolor const *in, i_img_dim in_width,
a10945af
TC
36 int channels);
37#/code
658f724e
TC
38
39/*
40=item i_scale_mixing
41
42Returns a new image scaled to the given size.
43
44Unlike i_scale_axis() this does a simple coverage of pixels from
45source to target and doesn't resample.
46
47Adapted from pnmscale.
48
49=cut
50*/
51i_img *
8d14daab 52i_scale_mixing(i_img *src, i_img_dim x_out, i_img_dim y_out) {
658f724e 53 i_img *result;
658f724e 54 i_fcolor *accum_row = NULL;
8d14daab
TC
55 i_img_dim x, y;
56 int ch;
57 size_t accum_row_bytes;
658f724e 58 double rowsleft, fracrowtofill;
8d14daab 59 i_img_dim rowsread;
658f724e
TC
60 double y_scale;
61
8d14daab
TC
62 mm_log((1, "i_scale_mixing(src %p, out(" i_DFp "))\n",
63 src, i_DFcp(x_out, y_out)));
658f724e
TC
64
65 i_clear_error();
66
67 if (x_out <= 0) {
8d14daab 68 i_push_errorf(0, "output width %" i_DF " invalid", i_DFc(x_out));
658f724e
TC
69 return NULL;
70 }
71 if (y_out <= 0) {
8d14daab 72 i_push_errorf(0, "output height %" i_DF " invalid", i_DFc(y_out));
658f724e
TC
73 return NULL;
74 }
75
658f724e
TC
76 if (x_out == src->xsize && y_out == src->ysize) {
77 return i_copy(src);
78 }
79
80 y_scale = y_out / (double)src->ysize;
81
82 result = i_sametype_chans(src, x_out, y_out, src->channels);
83 if (!result)
84 return NULL;
85
a10945af
TC
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");
89 return NULL;
90 }
91
92 accum_row = mymalloc(accum_row_bytes);
93
94#code src->bits <= 8
95 IM_COLOR *in_row = NULL;
96 IM_COLOR *xscale_row = NULL;
8d14daab 97 size_t in_row_bytes, out_row_bytes;
a10945af
TC
98
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");
102 return NULL;
103 }
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");
107 return NULL;
108 }
109
658f724e 110 in_row = mymalloc(in_row_bytes);
658f724e
TC
111 xscale_row = mymalloc(out_row_bytes);
112
113 rowsread = 0;
114 rowsleft = 0.0;
115 for (y = 0; y < y_out; ++y) {
116 if (y_out == src->ysize) {
a10945af 117 /* no vertical scaling, just load it */
a10945af 118#ifdef IM_EIGHT_BIT
8d14daab
TC
119 i_img_dim x;
120 int ch;
a10945af
TC
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];
126 }
127 }
128#else
129 IM_GLIN(src, 0, src->xsize, y, accum_row);
130#endif
874c55db
TC
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;
137 }
138 }
139 }
658f724e
TC
140 }
141 else {
142 fracrowtofill = 1.0;
143 zero_row(accum_row, src->xsize, src->channels);
144 while (fracrowtofill > 0) {
145 if (rowsleft <= 0) {
146 if (rowsread < src->ysize) {
a10945af 147 IM_GLIN(src, 0, src->xsize, rowsread, in_row);
658f724e
TC
148 ++rowsread;
149 }
150 /* else just use the last row read */
151
152 rowsleft = y_scale;
153 }
154 if (rowsleft < fracrowtofill) {
a10945af
TC
155 IM_SUFFIX(accum_output_row)(accum_row, rowsleft, in_row,
156 src->xsize, src->channels);
658f724e
TC
157 fracrowtofill -= rowsleft;
158 rowsleft = 0;
159 }
160 else {
a10945af
TC
161 IM_SUFFIX(accum_output_row)(accum_row, fracrowtofill, in_row,
162 src->xsize, src->channels);
658f724e
TC
163 rowsleft -= fracrowtofill;
164 fracrowtofill = 0;
165 }
166 }
a10945af
TC
167 }
168 /* we've accumulated a vertically scaled row */
169 if (x_out == src->xsize) {
a10945af 170#if IM_EIGHT_BIT
8d14daab
TC
171 i_img_dim x;
172 int ch;
a10945af 173 /* no need to scale, but we need to convert it */
874c55db
TC
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;
178 if (alpha) {
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);
182 }
183 }
2757bad0
TC
184 else {
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
188 image data.
189 RT #32324
190 */
191 for (ch = 0; ch < alpha_chan; ++ch) {
192 xscale_row[x].channel[ch] = 0;
193 }
194 }
874c55db
TC
195 xscale_row[x].channel[alpha_chan] = IM_LIMIT(accum_row[x].channel[alpha_chan]+0.5);
196 }
197 }
198 else {
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);
202 }
658f724e 203 }
a10945af
TC
204 IM_PLIN(result, 0, x_out, y, xscale_row);
205#else
206 IM_PLIN(result, 0, x_out, y, accum_row);
207#endif
208 }
209 else {
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);
658f724e
TC
213 }
214 }
658f724e 215 myfree(in_row);
658f724e 216 myfree(xscale_row);
a10945af
TC
217#/code
218 myfree(accum_row);
658f724e
TC
219
220 return result;
221}
222
223static void
8d14daab
TC
224zero_row(i_fcolor *row, i_img_dim width, int channels) {
225 i_img_dim x;
658f724e
TC
226 int ch;
227
228 /* with IEEE floats we could just use memset() but that's not
a10945af
TC
229 safe in general under ANSI C.
230 memset() is slightly faster.
231 */
658f724e
TC
232 for (x = 0; x < width; ++x) {
233 for (ch = 0; ch < channels; ++ch)
234 row[x].channel[ch] = 0.0;
235 }
236}
237
a10945af
TC
238#code
239
658f724e 240static void
a10945af 241IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
8d14daab
TC
242 i_img_dim width, int channels) {
243 i_img_dim x;
244 int ch;
658f724e 245
a10945af
TC
246 /* it's tempting to change this into a pointer iteration loop but
247 modern CPUs do the indexing as part of the instruction */
874c55db
TC
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;
252 }
253 accum[x].channel[channels-1] += in[x].channel[channels-1] * fraction;
254 }
255 }
256 else {
257 for (x = 0; x < width; ++x) {
258 for (ch = 0; ch < channels; ++ch) {
259 accum[x].channel[ch] += in[x].channel[ch] * fraction;
260 }
658f724e
TC
261 }
262 }
263}
264
265static void
8d14daab
TC
266IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width,
267 i_fcolor const *in, i_img_dim in_width,
658f724e
TC
268 int channels) {
269 double frac_col_to_fill, frac_col_left;
8d14daab
TC
270 i_img_dim in_x;
271 i_img_dim out_x;
658f724e
TC
272 double x_scale = (double)out_width / in_width;
273 int ch;
274 double accum[MAXCHANNELS] = { 0 };
275
276 frac_col_to_fill = 1.0;
277 out_x = 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];
283
874c55db
TC
284 if (channels == 2 || channels == 4) {
285 int alpha_chan = channels - 1;
286 double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
287 if (alpha) {
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);
291 }
292 }
2757bad0
TC
293 else {
294 for (ch = 0; ch < alpha_chan; ++ch) {
295 /* See RT #32324 (and mention above) */
296 out[out_x].channel[ch] = 0;
297 }
298 }
874c55db 299 out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
658f724e 300 }
874c55db
TC
301 else {
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);
305 }
306 }
307 for (ch = 0; ch < channels; ++ch)
308 accum[ch] = 0;
658f724e
TC
309 frac_col_left -= frac_col_to_fill;
310 frac_col_to_fill = 1.0;
311 ++out_x;
312 }
313
314 if (frac_col_left > 0) {
315 for (ch = 0; ch < channels; ++ch) {
316 accum[ch] += frac_col_left * in[in_x].channel[ch];
317 }
318 frac_col_to_fill -= frac_col_left;
319 }
320 }
321
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);
324 }
325
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];
874c55db
TC
329 }
330 if (channels == 2 || channels == 4) {
331 int alpha_chan = channels - 1;
332 double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
333 if (alpha) {
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);
337 }
338 }
2757bad0
TC
339 else {
340 for (ch = 0; ch < alpha_chan; ++ch) {
341 /* See RT #32324 (and mention above) */
342 out[out_x].channel[ch] = 0;
343 }
344 }
874c55db
TC
345 out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
346 }
347 else {
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);
351 }
658f724e
TC
352 }
353 }
354}
a10945af
TC
355
356#/code