]> git.imager.perl.org - imager.git/blob - scale.im
allow imcover to use a non-default perl
[imager.git] / scale.im
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 i_scale_mixing(i_img *src, i_img_dim x_out, i_img_dim y_out) {
53   i_img *result;
54   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   mm_log((1, "i_scale_mixing(src %p, out(" i_DFp "))\n", 
63           src, i_DFcp(x_out, y_out)));
64
65   i_clear_error();
66
67   if (x_out <= 0) {
68     i_push_errorf(0, "output width %" i_DF " invalid", i_DFc(x_out));
69     return NULL;
70   }
71   if (y_out <= 0) {
72     i_push_errorf(0, "output height %" i_DF " invalid", i_DFc(y_out));
73     return NULL;
74   }
75
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
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;
97   size_t in_row_bytes, out_row_bytes;
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
110   in_row     = mymalloc(in_row_bytes);
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) {
117       /* no vertical scaling, just load it */
118 #ifdef IM_EIGHT_BIT
119       i_img_dim x;
120       int ch;
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
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       }
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) {
147             IM_GLIN(src, 0, src->xsize, rowsread, in_row);
148             ++rowsread;
149           }
150           /* else just use the last row read */
151
152           rowsleft = y_scale;
153         }
154         if (rowsleft < fracrowtofill) {
155           IM_SUFFIX(accum_output_row)(accum_row, rowsleft, in_row, 
156                                       src->xsize, src->channels);
157           fracrowtofill -= rowsleft;
158           rowsleft = 0;
159         }
160         else {
161           IM_SUFFIX(accum_output_row)(accum_row, fracrowtofill, in_row, 
162                                       src->xsize, src->channels);
163           rowsleft -= fracrowtofill;
164           fracrowtofill = 0;
165         }
166       }
167     }
168     /* we've accumulated a vertically scaled row */
169     if (x_out == src->xsize) {
170 #if IM_EIGHT_BIT
171       i_img_dim x;
172       int ch;
173       /* no need to scale, but we need to convert it */
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           }
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           }
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         }
203       }
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);
213     }
214   }
215   myfree(in_row);
216   myfree(xscale_row);
217 #/code
218   myfree(accum_row);
219
220   return result;
221 }
222
223 static void
224 zero_row(i_fcolor *row, i_img_dim width, int channels) {
225   i_img_dim x;
226   int ch;
227
228   /* with IEEE floats we could just use memset() but that's not
229      safe in general under ANSI C.
230      memset() is slightly faster.
231   */
232   for (x = 0; x < width; ++x) {
233     for (ch = 0; ch < channels; ++ch)
234       row[x].channel[ch] = 0.0;
235   }
236 }
237
238 #code
239
240 static void
241 IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
242                  i_img_dim width, int channels) {
243   i_img_dim x;
244   int ch;
245
246   /* it's tempting to change this into a pointer iteration loop but
247      modern CPUs do the indexing as part of the instruction */
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       }
261     }
262   }
263 }
264
265 static void
266 IM_SUFFIX(horizontal_scale)(IM_COLOR *out, i_img_dim out_width, 
267                  i_fcolor const *in, i_img_dim in_width,
268                  int channels) {
269   double frac_col_to_fill, frac_col_left;
270   i_img_dim in_x;
271   i_img_dim out_x;
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
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         }
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         }
299         out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
300       }
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;
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];
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       }
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       }
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       }
352     }
353   }
354 }
355
356 #/code