373d5a18d8fcfa2fde85cfa4db02866509793180
[imager.git] / img8.c
1 #define IMAGER_NO_CONTEXT
2
3 #include "imager.h"
4 #include "imageri.h"
5
6 static int i_ppix_d(i_img *im, i_img_dim x, i_img_dim y, const i_color *val);
7 static int i_gpix_d(i_img *im, i_img_dim x, i_img_dim y, i_color *val);
8 static i_img_dim i_glin_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals);
9 static i_img_dim i_plin_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals);
10 static int i_ppixf_d(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *val);
11 static int i_gpixf_d(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *val);
12 static i_img_dim i_glinf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals);
13 static i_img_dim i_plinf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals);
14 static i_img_dim i_gsamp_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samps, const int *chans, int chan_count);
15 static i_img_dim i_gsampf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samps, const int *chans, int chan_count);
16 static i_img_dim i_psamp_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_sample_t *samps, const int *chans, int chan_count);
17 static i_img_dim i_psampf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fsample_t *samps, const int *chans, int chan_count);
18
19 /*
20 =item IIM_base_8bit_direct (static)
21
22 A static i_img object used to initialize direct 8-bit per sample images.
23
24 =cut
25 */
26 static i_img IIM_base_8bit_direct =
27 {
28   0, /* channels set */
29   0, 0, 0, /* xsize, ysize, bytes */
30   ~0U, /* ch_mask */
31   i_8_bits, /* bits */
32   i_direct_type, /* type */
33   0, /* virtual */
34   NULL, /* idata */
35   { 0, 0, NULL }, /* tags */
36   NULL, /* ext_data */
37
38   i_ppix_d, /* i_f_ppix */
39   i_ppixf_d, /* i_f_ppixf */
40   i_plin_d, /* i_f_plin */
41   i_plinf_d, /* i_f_plinf */
42   i_gpix_d, /* i_f_gpix */
43   i_gpixf_d, /* i_f_gpixf */
44   i_glin_d, /* i_f_glin */
45   i_glinf_d, /* i_f_glinf */
46   i_gsamp_d, /* i_f_gsamp */
47   i_gsampf_d, /* i_f_gsampf */
48
49   NULL, /* i_f_gpal */
50   NULL, /* i_f_ppal */
51   NULL, /* i_f_addcolors */
52   NULL, /* i_f_getcolors */
53   NULL, /* i_f_colorcount */
54   NULL, /* i_f_maxcolors */
55   NULL, /* i_f_findcolor */
56   NULL, /* i_f_setcolors */
57
58   NULL, /* i_f_destroy */
59
60   i_gsamp_bits_fb,
61   NULL, /* i_f_psamp_bits */
62
63   i_psamp_d,
64   i_psampf_d
65 };
66
67 /*static void set_8bit_direct(i_img *im) {
68   im->i_f_ppix = i_ppix_d;
69   im->i_f_ppixf = i_ppixf_d;
70   im->i_f_plin = i_plin_d;
71   im->i_f_plinf = i_plinf_d;
72   im->i_f_gpix = i_gpix_d;
73   im->i_f_gpixf = i_gpixf_d;
74   im->i_f_glin = i_glin_d;
75   im->i_f_glinf = i_glinf_d;
76   im->i_f_gpal = NULL;
77   im->i_f_ppal = NULL;
78   im->i_f_addcolor = NULL;
79   im->i_f_getcolor = NULL;
80   im->i_f_colorcount = NULL;
81   im->i_f_findcolor = NULL;
82   }*/
83
84 /*
85 =item i_img_8_new(x, y, ch)
86
87 =category Image creation/destruction
88
89 =synopsis i_img *img = i_img_8_new(width, height, channels);
90
91 Creates a new image object I<x> pixels wide, and I<y> pixels high with
92 I<ch> channels.
93
94 =cut
95 */
96
97
98 i_img *
99 im_img_8_new(pIMCTX, i_img_dim x,i_img_dim y,int ch) {
100   i_img *im;
101
102   im_log((aIMCTX, 1,"im_img_8_new(x %" i_DF ", y %" i_DF ", ch %d)\n",
103           i_DFc(x), i_DFc(y), ch));
104
105   im = im_img_empty_ch(aIMCTX, NULL,x,y,ch);
106   
107   im_log((aIMCTX, 1,"(%p) <- IIM_new\n",im));
108   return im;
109 }
110
111 /* 
112 =item i_img_empty(im, x, y)
113
114 Re-new image reference (assumes 3 channels)
115
116    im - Image pointer
117    x - xsize of destination image
118    y - ysize of destination image
119
120 **FIXME** what happens if a live image is passed in here?
121
122 Should this just call i_img_empty_ch()?
123
124 =cut
125 */
126
127 i_img *
128 im_img_empty(pIMCTX, i_img *im,i_img_dim x,i_img_dim y) {
129   im_log((aIMCTX, 1,"i_img_empty(*im %p, x %" i_DF ", y %" i_DF ")\n",
130           im, i_DFc(x), i_DFc(y)));
131   return im_img_empty_ch(aIMCTX, im, x, y, 3);
132 }
133
134 /* 
135 =item i_img_empty_ch(im, x, y, ch)
136
137 Re-new image reference 
138
139    im - Image pointer
140    x  - xsize of destination image
141    y  - ysize of destination image
142    ch - number of channels
143
144 =cut
145 */
146
147 i_img *
148 im_img_empty_ch(pIMCTX, i_img *im,i_img_dim x,i_img_dim y,int ch) {
149   size_t bytes;
150
151   im_log((aIMCTX, 1,"i_img_empty_ch(*im %p, x %" i_DF ", y %" i_DF ", ch %d)\n",
152           im, i_DFc(x), i_DFc(y), ch));
153
154   if (x < 1 || y < 1) {
155     im_push_error(aIMCTX, 0, "Image sizes must be positive");
156     return NULL;
157   }
158   if (ch < 1 || ch > MAXCHANNELS) {
159     im_push_errorf(aIMCTX, 0, "channels must be between 1 and %d", MAXCHANNELS);
160     return NULL;
161   }
162   /* check this multiplication doesn't overflow */
163   bytes = x*y*ch;
164   if (bytes / y / ch != x) {
165     im_push_errorf(aIMCTX, 0, "integer overflow calculating image allocation");
166     return NULL;
167   }
168
169   if (im == NULL)
170     im = im_img_alloc(aIMCTX);
171
172   memcpy(im, &IIM_base_8bit_direct, sizeof(i_img));
173   i_tags_new(&im->tags);
174   im->xsize    = x;
175   im->ysize    = y;
176   im->channels = ch;
177   im->ch_mask  = MAXINT;
178   im->bytes=bytes;
179   if ( (im->idata=mymalloc(im->bytes)) == NULL) 
180     im_fatal(aIMCTX, 2,"malloc() error\n"); 
181   memset(im->idata,0,(size_t)im->bytes);
182   
183   im->ext_data = NULL;
184
185   im_img_init(aIMCTX, im);
186   
187   im_log((aIMCTX, 1,"(%p) <- i_img_empty_ch\n",im));
188   return im;
189 }
190
191 /*
192 =head2 8-bit per sample image internal functions
193
194 These are the functions installed in an 8-bit per sample image.
195
196 =over
197
198 =item i_ppix_d(im, x, y, col)
199
200 Internal function.
201
202 This is the function kept in the i_f_ppix member of an i_img object.
203 It does a normal store of a pixel into the image with range checking.
204
205 Returns 0 if the pixel could be set, -1 otherwise.
206
207 =cut
208 */
209 static
210 int
211 i_ppix_d(i_img *im, i_img_dim x, i_img_dim y, const i_color *val) {
212   int ch;
213   
214   if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
215     for(ch=0;ch<im->channels;ch++)
216       if (im->ch_mask&(1<<ch)) 
217         im->idata[(x+y*im->xsize)*im->channels+ch]=val->channel[ch];
218     return 0;
219   }
220   return -1; /* error was clipped */
221 }
222
223 /*
224 =item i_gpix_d(im, x, y, &col)
225
226 Internal function.
227
228 This is the function kept in the i_f_gpix member of an i_img object.
229 It does normal retrieval of a pixel from the image with range checking.
230
231 Returns 0 if the pixel could be set, -1 otherwise.
232
233 =cut
234 */
235 static
236 int 
237 i_gpix_d(i_img *im, i_img_dim x, i_img_dim y, i_color *val) {
238   int ch;
239   if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
240     for(ch=0;ch<im->channels;ch++) 
241       val->channel[ch]=im->idata[(x+y*im->xsize)*im->channels+ch];
242     return 0;
243   }
244   for(ch=0;ch<im->channels;ch++) val->channel[ch] = 0;
245   return -1; /* error was cliped */
246 }
247
248 /*
249 =item i_glin_d(im, l, r, y, vals)
250
251 Reads a line of data from the image, storing the pixels at vals.
252
253 The line runs from (l,y) inclusive to (r,y) non-inclusive
254
255 vals should point at space for (r-l) pixels.
256
257 l should never be less than zero (to avoid confusion about where to
258 put the pixels in vals).
259
260 Returns the number of pixels copied (eg. if r, l or y is out of range)
261
262 =cut
263 */
264 static
265 i_img_dim
266 i_glin_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals) {
267   int ch;
268   i_img_dim count, i;
269   unsigned char *data;
270   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
271     if (r > im->xsize)
272       r = im->xsize;
273     data = im->idata + (l+y*im->xsize) * im->channels;
274     count = r - l;
275     for (i = 0; i < count; ++i) {
276       for (ch = 0; ch < im->channels; ++ch)
277         vals[i].channel[ch] = *data++;
278     }
279     return count;
280   }
281   else {
282     return 0;
283   }
284 }
285
286 /*
287 =item i_plin_d(im, l, r, y, vals)
288
289 Writes a line of data into the image, using the pixels at vals.
290
291 The line runs from (l,y) inclusive to (r,y) non-inclusive
292
293 vals should point at (r-l) pixels.
294
295 l should never be less than zero (to avoid confusion about where to
296 get the pixels in vals).
297
298 Returns the number of pixels copied (eg. if r, l or y is out of range)
299
300 =cut
301 */
302 static
303 i_img_dim
304 i_plin_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals) {
305   int ch;
306   i_img_dim count, i;
307   unsigned char *data;
308   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
309     if (r > im->xsize)
310       r = im->xsize;
311     data = im->idata + (l+y*im->xsize) * im->channels;
312     count = r - l;
313     for (i = 0; i < count; ++i) {
314       for (ch = 0; ch < im->channels; ++ch) {
315         if (im->ch_mask & (1 << ch)) 
316           *data = vals[i].channel[ch];
317         ++data;
318       }
319     }
320     return count;
321   }
322   else {
323     return 0;
324   }
325 }
326
327 /*
328 =item i_ppixf_d(im, x, y, val)
329
330 =cut
331 */
332 static
333 int
334 i_ppixf_d(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *val) {
335   int ch;
336   
337   if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
338     for(ch=0;ch<im->channels;ch++)
339       if (im->ch_mask&(1<<ch)) {
340         im->idata[(x+y*im->xsize)*im->channels+ch] = 
341           SampleFTo8(val->channel[ch]);
342       }
343     return 0;
344   }
345   return -1; /* error was clipped */
346 }
347
348 /*
349 =item i_gpixf_d(im, x, y, val)
350
351 =cut
352 */
353 static
354 int
355 i_gpixf_d(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *val) {
356   int ch;
357   if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
358     for(ch=0;ch<im->channels;ch++) {
359       val->channel[ch] = 
360         Sample8ToF(im->idata[(x+y*im->xsize)*im->channels+ch]);
361     }
362     return 0;
363   }
364   return -1; /* error was cliped */
365 }
366
367 /*
368 =item i_glinf_d(im, l, r, y, vals)
369
370 Reads a line of data from the image, storing the pixels at vals.
371
372 The line runs from (l,y) inclusive to (r,y) non-inclusive
373
374 vals should point at space for (r-l) pixels.
375
376 l should never be less than zero (to avoid confusion about where to
377 put the pixels in vals).
378
379 Returns the number of pixels copied (eg. if r, l or y is out of range)
380
381 =cut
382 */
383 static
384 i_img_dim
385 i_glinf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals) {
386   int ch;
387   i_img_dim count, i;
388   unsigned char *data;
389   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
390     if (r > im->xsize)
391       r = im->xsize;
392     data = im->idata + (l+y*im->xsize) * im->channels;
393     count = r - l;
394     for (i = 0; i < count; ++i) {
395       for (ch = 0; ch < im->channels; ++ch)
396         vals[i].channel[ch] = Sample8ToF(*data++);
397     }
398     return count;
399   }
400   else {
401     return 0;
402   }
403 }
404
405 /*
406 =item i_plinf_d(im, l, r, y, vals)
407
408 Writes a line of data into the image, using the pixels at vals.
409
410 The line runs from (l,y) inclusive to (r,y) non-inclusive
411
412 vals should point at (r-l) pixels.
413
414 l should never be less than zero (to avoid confusion about where to
415 get the pixels in vals).
416
417 Returns the number of pixels copied (eg. if r, l or y is out of range)
418
419 =cut
420 */
421 static
422 i_img_dim
423 i_plinf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals) {
424   int ch;
425   i_img_dim count, i;
426   unsigned char *data;
427   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
428     if (r > im->xsize)
429       r = im->xsize;
430     data = im->idata + (l+y*im->xsize) * im->channels;
431     count = r - l;
432     for (i = 0; i < count; ++i) {
433       for (ch = 0; ch < im->channels; ++ch) {
434         if (im->ch_mask & (1 << ch)) 
435           *data = SampleFTo8(vals[i].channel[ch]);
436         ++data;
437       }
438     }
439     return count;
440   }
441   else {
442     return 0;
443   }
444 }
445
446 /*
447 =item i_gsamp_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samps, int *chans, int chan_count)
448
449 Reads sample values from im for the horizontal line (l, y) to (r-1,y)
450 for the channels specified by chans, an array of int with chan_count
451 elements.
452
453 Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
454
455 =cut
456 */
457 static
458 i_img_dim
459 i_gsamp_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samps, 
460               const int *chans, int chan_count) {
461   int ch;
462   i_img_dim count, i, w;
463   unsigned char *data;
464
465   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
466     if (r > im->xsize)
467       r = im->xsize;
468     data = im->idata + (l+y*im->xsize) * im->channels;
469     w = r - l;
470     count = 0;
471
472     if (chans) {
473       /* make sure we have good channel numbers */
474       for (ch = 0; ch < chan_count; ++ch) {
475         if (chans[ch] < 0 || chans[ch] >= im->channels) {
476           dIMCTXim(im);
477           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
478           return 0;
479         }
480       }
481       for (i = 0; i < w; ++i) {
482         for (ch = 0; ch < chan_count; ++ch) {
483           *samps++ = data[chans[ch]];
484           ++count;
485         }
486         data += im->channels;
487       }
488     }
489     else {
490       if (chan_count <= 0 || chan_count > im->channels) {
491         dIMCTXim(im);
492         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
493                       chan_count);
494         return 0;
495       }
496       for (i = 0; i < w; ++i) {
497         for (ch = 0; ch < chan_count; ++ch) {
498           *samps++ = data[ch];
499           ++count;
500         }
501         data += im->channels;
502       }
503     }
504
505     return count;
506   }
507   else {
508     return 0;
509   }
510 }
511
512 /*
513 =item i_gsampf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samps, int *chans, int chan_count)
514
515 Reads sample values from im for the horizontal line (l, y) to (r-1,y)
516 for the channels specified by chan_mask, where bit 0 is the first
517 channel.
518
519 Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
520
521 =cut
522 */
523 static
524 i_img_dim
525 i_gsampf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samps, 
526            const int *chans, int chan_count) {
527   int ch;
528   i_img_dim count, i, w;
529   unsigned char *data;
530   for (ch = 0; ch < chan_count; ++ch) {
531     if (chans[ch] < 0 || chans[ch] >= im->channels) {
532       dIMCTXim(im);
533       im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
534     }
535   }
536   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
537     if (r > im->xsize)
538       r = im->xsize;
539     data = im->idata + (l+y*im->xsize) * im->channels;
540     w = r - l;
541     count = 0;
542
543     if (chans) {
544       /* make sure we have good channel numbers */
545       for (ch = 0; ch < chan_count; ++ch) {
546         if (chans[ch] < 0 || chans[ch] >= im->channels) {
547           dIMCTXim(im);
548           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
549           return 0;
550         }
551       }
552       for (i = 0; i < w; ++i) {
553         for (ch = 0; ch < chan_count; ++ch) {
554           *samps++ = Sample8ToF(data[chans[ch]]);
555           ++count;
556         }
557         data += im->channels;
558       }
559     }
560     else {
561       if (chan_count <= 0 || chan_count > im->channels) {
562         dIMCTXim(im);
563         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
564                       chan_count);
565         return 0;
566       }
567       for (i = 0; i < w; ++i) {
568         for (ch = 0; ch < chan_count; ++ch) {
569           *samps++ = Sample8ToF(data[ch]);
570           ++count;
571         }
572         data += im->channels;
573       }
574     }
575     return count;
576   }
577   else {
578     return 0;
579   }
580 }
581
582 /*
583 =item i_psamp_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samps, int *chans, int chan_count)
584
585 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
586 for the channels specified by chans, an array of int with chan_count
587 elements.
588
589 Returns the number of samples written (which should be (r-l) *
590 bits_set(chan_mask)
591
592 =cut
593 */
594
595 static
596 i_img_dim
597 i_psamp_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
598           const i_sample_t *samps, const int *chans, int chan_count) {
599   int ch;
600   i_img_dim count, i, w;
601   unsigned char *data;
602
603   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
604     if (r > im->xsize)
605       r = im->xsize;
606     data = im->idata + (l+y*im->xsize) * im->channels;
607     w = r - l;
608     count = 0;
609
610     if (chans) {
611       /* make sure we have good channel numbers */
612       /* and test if all channels specified are in the mask */
613       int all_in_mask = 1;
614       for (ch = 0; ch < chan_count; ++ch) {
615         if (chans[ch] < 0 || chans[ch] >= im->channels) {
616           dIMCTXim(im);
617           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
618           return -1;
619         }
620         if (!((1 << chans[ch]) & im->ch_mask))
621           all_in_mask = 0;
622       }
623       if (all_in_mask) {
624         for (i = 0; i < w; ++i) {
625           for (ch = 0; ch < chan_count; ++ch) {
626             data[chans[ch]] = *samps++;
627             ++count;
628           }
629           data += im->channels;
630         }
631       }
632       else {
633         for (i = 0; i < w; ++i) {
634           for (ch = 0; ch < chan_count; ++ch) {
635             if (im->ch_mask & (1 << (chans[ch])))
636               data[chans[ch]] = *samps;
637             ++samps;
638             ++count;
639           }
640           data += im->channels;
641         }
642       }
643     }
644     else {
645       if (chan_count <= 0 || chan_count > im->channels) {
646         dIMCTXim(im);
647         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
648                       chan_count);
649         return -1;
650       }
651       for (i = 0; i < w; ++i) {
652         unsigned mask = 1;
653         for (ch = 0; ch < chan_count; ++ch) {
654           if (im->ch_mask & mask)
655             data[ch] = *samps;
656           ++samps;
657           ++count;
658           mask <<= 1;
659         }
660         data += im->channels;
661       }
662     }
663
664     return count;
665   }
666   else {
667     dIMCTXim(im);
668     i_push_error(0, "Image position outside of image");
669     return -1;
670   }
671 }
672
673 /*
674 =item i_psampf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fsample_t *samps, int *chans, int chan_count)
675
676 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
677 for the channels specified by chans, an array of int with chan_count
678 elements.
679
680 Returns the number of samples written (which should be (r-l) *
681 bits_set(chan_mask)
682
683 =cut
684 */
685
686 static
687 i_img_dim
688 i_psampf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
689           const i_fsample_t *samps, const int *chans, int chan_count) {
690   int ch;
691   i_img_dim count, i, w;
692   unsigned char *data;
693
694   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
695     if (r > im->xsize)
696       r = im->xsize;
697     data = im->idata + (l+y*im->xsize) * im->channels;
698     w = r - l;
699     count = 0;
700
701     if (chans) {
702       /* make sure we have good channel numbers */
703       /* and test if all channels specified are in the mask */
704       int all_in_mask = 1;
705       for (ch = 0; ch < chan_count; ++ch) {
706         if (chans[ch] < 0 || chans[ch] >= im->channels) {
707           dIMCTXim(im);
708           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
709           return -1;
710         }
711         if (!((1 << chans[ch]) & im->ch_mask))
712           all_in_mask = 0;
713       }
714       if (all_in_mask) {
715         for (i = 0; i < w; ++i) {
716           for (ch = 0; ch < chan_count; ++ch) {
717             data[chans[ch]] = SampleFTo8(*samps);
718             ++samps;
719             ++count;
720           }
721           data += im->channels;
722         }
723       }
724       else {
725         for (i = 0; i < w; ++i) {
726           for (ch = 0; ch < chan_count; ++ch) {
727             if (im->ch_mask & (1 << (chans[ch])))
728               data[chans[ch]] = SampleFTo8(*samps);
729             ++samps;
730             ++count;
731           }
732           data += im->channels;
733         }
734       }
735     }
736     else {
737       if (chan_count <= 0 || chan_count > im->channels) {
738         dIMCTXim(im);
739         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
740                       chan_count);
741         return -1;
742       }
743       for (i = 0; i < w; ++i) {
744         unsigned mask = 1;
745         for (ch = 0; ch < chan_count; ++ch) {
746           if (im->ch_mask & mask)
747             data[ch] = SampleFTo8(*samps);
748           ++samps;
749           ++count;
750           mask <<= 1;
751         }
752         data += im->channels;
753       }
754     }
755
756     return count;
757   }
758   else {
759     dIMCTXim(im);
760     i_push_error(0, "Image position outside of image");
761     return -1;
762   }
763 }
764
765 /*
766 =back
767
768 =head1 AUTHOR
769
770 Arnar M. Hrafnkelsson <addi@umich.edu>
771
772 Tony Cook <tony@develop-help.com>
773
774 =head1 SEE ALSO
775
776 L<Imager>
777
778 =cut
779 */