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