File Coverage

scale.im
Criterion Covered Total %
statement 128 157 81.5
branch 156 244 63.9
condition n/a
subroutine n/a
pod n/a
total 284 401 70.8


line stmt bran cond sub pod time code
1             #include "imager.h"
2             #include "imageri.h"
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              
26             static void
27             zero_row(i_fcolor *row, i_img_dim width, int channels);
28              
29             #code
30             static void
31             IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
32             i_img_dim width, int channels);
33             static void
34             IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width,
35             i_fcolor const *in, i_img_dim in_width,
36             int channels);
37             #/code
38              
39             /*
40             =item i_scale_mixing
41              
42             Returns a new image scaled to the given size.
43              
44             Unlike i_scale_axis() this does a simple coverage of pixels from
45             source to target and doesn't resample.
46              
47             Adapted from pnmscale.
48              
49             =cut
50             */
51             i_img *
52 20           i_scale_mixing(i_img *src, i_img_dim x_out, i_img_dim y_out) {
53 20           i_img *result = NULL;
54 20           i_fcolor *accum_row = NULL;
55             i_img_dim x, y;
56             int ch;
57             size_t accum_row_bytes;
58             double rowsleft, fracrowtofill;
59             i_img_dim rowsread;
60             double y_scale;
61              
62 20           mm_log((1, "i_scale_mixing(src %p, out(" i_DFp "))\n",
63             src, i_DFcp(x_out, y_out)));
64              
65 20           i_clear_error();
66              
67 20 50         if (x_out <= 0) {
68 0           i_push_errorf(0, "output width %" i_DF " invalid", i_DFc(x_out));
69 0           return NULL;
70             }
71 20 50         if (y_out <= 0) {
72 0           i_push_errorf(0, "output height %" i_DF " invalid", i_DFc(y_out));
73 0           return NULL;
74             }
75              
76 20 100         if (x_out == src->xsize && y_out == src->ysize) {
    100          
77 1           return i_copy(src);
78             }
79              
80 19           y_scale = y_out / (double)src->ysize;
81              
82 19           accum_row_bytes = sizeof(i_fcolor) * src->xsize;
83 19 50         if (accum_row_bytes / sizeof(i_fcolor) != src->xsize) {
84 0           i_push_error(0, "integer overflow allocating accumulator row buffer");
85 0           return NULL;
86             }
87              
88 19           result = i_sametype_chans(src, x_out, y_out, src->channels);
89 19 50         if (!result)
90 0           return NULL;
91              
92 19           accum_row = mymalloc(accum_row_bytes);
93              
94 19 100         #code src->bits <= 8
95 19           IM_COLOR *in_row = NULL;
96 19           IM_COLOR *xscale_row = NULL;
97             size_t in_row_bytes, out_row_bytes;
98              
99 19           in_row_bytes = sizeof(IM_COLOR) * src->xsize;
100 19 50         if (in_row_bytes / sizeof(IM_COLOR) != src->xsize) {
    50          
101 0           myfree(accum_row);
102 0           i_img_destroy(result);
103 0           i_push_error(0, "integer overflow allocating input row buffer");
104 0           return NULL;
105             }
106 19           out_row_bytes = sizeof(IM_COLOR) * x_out;
107 19 50         if (out_row_bytes / sizeof(IM_COLOR) != x_out) {
    50          
108 0           myfree(accum_row);
109 0           i_img_destroy(result);
110 0           i_push_error(0, "integer overflow allocating output row buffer");
111 0           return NULL;
112             }
113              
114 19           in_row = mymalloc(in_row_bytes);
115 19           xscale_row = mymalloc(out_row_bytes);
116              
117 19           rowsread = 0;
118 19           rowsleft = 0.0;
119 935 100         for (y = 0; y < y_out; ++y) {
    100          
120 916 100         if (y_out == src->ysize) {
    100          
121             /* no vertical scaling, just load it */
122             #ifdef IM_EIGHT_BIT
123             i_img_dim x;
124             int ch;
125             /* load and convert to doubles */
126 96           IM_GLIN(src, 0, src->xsize, y, in_row);
127 15456 100         for (x = 0; x < src->xsize; ++x) {
128 61440 100         for (ch = 0; ch < src->channels; ++ch) {
129 46080           accum_row[x].channel[ch] = in_row[x].channel[ch];
130             }
131             }
132             #else
133 85           IM_GLIN(src, 0, src->xsize, y, accum_row);
134             #endif
135             /* alpha adjust if needed */
136 181 50         if (src->channels == 2 || src->channels == 4) {
    50          
137 96 0         for (x = 0; x < src->xsize; ++x) {
    0          
138 0 0         for (ch = 0; ch < src->channels-1; ++ch) {
    0          
139 0           accum_row[x].channel[ch] *=
140 0           accum_row[x].channel[src->channels-1] / IM_SAMPLE_MAX;
141             }
142             }
143             }
144             }
145             else {
146 735           fracrowtofill = 1.0;
147 735           zero_row(accum_row, src->xsize, src->channels);
148 2474 100         while (fracrowtofill > 0) {
    100          
149 1739 100         if (rowsleft <= 0) {
    100          
150 1537 100         if (rowsread < src->ysize) {
    100          
151 1535           IM_GLIN(src, 0, src->xsize, rowsread, in_row);
152 1535           ++rowsread;
153             }
154             /* else just use the last row read */
155              
156 1537           rowsleft = y_scale;
157             }
158 1739 100         if (rowsleft < fracrowtofill) {
    100          
159 1004           IM_SUFFIX(accum_output_row)(accum_row, rowsleft, in_row,
160             src->xsize, src->channels);
161 1004           fracrowtofill -= rowsleft;
162 1004           rowsleft = 0;
163             }
164             else {
165 735           IM_SUFFIX(accum_output_row)(accum_row, fracrowtofill, in_row,
166             src->xsize, src->channels);
167 735           rowsleft -= fracrowtofill;
168 735           fracrowtofill = 0;
169             }
170             }
171             }
172             /* we've accumulated a vertically scaled row */
173 916 100         if (x_out == src->xsize) {
    100          
174             #if IM_EIGHT_BIT
175             i_img_dim x;
176             int ch;
177             /* no need to scale, but we need to convert it */
178 96 50         if (result->channels == 2 || result->channels == 4) {
    50          
179 0           int alpha_chan = result->channels - 1;
180 0 0         for (x = 0; x < x_out; ++x) {
181 0           double alpha = accum_row[x].channel[alpha_chan] / IM_SAMPLE_MAX;
182 0 0         if (alpha) {
183 0 0         for (ch = 0; ch < alpha_chan; ++ch) {
184 0           int val = accum_row[x].channel[ch] / alpha + 0.5;
185 0 0         xscale_row[x].channel[ch] = IM_LIMIT(val);
186             }
187             }
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 0 0         for (ch = 0; ch < alpha_chan; ++ch) {
196 0           xscale_row[x].channel[ch] = 0;
197             }
198             }
199 0 0         xscale_row[x].channel[alpha_chan] = IM_LIMIT(accum_row[x].channel[alpha_chan]+0.5);
    0          
200             }
201             }
202             else {
203 15456 100         for (x = 0; x < x_out; ++x) {
204 61440 100         for (ch = 0; ch < result->channels; ++ch)
205 46080 50         xscale_row[x].channel[ch] = IM_LIMIT(accum_row[x].channel[ch]+0.5);
    50          
206             }
207             }
208 96           IM_PLIN(result, 0, x_out, y, xscale_row);
209             #else
210 28           IM_PLIN(result, 0, x_out, y, accum_row);
211             #endif
212             }
213             else {
214 792           IM_SUFFIX(horizontal_scale)(xscale_row, x_out, accum_row,
215             src->xsize, src->channels);
216 792           IM_PLIN(result, 0, x_out, y, xscale_row);
217             }
218             }
219 19           myfree(in_row);
220 19           myfree(xscale_row);
221             #/code
222 19           myfree(accum_row);
223              
224 19           return result;
225             }
226              
227             static void
228 735           zero_row(i_fcolor *row, i_img_dim width, int channels) {
229             i_img_dim x;
230             int ch;
231              
232             /* with IEEE floats we could just use memset() but that's not
233             safe in general under ANSI C.
234             memset() is slightly faster.
235             */
236 110673 100         for (x = 0; x < width; ++x) {
237 441472 100         for (ch = 0; ch < channels; ++ch)
238 331534           row[x].channel[ch] = 0.0;
239             }
240 735           }
241              
242             #code
243              
244             static void
245 1739           IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
246             i_img_dim width, int channels) {
247             i_img_dim x;
248             int ch;
249              
250             /* it's tempting to change this into a pointer iteration loop but
251             modern CPUs do the indexing as part of the instruction */
252 1822 50         if (channels == 2 || channels == 4) {
    100          
    50          
    50          
253 3652 100         for (x = 0; x < width; ++x) {
    0          
254 14276 100         for (ch = 0; ch < channels-1; ++ch) {
    0          
255 10707           accum[x].channel[ch] += in[x].channel[ch] * fraction * in[x].channel[channels-1] / IM_SAMPLE_MAX;
256             }
257 3569           accum[x].channel[channels-1] += in[x].channel[channels-1] * fraction;
258             }
259             }
260             else {
261 249480 100         for (x = 0; x < width; ++x) {
    100          
262 991296 100         for (ch = 0; ch < channels; ++ch) {
    100          
263 743472           accum[x].channel[ch] += in[x].channel[ch] * fraction;
264             }
265             }
266             }
267 1739           }
268              
269             static void
270 792           IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width,
271             i_fcolor const *in, i_img_dim in_width,
272             int channels) {
273             double frac_col_to_fill, frac_col_left;
274             i_img_dim in_x;
275             i_img_dim out_x;
276 792           double x_scale = (double)out_width / in_width;
277             int ch;
278 792           double accum[MAXCHANNELS] = { 0 };
279            
280 792           frac_col_to_fill = 1.0;
281 792           out_x = 0;
282 117456 100         for (in_x = 0; in_x < in_width; ++in_x) {
    100          
283 116664           frac_col_left = x_scale;
284 176517 100         while (frac_col_left >= frac_col_to_fill) {
    100          
285 240972 100         for (ch = 0; ch < channels; ++ch)
    100          
286 181119           accum[ch] += frac_col_to_fill * in[in_x].channel[ch];
287              
288 61413 50         if (channels == 2 || channels == 4) {
    100          
    50          
    50          
289 1560           int alpha_chan = channels - 1;
290 1560           double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
291 1560 100         if (alpha) {
    0          
292 6088 100         for (ch = 0; ch < alpha_chan; ++ch) {
    0          
293 4566           IM_WORK_T val = IM_ROUND(accum[ch] / alpha);
294 4566 50         out[out_x].channel[ch] = IM_LIMIT(val);
    50          
    0          
295             }
296             }
297             else {
298 152 100         for (ch = 0; ch < alpha_chan; ++ch) {
    0          
299             /* See RT #32324 (and mention above) */
300 114           out[out_x].channel[ch] = 0;
301             }
302             }
303 1560 50         out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
    50          
    0          
304             }
305             else {
306 233172 100         for (ch = 0; ch < channels; ++ch) {
    100          
307 174879           IM_WORK_T val = IM_ROUND(accum[ch]);
308 174879 50         out[out_x].channel[ch] = IM_LIMIT(val);
    50          
    50          
309             }
310             }
311 240972 100         for (ch = 0; ch < channels; ++ch)
    100          
312 181119           accum[ch] = 0;
313 59853           frac_col_left -= frac_col_to_fill;
314 59853           frac_col_to_fill = 1.0;
315 59853           ++out_x;
316             }
317              
318 116664 50         if (frac_col_left > 0) {
    100          
319 299416 100         for (ch = 0; ch < channels; ++ch) {
    100          
320 224992           accum[ch] += frac_col_left * in[in_x].channel[ch];
321             }
322 74424           frac_col_to_fill -= frac_col_left;
323             }
324             }
325              
326 792 50         if (out_x < out_width-1 || out_x > out_width) {
    50          
    50          
    50          
327 0           i_fatal(3, "Internal error: out_x #" i_DF " out of range (width %" i_DF ")", i_DFc(out_x),
328             i_DFc(out_width));
329             }
330            
331 792 100         if (out_x < out_width) {
    100          
332 372 100         for (ch = 0; ch < channels; ++ch) {
    100          
333 289           accum[ch] += frac_col_to_fill * in[in_width-1].channel[ch];
334             }
335 123 50         if (channels == 2 || channels == 4) {
    100          
    50          
    50          
336 40           int alpha_chan = channels - 1;
337 40           double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
338 40 100         if (alpha) {
    0          
339 104 100         for (ch = 0; ch < alpha_chan; ++ch) {
    0          
340 78           IM_WORK_T val = IM_ROUND(accum[ch] / alpha);
341 78 50         out[out_x].channel[ch] = IM_LIMIT(val);
    50          
    0          
342             }
343             }
344             else {
345 56 100         for (ch = 0; ch < alpha_chan; ++ch) {
    0          
346             /* See RT #32324 (and mention above) */
347 42           out[out_x].channel[ch] = 0;
348             }
349             }
350 40 50         out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
    50          
    0          
351             }
352             else {
353 172 100         for (ch = 0; ch < channels; ++ch) {
    100          
354 129           IM_WORK_T val = IM_ROUND(accum[ch]);
355 129 50         out[out_x].channel[ch] = IM_LIMIT(val);
    50          
    50          
356             }
357             }
358             }
359 792           }
360              
361             #/code