]> git.imager.perl.org - imager.git/blob - convert.im
addi style makemap could potentially read one past the end of an array
[imager.git] / convert.im
1 /*
2 =head1 NAME
3
4   convert.im - image conversions
5
6 =head1 SYNOPSIS
7
8   out = i_convert(srcimage, coeff, outchans, inchans)
9
10 =head1 DESCRIPTION
11
12 Converts images from one format to another, typically in this case for
13 converting from RGBA to greyscale and back.
14
15 =over
16
17 =cut
18 */
19
20 #define IMAGER_NO_CONTEXT
21 #include "imager.h"
22
23 struct chan_copy {
24   /* channels to copy */
25   int copy_count;
26   int from[MAXCHANNELS];
27   int to[MAXCHANNELS];
28
29   /* channels to zero */
30   int zero_count;
31   int zero[MAXCHANNELS];
32
33   /* channels to set to maxsample */
34   int one_count;
35   int one[MAXCHANNELS];
36 };
37
38 static int 
39 is_channel_copy(i_img *im, const double *coeff, 
40                 int outchan, int inchan, 
41                 struct chan_copy *info);
42
43 static i_img *
44 convert_via_copy(i_img *im, i_img *src, struct chan_copy *info);
45
46 /*
47 =item i_convert(src, coeff, outchan, inchan)
48
49 Converts the image src into another image.
50
51 coeff contains the co-efficients of an outchan x inchan matrix, for
52 each output pixel:
53
54               coeff[0], coeff[1] ...
55   im[x,y] = [ coeff[inchan], coeff[inchan+1]...        ] * [ src[x,y], 1]
56               ...              coeff[inchan*outchan-1]
57
58 If im has the wrong number of channels or is the wrong size then
59 i_convert() will re-create it.
60
61 Now handles images with more than 8-bits/sample.
62
63 =cut
64 */
65
66 i_img *
67 i_convert(i_img *src, const double *coeff, int outchan, int inchan) {
68   double work[MAXCHANNELS];
69   i_img_dim x, y;
70   int i, j;
71   int ilimit;
72   i_img *im = NULL;
73   dIMCTXim(src);
74
75   im_log((aIMCTX,1,"i_convert(im %p, src %p, coeff %p,outchan %d, inchan %d)\n",
76           im, src, coeff, outchan, inchan));
77  
78   im_clear_error(aIMCTX);
79
80   ilimit = inchan;
81   if (ilimit > src->channels)
82     ilimit = src->channels;
83   if (outchan > MAXCHANNELS) {
84     im_push_error(aIMCTX, 0, "cannot have outchan > MAXCHANNELS");
85     return 0;
86   }
87
88   if (src->type == i_direct_type) {
89     struct chan_copy info;
90     im = i_sametype_chans(src, src->xsize, src->ysize, outchan);
91     
92     if (is_channel_copy(src, coeff, outchan, inchan, &info)) {
93       return convert_via_copy(im, src, &info);
94     }
95     else {
96 #code src->bits <= i_8_bits
97       IM_COLOR *vals;
98       
99       /* we can always allocate a single scanline of i_color */
100       vals = mymalloc(sizeof(IM_COLOR) * src->xsize); /* checked 04Jul05 tonyc */
101       for (y = 0; y < src->ysize; ++y) {
102         IM_GLIN(src, 0, src->xsize, y, vals);
103         for (x = 0; x < src->xsize; ++x) {
104           for (j = 0; j < outchan; ++j) {
105             work[j] = 0;
106             for (i = 0; i < ilimit; ++i) {
107               work[j] += coeff[i+inchan*j] * vals[x].channel[i];
108             }
109             if (i < inchan) {
110               work[j] += coeff[i+inchan*j] * IM_SAMPLE_MAX;
111             }
112           }
113           for (j = 0; j < outchan; ++j) {
114             if (work[j] < 0)
115               vals[x].channel[j] = 0;
116             else if (work[j] >= IM_SAMPLE_MAX)
117               vals[x].channel[j] = IM_SAMPLE_MAX;
118             else
119               vals[x].channel[j] = work[j];
120           }
121         }
122         IM_PLIN(im, 0, src->xsize, y, vals);
123       }
124       myfree(vals);
125 #/code
126     }
127   }
128   else {
129     int count;
130     int outcount;
131     int index;
132     i_color *colors;
133     i_palidx *vals;
134
135     im = im_img_pal_new(aIMCTX, src->xsize, src->ysize, outchan, 
136                         i_maxcolors(src));
137
138     /* just translate the color table */
139     count = i_colorcount(src);
140     outcount = i_colorcount(im);
141     /* color table allocated for image, so it must fit */
142     colors = mymalloc(count * sizeof(i_color)); /* check 04Jul05 tonyc */
143     i_getcolors(src, 0, colors, count);
144     for (index = 0; index < count; ++index) {
145       for (j = 0; j < outchan; ++j) {
146         work[j] = 0;
147         for (i = 0; i < ilimit; ++i) {
148           work[j] += coeff[i+inchan*j] * colors[index].channel[i];
149         }
150         if (i < inchan) {
151           work[j] += coeff[i+inchan*j] * 255.9;
152         }
153       }
154       for (j = 0; j < outchan; ++j) {
155         if (work[j] < 0)
156           colors[index].channel[j] = 0;
157         else if (work[j] >= 255)
158           colors[index].channel[j] = 255;
159         else
160           colors[index].channel[j] = work[j];
161       }
162     }
163     if (count < outcount) {
164       i_setcolors(im, 0, colors, count);
165     }
166     else {
167       i_setcolors(im, 0, colors, outcount);
168       i_addcolors(im, colors, count-outcount);
169     }
170     /* and copy the indicies */
171     /* i_palidx is always unsigned char and will never be bigger than short
172        and since a line of 4-byte i_colors can fit then a line of i_palidx
173        will fit */
174     vals = mymalloc(sizeof(i_palidx) * im->xsize); /* checked 4jul05 tonyc */
175     for (y = 0; y < im->ysize; ++y) {
176       i_gpal(src, 0, im->xsize, y, vals);
177       i_ppal(im, 0, im->xsize, y, vals);
178     }
179     myfree(vals);
180     myfree(colors);
181   }
182
183   return im;
184 }
185
186 /*
187 =item is_channel_copy(coeff, outchan, inchan, chan_copy_info)
188
189 Test if the coefficients represent just copying channels around, and
190 initialize lists of the channels to copy, zero or set to max.
191
192 =cut
193 */
194
195 static
196 int is_channel_copy(i_img *im, const double *coeff, int outchan, int inchan, 
197                     struct chan_copy *info) {
198   int srcchan[MAXCHANNELS];
199   int onechan[MAXCHANNELS];
200   int i, j;
201   int ilimit = im->channels > inchan ? inchan : im->channels;
202
203   for (j = 0; j < outchan; ++j) {
204     srcchan[j] = -1;
205     onechan[j] = 0;
206   }
207
208   for (j = 0; j < outchan; ++j) {
209     for (i = 0; i < ilimit; ++i) {
210       if (coeff[i+inchan*j] == 1.0) {
211         if (srcchan[j] != -1) {
212           /* from two or more channels, not a copy */
213           return 0;
214         }
215         srcchan[j] = i;
216       }
217       else if (coeff[i+inchan*j]) {
218         /* some other non-zero value, not a copy */
219         return 0;
220       }
221     }
222     if (i < inchan) {
223       if (coeff[i+inchan*j] == 1.0) {
224         if (srcchan[j] != -1) {
225           /* can't do both */
226           return 0;
227         }
228         onechan[j] = 1;
229       }
230       else if (coeff[i+inchan*j]) {
231         /* some other non-zero value, not a copy */
232         return 0;
233       }
234     }
235   }
236
237   /* build our working data structures */
238   info->copy_count = info->zero_count = info->one_count = 0;
239   for (j = 0; j < outchan; ++j) {
240     if (srcchan[j] != -1) {
241       info->from[info->copy_count] = srcchan[j];
242       info->to[info->copy_count] = j;
243       ++info->copy_count;
244     }
245     else if (onechan[j]) {
246       info->one[info->one_count] = j;
247       ++info->one_count;
248     }
249     else {
250       info->zero[info->zero_count] = j;
251       ++info->zero_count;
252     }
253   }
254
255 #if 0
256   {
257     for (i = 0; i < info->copy_count; ++i) {
258       printf("From %d to %d\n", info->from[i], info->to[i]);
259     }
260     for (i = 0; i < info->one_count; ++i) {
261       printf("One %d\n", info->one[i]);
262     }
263     for (i = 0; i < info->zero_count; ++i) {
264       printf("Zero %d\n", info->zero[i]);
265     }
266     fflush(stdout);
267   }
268 #endif
269
270   return 1;
271 }
272
273 /*
274 =item convert_via_copy(im, src, chan_copy_info)
275
276 Perform a convert that only requires channel copies.
277
278 =cut
279 */
280
281 static i_img *
282 convert_via_copy(i_img *im, i_img *src, struct chan_copy *info) {
283 #code src->bits <= i_8_bits
284   IM_COLOR *in_line = mymalloc(sizeof(IM_COLOR) * src->xsize);
285   IM_COLOR *out_line = mymalloc(sizeof(IM_COLOR) * src->xsize);
286   i_img_dim x, y;
287   int i;
288   IM_COLOR *inp, *outp;
289
290   for (y = 0; y < src->ysize; ++y) {
291     IM_GLIN(src, 0, src->xsize, y, in_line);
292
293     inp = in_line;
294     outp = out_line;
295     for (x = 0; x < src->xsize; ++x) {
296       for (i = 0; i < info->copy_count; ++i) {
297         outp->channel[info->to[i]] = inp->channel[info->from[i]];
298       }
299       for (i = 0; i < info->one_count; ++i) {
300         outp->channel[info->one[i]] = IM_SAMPLE_MAX;
301       }
302       for (i = 0; i < info->zero_count; ++i) {
303         outp->channel[info->zero[i]] = 0;
304       }
305       ++inp;
306       ++outp;
307     }
308     
309     IM_PLIN(im, 0, src->xsize, y, out_line);
310   }
311   
312   myfree(in_line);
313   myfree(out_line);
314 #/code
315       
316   return im;
317 }
318
319 /*
320 =back
321
322 =head1 SEE ALSO
323
324 Imager(3)
325
326 =head1 AUTHOR
327
328 Tony Cook <tony@develop-help.com>
329
330 =cut
331 */