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