]> git.imager.perl.org - imager.git/blob - scale.im
move TIFF into its own module
[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, int 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                  int width, int channels);
33 static void
34 IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width, 
35                             i_fcolor const *in, int 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, int x_out, int y_out) {
53   i_img *result;
54   i_fcolor *accum_row = NULL;
55   int x, y, ch;
56   int accum_row_bytes;
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
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
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
109   in_row     = mymalloc(in_row_bytes);
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) {
116       /* no vertical scaling, just load it */
117 #ifdef IM_EIGHT_BIT
118       int x, ch;
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
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       }
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) {
145             IM_GLIN(src, 0, src->xsize, rowsread, in_row);
146             ++rowsread;
147           }
148           /* else just use the last row read */
149
150           rowsleft = y_scale;
151         }
152         if (rowsleft < fracrowtofill) {
153           IM_SUFFIX(accum_output_row)(accum_row, rowsleft, in_row, 
154                                       src->xsize, src->channels);
155           fracrowtofill -= rowsleft;
156           rowsleft = 0;
157         }
158         else {
159           IM_SUFFIX(accum_output_row)(accum_row, fracrowtofill, in_row, 
160                                       src->xsize, src->channels);
161           rowsleft -= fracrowtofill;
162           fracrowtofill = 0;
163         }
164       }
165     }
166     /* we've accumulated a vertically scaled row */
167     if (x_out == src->xsize) {
168 #if IM_EIGHT_BIT
169       int x, ch;
170       /* no need to scale, but we need to convert it */
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           }
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           }
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         }
200       }
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);
210     }
211   }
212   myfree(in_row);
213   myfree(xscale_row);
214 #/code
215   myfree(accum_row);
216
217   return result;
218 }
219
220 static void
221 zero_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
226      safe in general under ANSI C.
227      memset() is slightly faster.
228   */
229   for (x = 0; x < width; ++x) {
230     for (ch = 0; ch < channels; ++ch)
231       row[x].channel[ch] = 0.0;
232   }
233 }
234
235 #code
236
237 static void
238 IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
239                  int width, int channels) {
240   int x, ch;
241
242   /* it's tempting to change this into a pointer iteration loop but
243      modern CPUs do the indexing as part of the instruction */
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       }
257     }
258   }
259 }
260
261 static void
262 IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width, 
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
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         }
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         }
295         out[out_x].channel[alpha_chan] = IM_LIMIT(IM_ROUND(accum[alpha_chan]));
296       }
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;
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];
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       }
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       }
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       }
348     }
349   }
350 }
351
352 #/code