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
12Converts images from one format to another, typically in this case for
13converting from RGBA to greyscale and back.
14
15=over
16
17=cut
18*/
19
20#define IMAGER_NO_CONTEXT
21#include "imager.h"
22
23struct 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
38static int
39is_channel_copy(i_img *im, const double *coeff,
40		int outchan, int inchan,
41		struct chan_copy *info);
42
43static i_img *
44convert_via_copy(i_img *im, i_img *src, struct chan_copy *info);
45
46/*
47=item i_convert(src, coeff, outchan, inchan)
48
49Converts the image src into another image.
50
51coeff contains the co-efficients of an outchan x inchan matrix, for
52each 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
58If im has the wrong number of channels or is the wrong size then
59i_convert() will re-create it.
60
61Now handles images with more than 8-bits/sample.
62
63=cut
64*/
65
66i_img *
67i_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
189Test if the coefficients represent just copying channels around, and
190initialize lists of the channels to copy, zero or set to max.
191
192=cut
193*/
194
195static
196int 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
276Perform a convert that only requires channel copies.
277
278=cut
279*/
280
281static i_img *
282convert_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
324Imager(3)
325
326=head1 AUTHOR
327
328Tony Cook <tony@develop-help.com>
329
330=cut
331*/
332