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