File Coverage

scale.im
Criterion Covered Total %
statement 225 279 80.6
branch 156 244 63.9
condition n/a
subroutine n/a
pod n/a
total 381 523 72.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 0 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,
  300            
  1439            
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 1739 50         if (channels == 2 || channels == 4) {
  300 100          
  1439 50          
    50          
253 3652 100         for (x = 0; x < width; ++x) {
  3652 0          
  0            
254 14276 100         for (ch = 0; ch < channels-1; ++ch) {
  14276 0          
  0            
255 10707           accum[x].channel[ch] += in[x].channel[ch] * fraction * in[x].channel[channels-1] / IM_SAMPLE_MAX;
  10707            
  0            
256             }
257 3569           accum[x].channel[channels-1] += in[x].channel[channels-1] * fraction;
  3569            
  0            
258             }
259             }
260             else {
261 249480 100         for (x = 0; x < width; ++x) {
  25823 100          
  223657            
262 991296 100         for (ch = 0; ch < channels; ++ch) {
  102424 100          
  888872            
263 743472           accum[x].channel[ch] += in[x].channel[ch] * fraction;
  76818            
  666654            
264             }
265             }
266             }
267 1739           }
  300            
  1439            
268              
269             static void
270 792           IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width,
  146            
  646            
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;
  146            
  646            
277             int ch;
278 792           double accum[MAXCHANNELS] = { 0 };
  146            
  646            
279            
280 792           frac_col_to_fill = 1.0;
  146            
  646            
281 792           out_x = 0;
  146            
  646            
282 117456 100         for (in_x = 0; in_x < in_width; ++in_x) {
  14374 100          
  103082            
283 116664           frac_col_left = x_scale;
  14228            
  102436            
284 176517 100         while (frac_col_left >= frac_col_to_fill) {
  19712 100          
  156805            
285 240972 100         for (ch = 0; ch < channels; ++ch)
  23496 100          
  217476            
286 181119           accum[ch] += frac_col_to_fill * in[in_x].channel[ch];
  18012            
  163107            
287              
288 59853 50         if (channels == 2 || channels == 4) {
  5484 100          
  54369 50          
    50          
289 1560           int alpha_chan = channels - 1;
  1560            
  0            
290 1560           double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
  1560            
  0            
291 1560 100         if (alpha) {
  1560 0          
  0            
292 6088 100         for (ch = 0; ch < alpha_chan; ++ch) {
  6088 0          
  0            
293 4566           IM_WORK_T val = IM_ROUND(accum[ch] / alpha);
  4566            
  0            
294 4566 50         out[out_x].channel[ch] = IM_LIMIT(val);
  4566 50          
  0 0          
295             }
296             }
297             else {
298 152 100         for (ch = 0; ch < alpha_chan; ++ch) {
  152 0          
  0            
299             /* See RT #32324 (and mention above) */
300 114           out[out_x].channel[ch] = 0;
  114            
  0            
301             }
302             }
303 1560 50         out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
  1560 50          
  0 0          
304             }
305             else {
306 233172 100         for (ch = 0; ch < channels; ++ch) {
  15696 100          
  217476            
307 174879           IM_WORK_T val = IM_ROUND(accum[ch]);
  11772            
  163107            
308 174879 50         out[out_x].channel[ch] = IM_LIMIT(val);
  11772 50          
  163107 50          
309             }
310             }
311 240972 100         for (ch = 0; ch < channels; ++ch)
  23496 100          
  217476            
312 181119           accum[ch] = 0;
  18012            
  163107            
313 59853           frac_col_left -= frac_col_to_fill;
  5484            
  54369            
314 59853           frac_col_to_fill = 1.0;
  5484            
  54369            
315 59853           ++out_x;
  5484            
  54369            
316             }
317              
318 116664 50         if (frac_col_left > 0) {
  14228 100          
  102436            
319 299416 100         for (ch = 0; ch < channels; ++ch) {
  58632 100          
  240784            
320 224992           accum[ch] += frac_col_left * in[in_x].channel[ch];
  44404            
  180588            
321             }
322 74424           frac_col_to_fill -= frac_col_left;
  14228            
  60196            
323             }
324             }
325              
326 792 50         if (out_x < out_width-1 || out_x > out_width) {
  146 50          
  646 50          
    50          
327 0           i_fatal(3, "Internal error: out_x #" i_DF " out of range (width %" i_DF ")", i_DFc(out_x),
  0            
  0            
328             i_DFc(out_width));
329             }
330            
331 792 100         if (out_x < out_width) {
  146 100          
  646            
332 372 100         for (ch = 0; ch < channels; ++ch) {
  284 100          
  88            
333 289           accum[ch] += frac_col_to_fill * in[in_width-1].channel[ch];
  223            
  66            
334             }
335 83 50         if (channels == 2 || channels == 4) {
  61 100          
  22 50          
    50          
336 40           int alpha_chan = channels - 1;
  40            
  0            
337 40           double alpha = accum[alpha_chan] / IM_SAMPLE_MAX;
  40            
  0            
338 40 100         if (alpha) {
  40 0          
  0            
339 104 100         for (ch = 0; ch < alpha_chan; ++ch) {
  104 0          
  0            
340 78           IM_WORK_T val = IM_ROUND(accum[ch] / alpha);
  78            
  0            
341 78 50         out[out_x].channel[ch] = IM_LIMIT(val);
  78 50          
  0 0          
342             }
343             }
344             else {
345 56 100         for (ch = 0; ch < alpha_chan; ++ch) {
  56 0          
  0            
346             /* See RT #32324 (and mention above) */
347 42           out[out_x].channel[ch] = 0;
  42            
  0            
348             }
349             }
350 40 50         out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
  40 50          
  0 0          
351             }
352             else {
353 172 100         for (ch = 0; ch < channels; ++ch) {
  84 100          
  88            
354 129           IM_WORK_T val = IM_ROUND(accum[ch]);
  63            
  66            
355 129 50         out[out_x].channel[ch] = IM_LIMIT(val);
  63 50          
  66 50          
356             }
357             }
358             }
359 792           }
  146            
  646            
360              
361             #/code