]> git.imager.perl.org - imager.git/blame - scale.im
move TIFF into its own module
[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
27zero_row(i_fcolor *row, int 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,
658f724e
TC
32 int width, int channels);
33static void
a10945af
TC
34IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width,
35 i_fcolor const *in, int in_width,
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 *
52i_scale_mixing(i_img *src, int x_out, int y_out) {
53 i_img *result;
658f724e 54 i_fcolor *accum_row = NULL;
874c55db 55 int x, y, ch;
a10945af 56 int accum_row_bytes;
658f724e
TC
57 double rowsleft, fracrowtofill;
58 int rowsread;
59 double y_scale;
60
61 mm_log((1, "i_scale_mixing(src %p, x_out %d, y_out %d)\n",
62 src, x_out, y_out));
63
64 i_clear_error();
65
66 if (x_out <= 0) {
67 i_push_errorf(0, "output width %d invalid", x_out);
68 return NULL;
69 }
70 if (y_out <= 0) {
71 i_push_errorf(0, "output height %d invalid", y_out);
72 return NULL;
73 }
74
658f724e
TC
75 if (x_out == src->xsize && y_out == src->ysize) {
76 return i_copy(src);
77 }
78
79 y_scale = y_out / (double)src->ysize;
80
81 result = i_sametype_chans(src, x_out, y_out, src->channels);
82 if (!result)
83 return NULL;
84
a10945af
TC
85 accum_row_bytes = sizeof(i_fcolor) * src->xsize;
86 if (accum_row_bytes / sizeof(i_fcolor) != src->xsize) {
87 i_push_error(0, "integer overflow allocating accumulator row buffer");
88 return NULL;
89 }
90
91 accum_row = mymalloc(accum_row_bytes);
92
93#code src->bits <= 8
94 IM_COLOR *in_row = NULL;
95 IM_COLOR *xscale_row = NULL;
96 int in_row_bytes, out_row_bytes;
97
98 in_row_bytes = sizeof(IM_COLOR) * src->xsize;
99 if (in_row_bytes / sizeof(IM_COLOR) != src->xsize) {
100 i_push_error(0, "integer overflow allocating input row buffer");
101 return NULL;
102 }
103 out_row_bytes = sizeof(IM_COLOR) * x_out;
104 if (out_row_bytes / sizeof(IM_COLOR) != x_out) {
105 i_push_error(0, "integer overflow allocating output row buffer");
106 return NULL;
107 }
108
658f724e 109 in_row = mymalloc(in_row_bytes);
658f724e
TC
110 xscale_row = mymalloc(out_row_bytes);
111
112 rowsread = 0;
113 rowsleft = 0.0;
114 for (y = 0; y < y_out; ++y) {
115 if (y_out == src->ysize) {
a10945af 116 /* no vertical scaling, just load it */
a10945af 117#ifdef IM_EIGHT_BIT
e4bf9335 118 int x, ch;
a10945af
TC
119 /* load and convert to doubles */
120 IM_GLIN(src, 0, src->xsize, y, in_row);
121 for (x = 0; x < src->xsize; ++x) {
122 for (ch = 0; ch < src->channels; ++ch) {
123 accum_row[x].channel[ch] = in_row[x].channel[ch];
124 }
125 }
126#else
127 IM_GLIN(src, 0, src->xsize, y, accum_row);
128#endif
874c55db
TC
129 /* alpha adjust if needed */
130 if (src->channels == 2 || src->channels == 4) {
131 for (x = 0; x < src->xsize; ++x) {
132 for (ch = 0; ch < src->channels-1; ++ch) {
133 accum_row[x].channel[ch] *=
134 accum_row[x].channel[src->channels-1] / IM_SAMPLE_MAX;
135 }
136 }
137 }
658f724e
TC
138 }
139 else {
140 fracrowtofill = 1.0;
141 zero_row(accum_row, src->xsize, src->channels);
142 while (fracrowtofill > 0) {
143 if (rowsleft <= 0) {
144 if (rowsread < src->ysize) {
a10945af 145 IM_GLIN(src, 0, src->xsize, rowsread, in_row);
658f724e
TC
146 ++rowsread;
147 }
148 /* else just use the last row read */
149
150 rowsleft = y_scale;
151 }
152 if (rowsleft < fracrowtofill) {
a10945af
TC
153 IM_SUFFIX(accum_output_row)(accum_row, rowsleft, in_row,
154 src->xsize, src->channels);
658f724e
TC
155 fracrowtofill -= rowsleft;
156 rowsleft = 0;
157 }
158 else {
a10945af
TC
159 IM_SUFFIX(accum_output_row)(accum_row, fracrowtofill, in_row,
160 src->xsize, src->channels);
658f724e
TC
161 rowsleft -= fracrowtofill;
162 fracrowtofill = 0;
163 }
164 }
a10945af
TC
165 }
166 /* we've accumulated a vertically scaled row */
167 if (x_out == src->xsize) {
a10945af 168#if IM_EIGHT_BIT
e4bf9335 169 int x, ch;
a10945af 170 /* no need to scale, but we need to convert it */
874c55db
TC
171 if (result->channels == 2 || result->channels == 4) {
172 int alpha_chan = result->channels - 1;
173 for (x = 0; x < x_out; ++x) {
174 double alpha = accum_row[x].channel[alpha_chan] / IM_SAMPLE_MAX;
175 if (alpha) {
176 for (ch = 0; ch < alpha_chan; ++ch) {
177 int val = accum_row[x].channel[ch] / alpha + 0.5;
178 xscale_row[x].channel[ch] = IM_LIMIT(val);
179 }
180 }
2757bad0
TC
181 else {
182 /* rather than leaving any color data as whatever was
183 originally in the buffer, set it to black. This isn't
184 any more correct, but it gives us more compressible
185 image data.
186 RT #32324
187 */
188 for (ch = 0; ch < alpha_chan; ++ch) {
189 xscale_row[x].channel[ch] = 0;
190 }
191 }
874c55db
TC
192 xscale_row[x].channel[alpha_chan] = IM_LIMIT(accum_row[x].channel[alpha_chan]+0.5);
193 }
194 }
195 else {
196 for (x = 0; x < x_out; ++x) {
197 for (ch = 0; ch < result->channels; ++ch)
198 xscale_row[x].channel[ch] = IM_LIMIT(accum_row[x].channel[ch]+0.5);
199 }
658f724e 200 }
a10945af
TC
201 IM_PLIN(result, 0, x_out, y, xscale_row);
202#else
203 IM_PLIN(result, 0, x_out, y, accum_row);
204#endif
205 }
206 else {
207 IM_SUFFIX(horizontal_scale)(xscale_row, x_out, accum_row,
208 src->xsize, src->channels);
209 IM_PLIN(result, 0, x_out, y, xscale_row);
658f724e
TC
210 }
211 }
658f724e 212 myfree(in_row);
658f724e 213 myfree(xscale_row);
a10945af
TC
214#/code
215 myfree(accum_row);
658f724e
TC
216
217 return result;
218}
219
220static void
221zero_row(i_fcolor *row, int width, int channels) {
222 int x;
223 int ch;
224
225 /* with IEEE floats we could just use memset() but that's not
a10945af
TC
226 safe in general under ANSI C.
227 memset() is slightly faster.
228 */
658f724e
TC
229 for (x = 0; x < width; ++x) {
230 for (ch = 0; ch < channels; ++ch)
231 row[x].channel[ch] = 0.0;
232 }
233}
234
a10945af
TC
235#code
236
658f724e 237static void
a10945af 238IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
658f724e
TC
239 int width, int channels) {
240 int x, ch;
241
a10945af
TC
242 /* it's tempting to change this into a pointer iteration loop but
243 modern CPUs do the indexing as part of the instruction */
874c55db
TC
244 if (channels == 2 || channels == 4) {
245 for (x = 0; x < width; ++x) {
246 for (ch = 0; ch < channels-1; ++ch) {
247 accum[x].channel[ch] += in[x].channel[ch] * fraction * in[x].channel[channels-1] / IM_SAMPLE_MAX;
248 }
249 accum[x].channel[channels-1] += in[x].channel[channels-1] * fraction;
250 }
251 }
252 else {
253 for (x = 0; x < width; ++x) {
254 for (ch = 0; ch < channels; ++ch) {
255 accum[x].channel[ch] += in[x].channel[ch] * fraction;
256 }
658f724e
TC
257 }
258 }
259}
260
261static void
a10945af 262IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width,
658f724e
TC
263 i_fcolor const *in, int in_width,
264 int channels) {
265 double frac_col_to_fill, frac_col_left;
266 int in_x;
267 int out_x;
268 double x_scale = (double)out_width / in_width;
269 int ch;
270 double accum[MAXCHANNELS] = { 0 };
271
272 frac_col_to_fill = 1.0;
273 out_x = 0;
274 for (in_x = 0; in_x < in_width; ++in_x) {
275 frac_col_left = x_scale;
276 while (frac_col_left >= frac_col_to_fill) {
277 for (ch = 0; ch < channels; ++ch)
278 accum[ch] += frac_col_to_fill * in[in_x].channel[ch];
279
874c55db
TC
280 if (channels == 2 || channels == 4) {
281 int alpha_chan = channels - 1;
282 double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
283 if (alpha) {
284 for (ch = 0; ch < alpha_chan; ++ch) {
285 IM_WORK_T val = IM_ROUND(accum[ch] / alpha);
286 out[out_x].channel[ch] = IM_LIMIT(val);
287 }
288 }
2757bad0
TC
289 else {
290 for (ch = 0; ch < alpha_chan; ++ch) {
291 /* See RT #32324 (and mention above) */
292 out[out_x].channel[ch] = 0;
293 }
294 }
874c55db 295 out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
658f724e 296 }
874c55db
TC
297 else {
298 for (ch = 0; ch < channels; ++ch) {
299 IM_WORK_T val = IM_ROUND(accum[ch]);
300 out[out_x].channel[ch] = IM_LIMIT(val);
301 }
302 }
303 for (ch = 0; ch < channels; ++ch)
304 accum[ch] = 0;
658f724e
TC
305 frac_col_left -= frac_col_to_fill;
306 frac_col_to_fill = 1.0;
307 ++out_x;
308 }
309
310 if (frac_col_left > 0) {
311 for (ch = 0; ch < channels; ++ch) {
312 accum[ch] += frac_col_left * in[in_x].channel[ch];
313 }
314 frac_col_to_fill -= frac_col_left;
315 }
316 }
317
318 if (out_x < out_width-1 || out_x > out_width) {
319 i_fatal(3, "Internal error: out_x %d out of range (width %d)", out_x, out_width);
320 }
321
322 if (out_x < out_width) {
323 for (ch = 0; ch < channels; ++ch) {
324 accum[ch] += frac_col_to_fill * in[in_width-1].channel[ch];
874c55db
TC
325 }
326 if (channels == 2 || channels == 4) {
327 int alpha_chan = channels - 1;
328 double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
329 if (alpha) {
330 for (ch = 0; ch < alpha_chan; ++ch) {
331 IM_WORK_T val = IM_ROUND(accum[ch] / alpha);
332 out[out_x].channel[ch] = IM_LIMIT(val);
333 }
334 }
2757bad0
TC
335 else {
336 for (ch = 0; ch < alpha_chan; ++ch) {
337 /* See RT #32324 (and mention above) */
338 out[out_x].channel[ch] = 0;
339 }
340 }
874c55db
TC
341 out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
342 }
343 else {
344 for (ch = 0; ch < channels; ++ch) {
345 IM_WORK_T val = IM_ROUND(accum[ch]);
346 out[out_x].channel[ch] = IM_LIMIT(val);
347 }
658f724e
TC
348 }
349 }
350}
a10945af
TC
351
352#/code