cfd4da6a053aca55290c02a590d46cb7c09ed309
[imager.git] / img16.c
1 /*
2 =head1 NAME
3
4 img16.c - implements 16-bit images
5
6 =head1 SYNOPSIS
7
8   i_img *im = i_img_16_new(i_img_dim x, i_img_dim y, int channels);
9   # use like a normal image
10
11 =head1 DESCRIPTION
12
13 Implements 16-bit/sample images.
14
15 This basic implementation is required so that we have some larger 
16 sample image type to work with.
17
18 =over
19
20 =cut
21 */
22
23 #define IMAGER_NO_CONTEXT
24
25 #include "imager.h"
26 #include "imageri.h"
27
28 static int i_ppix_d16(i_img *im, i_img_dim x, i_img_dim y, const i_color *val);
29 static int i_gpix_d16(i_img *im, i_img_dim x, i_img_dim y, i_color *val);
30 static i_img_dim i_glin_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals);
31 static i_img_dim i_plin_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals);
32 static int i_ppixf_d16(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *val);
33 static int i_gpixf_d16(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *val);
34 static i_img_dim i_glinf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals);
35 static i_img_dim i_plinf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals);
36 static i_img_dim i_gsamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samps, 
37                        int const *chans, int chan_count);
38 static i_img_dim i_gsampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samps, 
39                         int const *chans, int chan_count);
40 static i_img_dim i_gsamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *samps, 
41                             int const *chans, int chan_count, int bits);
42 static i_img_dim i_psamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned const *samps, 
43                             int const *chans, int chan_count, int bits);
44 static i_img_dim i_psamp_d16(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);
45 static i_img_dim i_psampf_d16(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);
46
47 /*
48 =item IIM_base_16bit_direct
49
50 Base structure used to initialize a 16-bit/sample image.
51
52 Internal.
53
54 =cut
55 */
56 static i_img IIM_base_16bit_direct =
57 {
58   0, /* channels set */
59   0, 0, 0, /* xsize, ysize, bytes */
60   ~0U, /* ch_mask */
61   i_16_bits, /* bits */
62   i_direct_type, /* type */
63   0, /* virtual */
64   NULL, /* idata */
65   { 0, 0, NULL }, /* tags */
66   NULL, /* ext_data */
67
68   i_ppix_d16, /* i_f_ppix */
69   i_ppixf_d16, /* i_f_ppixf */
70   i_plin_d16, /* i_f_plin */
71   i_plinf_d16, /* i_f_plinf */
72   i_gpix_d16, /* i_f_gpix */
73   i_gpixf_d16, /* i_f_gpixf */
74   i_glin_d16, /* i_f_glin */
75   i_glinf_d16, /* i_f_glinf */
76   i_gsamp_d16, /* i_f_gsamp */
77   i_gsampf_d16, /* i_f_gsampf */
78
79   NULL, /* i_f_gpal */
80   NULL, /* i_f_ppal */
81   NULL, /* i_f_addcolors */
82   NULL, /* i_f_getcolors */
83   NULL, /* i_f_colorcount */
84   NULL, /* i_f_maxcolors */
85   NULL, /* i_f_findcolor */
86   NULL, /* i_f_setcolors */
87
88   NULL, /* i_f_destroy */
89
90   i_gsamp_bits_d16,
91   i_psamp_bits_d16,
92
93   i_psamp_d16,
94   i_psampf_d16
95 };
96
97 /* it's possible some platforms won't have a 16-bit integer type,
98    so we check for one otherwise we work by bytes directly
99
100    We do assume 8-bit char
101
102    "Compaq C V6.4-009 on Compaq Tru64 UNIX V5.1A (Rev. 1885)" says it
103    supports C99, but doesn't supply stdint.h, which is required for
104    both hosted and freestanding implementations.  So guard against it.
105 */
106 #if __STDC_VERSION__ >= 199901L && !defined(OS_dec_osf)
107 /* C99 should define something useful */
108 #include <stdint.h>
109 #ifdef UINT16_MAX
110 typedef uint16_t i_sample16_t;
111 #define GOT16
112 #endif
113 #endif
114
115 /* check out unsigned short */
116 #ifndef GOT16
117 #include <limits.h>
118 #if USHRT_MAX == 65535
119 typedef unsigned short i_sample16_t;
120 #define GOT16
121 #endif
122 #endif
123
124 #ifdef GOT16
125
126 /* we have a real 16-bit unsigned integer */
127 #define STORE16(bytes, offset, word) \
128    (((i_sample16_t *)(bytes))[offset] = (word))
129 #define STORE8as16(bytes, offset, byte) \
130    (((i_sample16_t *)(bytes))[offset] = (byte) * 256 + (byte))
131 #define GET16(bytes, offset) \
132      (((i_sample16_t *)(bytes))[offset])
133 #else
134
135 /* we have to do this the hard way */
136 #define STORE16(bytes, offset, word) \
137    ((((unsigned char *)(bytes))[(offset)*2] = (word) >> 8), \
138     (((unsigned char *)(bytes))[(offset)*2+1] = (word) & 0xFF))
139 #define STORE8as16(bytes, offset, byte) \
140    ((((unsigned char *)(bytes))[(offset)*2] = (byte)), \
141     (((unsigned char *)(bytes))[(offset)*2+1] = (byte)))
142    
143 #define GET16(bytes, offset) \
144    (((unsigned char *)(bytes))[(offset)*2] * 256 \
145     + ((unsigned char *)(bytes))[(offset)*2+1])
146
147 #endif
148
149 #define GET16as8(bytes, offset) \
150      ((((i_sample16_t *)(bytes))[offset]+127) / 257)
151
152 /*
153 =item i_img_16_new(x, y, ch)
154
155 =category Image creation/destruction
156 =synopsis i_img *img = i_img_16_new(width, height, channels);
157
158 Create a new 16-bit/sample image.
159
160 Returns the image on success, or NULL on failure.
161
162 =cut
163 */
164
165 i_img *
166 im_img_16_new(pIMCTX, i_img_dim x, i_img_dim y, int ch) {
167   i_img *im;
168   size_t bytes, line_bytes;
169
170   mm_log((1,"i_img_16_new(x %" i_DF ", y %" i_DF ", ch %d)\n",
171           i_DFc(x), i_DFc(y), ch));
172
173   if (x < 1 || y < 1) {
174     im_push_error(aIMCTX, 0, "Image sizes must be positive");
175     return NULL;
176   }
177   if (ch < 1 || ch > MAXCHANNELS) {
178     im_push_errorf(aIMCTX, 0, "channels must be between 1 and %d", MAXCHANNELS);
179     return NULL;
180   }
181   bytes =  x * y * ch * 2;
182   if (bytes / y / ch / 2 != x) {
183     im_push_errorf(aIMCTX, 0, "integer overflow calculating image allocation");
184     return NULL;
185   }
186   
187   /* basic assumption: we can always allocate a buffer representing a
188      line from the image, otherwise we're going to have trouble
189      working with the image */
190   line_bytes = sizeof(i_fcolor) * x;
191   if (line_bytes / x != sizeof(i_fcolor)) {
192     im_push_error(aIMCTX, 0, "integer overflow calculating scanline allocation");
193     return NULL;
194   }
195
196   im = im_img_alloc(aIMCTX);
197   *im = IIM_base_16bit_direct;
198   i_tags_new(&im->tags);
199   im->xsize = x;
200   im->ysize = y;
201   im->channels = ch;
202   im->bytes = bytes;
203   im->ext_data = NULL;
204   im->idata = mymalloc(im->bytes);
205   memset(im->idata, 0, im->bytes);
206
207   im_img_init(aIMCTX, im);
208
209   return im;
210 }
211
212 /*
213 =item i_img_to_rgb16(im)
214
215 =category Image creation
216
217 Returns a 16-bit/sample version of the supplied image.
218
219 Returns the image on success, or NULL on failure.
220
221 =cut
222 */
223
224 i_img *
225 i_img_to_rgb16(i_img *im) {
226   i_img *targ;
227   i_fcolor *line;
228   i_img_dim y;
229   dIMCTXim(im);
230
231   targ = im_img_16_new(aIMCTX, im->xsize, im->ysize, im->channels);
232   if (!targ)
233     return NULL;
234   line = mymalloc(sizeof(i_fcolor) * im->xsize);
235   for (y = 0; y < im->ysize; ++y) {
236     i_glinf(im, 0, im->xsize, y, line);
237     i_plinf(targ, 0, im->xsize, y, line);
238   }
239
240   myfree(line);
241
242   return targ;
243 }
244
245 static int i_ppix_d16(i_img *im, i_img_dim x, i_img_dim y, const i_color *val) {
246   i_img_dim off;
247   int ch;
248
249   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
250     return -1;
251
252   off = (x + y * im->xsize) * im->channels;
253   if (I_ALL_CHANNELS_WRITABLE(im)) {
254     for (ch = 0; ch < im->channels; ++ch)
255       STORE8as16(im->idata, off+ch, val->channel[ch]);
256   }
257   else {
258     for (ch = 0; ch < im->channels; ++ch)
259       if (im->ch_mask & (1 << ch))
260         STORE8as16(im->idata, off+ch, val->channel[ch]);
261   }
262
263   return 0;
264 }
265
266 static int i_gpix_d16(i_img *im, i_img_dim x, i_img_dim y, i_color *val) {
267   i_img_dim off;
268   int ch;
269
270   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
271     return -1;
272
273   off = (x + y * im->xsize) * im->channels;
274   for (ch = 0; ch < im->channels; ++ch)
275     val->channel[ch] = GET16as8(im->idata, off+ch);
276
277   return 0;
278 }
279
280 static int i_ppixf_d16(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *val) {
281   i_img_dim off;
282   int ch;
283
284   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
285     return -1;
286
287   off = (x + y * im->xsize) * im->channels;
288   if (I_ALL_CHANNELS_WRITABLE(im)) {
289     for (ch = 0; ch < im->channels; ++ch)
290       STORE16(im->idata, off+ch, SampleFTo16(val->channel[ch]));
291   }
292   else {
293     for (ch = 0; ch < im->channels; ++ch)
294       if (im->ch_mask & (1 << ch))
295         STORE16(im->idata, off+ch, SampleFTo16(val->channel[ch]));
296   }
297
298   return 0;
299 }
300
301 static int i_gpixf_d16(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *val) {
302   i_img_dim off;
303   int ch;
304
305   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
306     return -1;
307
308   off = (x + y * im->xsize) * im->channels;
309   for (ch = 0; ch < im->channels; ++ch)
310     val->channel[ch] = Sample16ToF(GET16(im->idata, off+ch));
311
312   return 0;
313 }
314
315 static i_img_dim i_glin_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals) {
316   int ch;
317   i_img_dim count, i;
318   i_img_dim off;
319   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
320     if (r > im->xsize)
321       r = im->xsize;
322     off = (l+y*im->xsize) * im->channels;
323     count = r - l;
324     for (i = 0; i < count; ++i) {
325       for (ch = 0; ch < im->channels; ++ch) {
326         vals[i].channel[ch] = GET16as8(im->idata, off);
327         ++off;
328       }
329     }
330     return count;
331   }
332   else {
333     return 0;
334   }
335 }
336
337 static i_img_dim i_plin_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals) {
338   int ch;
339   i_img_dim count, i;
340   i_img_dim off;
341   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
342     if (r > im->xsize)
343       r = im->xsize;
344     off = (l+y*im->xsize) * im->channels;
345     count = r - l;
346     if (I_ALL_CHANNELS_WRITABLE(im)) {
347       for (i = 0; i < count; ++i) {
348         for (ch = 0; ch < im->channels; ++ch) {
349           STORE8as16(im->idata, off, vals[i].channel[ch]);
350           ++off;
351         }
352       }
353     }
354     else {
355       for (i = 0; i < count; ++i) {
356         for (ch = 0; ch < im->channels; ++ch) {
357           if (im->ch_mask & (1 << ch))
358             STORE8as16(im->idata, off, vals[i].channel[ch]);
359           ++off;
360         }
361       }
362     }
363     return count;
364   }
365   else {
366     return 0;
367   }
368 }
369
370 static i_img_dim i_glinf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals) {
371   int ch;
372   i_img_dim count, i;
373   i_img_dim off;
374   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
375     if (r > im->xsize)
376       r = im->xsize;
377     off = (l+y*im->xsize) * im->channels;
378     count = r - l;
379     for (i = 0; i < count; ++i) {
380       for (ch = 0; ch < im->channels; ++ch) {
381         vals[i].channel[ch] = Sample16ToF(GET16(im->idata, off));
382         ++off;
383       }
384     }
385     return count;
386   }
387   else {
388     return 0;
389   }
390 }
391
392 static i_img_dim i_plinf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals) {
393   int ch;
394   i_img_dim count, i;
395   i_img_dim off;
396   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
397     if (r > im->xsize)
398       r = im->xsize;
399     off = (l+y*im->xsize) * im->channels;
400     count = r - l;
401     if (I_ALL_CHANNELS_WRITABLE(im)) {
402       for (i = 0; i < count; ++i) {
403         for (ch = 0; ch < im->channels; ++ch) {
404           STORE16(im->idata, off, SampleFTo16(vals[i].channel[ch]));
405           ++off;
406         }
407       }
408     }
409     else {
410       for (i = 0; i < count; ++i) {
411         for (ch = 0; ch < im->channels; ++ch) {
412           if (im->ch_mask & (1 << ch))
413             STORE16(im->idata, off, SampleFTo16(vals[i].channel[ch]));
414           ++off;
415         }
416       }
417     }
418     return count;
419   }
420   else {
421     return 0;
422   }
423 }
424
425 static i_img_dim i_gsamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samps, 
426                        int const *chans, int chan_count) {
427   int ch;
428   i_img_dim count, i, w;
429   i_img_dim off;
430
431   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
432     if (r > im->xsize)
433       r = im->xsize;
434     off = (l+y*im->xsize) * im->channels;
435     w = r - l;
436     count = 0;
437
438     if (chans) {
439       /* make sure we have good channel numbers */
440       for (ch = 0; ch < chan_count; ++ch) {
441         if (chans[ch] < 0 || chans[ch] >= im->channels) {
442           i_push_errorf(0, "No channel %d in this image", chans[ch]);
443           return 0;
444         }
445       }
446       for (i = 0; i < w; ++i) {
447         for (ch = 0; ch < chan_count; ++ch) {
448           *samps++ = GET16as8(im->idata, off+chans[ch]);
449           ++count;
450         }
451         off += im->channels;
452       }
453     }
454     else {
455       if (chan_count <= 0 || chan_count > im->channels) {
456         i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
457                       chan_count);
458         return 0;
459       }
460       for (i = 0; i < w; ++i) {
461         for (ch = 0; ch < chan_count; ++ch) {
462           *samps++ = GET16as8(im->idata, off+ch);
463           ++count;
464         }
465         off += im->channels;
466       }
467     }
468
469     return count;
470   }
471   else {
472     return 0;
473   }
474 }
475
476 static i_img_dim i_gsampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samps, 
477                         int const *chans, int chan_count) {
478   int ch;
479   i_img_dim count, i, w;
480   i_img_dim off;
481
482   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
483     if (r > im->xsize)
484       r = im->xsize;
485     off = (l+y*im->xsize) * im->channels;
486     w = r - l;
487     count = 0;
488
489     if (chans) {
490       /* make sure we have good channel numbers */
491       for (ch = 0; ch < chan_count; ++ch) {
492         if (chans[ch] < 0 || chans[ch] >= im->channels) {
493           i_push_errorf(0, "No channel %d in this image", chans[ch]);
494           return 0;
495         }
496       }
497       for (i = 0; i < w; ++i) {
498         for (ch = 0; ch < chan_count; ++ch) {
499           *samps++ = Sample16ToF(GET16(im->idata, off+chans[ch]));
500           ++count;
501         }
502         off += im->channels;
503       }
504     }
505     else {
506       if (chan_count <= 0 || chan_count > im->channels) {
507         i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
508                       chan_count);
509         return 0;
510       }
511       for (i = 0; i < w; ++i) {
512         for (ch = 0; ch < chan_count; ++ch) {
513           *samps++ = Sample16ToF(GET16(im->idata, off+ch));
514           ++count;
515         }
516         off += im->channels;
517       }
518     }
519
520     return count;
521   }
522   else {
523     return 0;
524   }
525 }
526
527 static i_img_dim 
528 i_gsamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *samps, 
529                             int const *chans, int chan_count, int bits) {
530   int ch;
531   i_img_dim count, i, w;
532   i_img_dim off;
533
534   if (bits != 16) {
535     return i_gsamp_bits_fb(im, l, r, y, samps, chans, chan_count, bits);
536   }
537
538   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
539     if (r > im->xsize)
540       r = im->xsize;
541     off = (l+y*im->xsize) * im->channels;
542     w = r - l;
543     count = 0;
544
545     if (chans) {
546       /* make sure we have good channel numbers */
547       for (ch = 0; ch < chan_count; ++ch) {
548         if (chans[ch] < 0 || chans[ch] >= im->channels) {
549           i_push_errorf(0, "No channel %d in this image", chans[ch]);
550           return -1;
551         }
552       }
553       for (i = 0; i < w; ++i) {
554         for (ch = 0; ch < chan_count; ++ch) {
555           *samps++ = GET16(im->idata, off+chans[ch]);
556           ++count;
557         }
558         off += im->channels;
559       }
560     }
561     else {
562       if (chan_count <= 0 || chan_count > im->channels) {
563         dIMCTXim(im);
564         i_push_error(0, "Invalid channel count");
565         return -1;
566       }
567       for (i = 0; i < w; ++i) {
568         for (ch = 0; ch < chan_count; ++ch) {
569           *samps++ = GET16(im->idata, off+ch);
570           ++count;
571         }
572         off += im->channels;
573       }
574     }
575
576     return count;
577   }
578   else {
579     dIMCTXim(im);
580     i_push_error(0, "Image position outside of image");
581     return -1;
582   }
583 }
584
585 static i_img_dim 
586 i_psamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned const *samps, 
587                             int const *chans, int chan_count, int bits) {
588   int ch;
589   i_img_dim count, i, w;
590   i_img_dim off;
591
592   if (bits != 16) {
593     dIMCTXim(im);
594     i_push_error(0, "Invalid bits for 16-bit image");
595     return -1;
596   }
597
598   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
599     if (r > im->xsize)
600       r = im->xsize;
601     off = (l+y*im->xsize) * im->channels;
602     w = r - l;
603     count = 0;
604
605     if (chans) {
606       /* make sure we have good channel numbers */
607       for (ch = 0; ch < chan_count; ++ch) {
608         if (chans[ch] < 0 || chans[ch] >= im->channels) {
609           i_push_errorf(0, "No channel %d in this image", chans[ch]);
610           return -1;
611         }
612       }
613       for (i = 0; i < w; ++i) {
614         for (ch = 0; ch < chan_count; ++ch) {
615           if (im->ch_mask & (1 << ch))
616             STORE16(im->idata, off+chans[ch], *samps);
617           ++samps;
618           ++count;
619         }
620         off += im->channels;
621       }
622     }
623     else {
624       if (chan_count <= 0 || chan_count > im->channels) {
625         dIMCTXim(im);
626         i_push_error(0, "Invalid channel count");
627         return -1;
628       }
629       for (i = 0; i < w; ++i) {
630         for (ch = 0; ch < chan_count; ++ch) {
631           if (im->ch_mask & (1 << ch)) 
632             STORE16(im->idata, off+ch, *samps);
633           ++samps;
634           ++count;
635         }
636         off += im->channels;
637       }
638     }
639
640     return count;
641   }
642   else {
643     dIMCTXim(im);
644     i_push_error(0, "Image position outside of image");
645     return -1;
646   }
647 }
648
649 /*
650 =item i_psamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_sample_t *samps, int *chans, int chan_count)
651
652 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
653 for the channels specified by chans, an array of int with chan_count
654 elements.
655
656 Returns the number of samples written (which should be (r-l) *
657 bits_set(chan_mask)
658
659 =cut
660 */
661
662 static
663 i_img_dim
664 i_psamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
665           const i_sample_t *samps, const int *chans, int chan_count) {
666   int ch;
667   i_img_dim count, i, w;
668
669   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
670     i_img_dim offset;
671     if (r > im->xsize)
672       r = im->xsize;
673     offset = (l+y*im->xsize) * im->channels;
674     w = r - l;
675     count = 0;
676
677     if (chans) {
678       /* make sure we have good channel numbers */
679       /* and test if all channels specified are in the mask */
680       int all_in_mask = 1;
681       for (ch = 0; ch < chan_count; ++ch) {
682         if (chans[ch] < 0 || chans[ch] >= im->channels) {
683           i_push_errorf(0, "No channel %d in this image", chans[ch]);
684           return -1;
685         }
686         if (!((1 << chans[ch]) & im->ch_mask))
687           all_in_mask = 0;
688       }
689       if (all_in_mask) {
690         for (i = 0; i < w; ++i) {
691           for (ch = 0; ch < chan_count; ++ch) {
692             STORE8as16(im->idata, offset + chans[ch], *samps);
693             ++samps;
694             ++count;
695           }
696           offset += im->channels;
697         }
698       }
699       else {
700         for (i = 0; i < w; ++i) {
701           for (ch = 0; ch < chan_count; ++ch) {
702             if (im->ch_mask & (1 << (chans[ch])))
703             STORE8as16(im->idata, offset + chans[ch], *samps);
704             ++samps;
705             ++count;
706           }
707           offset += im->channels;
708         }
709       }
710     }
711     else {
712       if (chan_count <= 0 || chan_count > im->channels) {
713         i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
714                       chan_count);
715         return -1;
716       }
717       for (i = 0; i < w; ++i) {
718         unsigned mask = 1;
719         for (ch = 0; ch < chan_count; ++ch) {
720           if (im->ch_mask & mask)
721             STORE8as16(im->idata, offset + ch, *samps);
722           ++samps;
723           ++count;
724           mask <<= 1;
725         }
726         offset += im->channels;
727       }
728     }
729
730     return count;
731   }
732   else {
733     dIMCTXim(im);
734     i_push_error(0, "Image position outside of image");
735     return -1;
736   }
737 }
738
739 /*
740 =item i_psampf_d16(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)
741
742 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
743 for the channels specified by chans, an array of int with chan_count
744 elements.
745
746 Returns the number of samples written (which should be (r-l) *
747 bits_set(chan_mask)
748
749 =cut
750 */
751
752 static
753 i_img_dim
754 i_psampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
755           const i_fsample_t *samps, const int *chans, int chan_count) {
756   int ch;
757   i_img_dim count, i, w;
758
759   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
760     i_img_dim offset;
761     if (r > im->xsize)
762       r = im->xsize;
763     offset = (l+y*im->xsize) * im->channels;
764     w = r - l;
765     count = 0;
766
767     if (chans) {
768       /* make sure we have good channel numbers */
769       /* and test if all channels specified are in the mask */
770       int all_in_mask = 1;
771       for (ch = 0; ch < chan_count; ++ch) {
772         if (chans[ch] < 0 || chans[ch] >= im->channels) {
773           i_push_errorf(0, "No channel %d in this image", chans[ch]);
774           return -1;
775         }
776         if (!((1 << chans[ch]) & im->ch_mask))
777           all_in_mask = 0;
778       }
779       if (all_in_mask) {
780         for (i = 0; i < w; ++i) {
781           for (ch = 0; ch < chan_count; ++ch) {
782             unsigned samp16 = SampleFTo16(*samps);
783             STORE16(im->idata, offset + chans[ch], samp16);
784             ++samps;
785             ++count;
786           }
787           offset += im->channels;
788         }
789       }
790       else {
791         for (i = 0; i < w; ++i) {
792           for (ch = 0; ch < chan_count; ++ch) {
793             if (im->ch_mask & (1 << (chans[ch]))) {
794               unsigned samp16 = SampleFTo16(*samps);
795               STORE16(im->idata, offset + chans[ch], samp16);
796             }
797             ++samps;
798             ++count;
799           }
800           offset += im->channels;
801         }
802       }
803     }
804     else {
805       if (chan_count <= 0 || chan_count > im->channels) {
806         i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
807                       chan_count);
808         return -1;
809       }
810       for (i = 0; i < w; ++i) {
811         unsigned mask = 1;
812         for (ch = 0; ch < chan_count; ++ch) {
813           if (im->ch_mask & mask) {
814             unsigned samp16 = SampleFTo16(*samps);
815             STORE16(im->idata, offset + ch, samp16);
816           }
817           ++samps;
818           ++count;
819           mask <<= 1;
820         }
821         offset += im->channels;
822       }
823     }
824
825     return count;
826   }
827   else {
828     dIMCTXim(im);
829     i_push_error(0, "Image position outside of image");
830     return -1;
831   }
832 }
833
834 /*
835 =back
836
837 =head1 AUTHOR
838
839 Tony Cook <tony@develop-help.com>
840
841 =head1 SEE ALSO
842
843 Imager(3)
844
845 =cut
846 */