remove a pointless NULL check from i_flipxy()
[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 im_img_16_new(ctx, x, y, ch)
154 X<im_img_16_new API>X<i_img_16_new API>
155 =category Image creation/destruction
156 =synopsis i_img *img = im_img_16_new(aIMCTX, width, height, channels);
157 =synopsis i_img *img = i_img_16_new(width, height, channels);
158
159 Create a new 16-bit/sample image.
160
161 Returns the image on success, or NULL on failure.
162
163 Also callable as C<i_img_16_new(x, y, ch)>
164
165 =cut
166 */
167
168 i_img *
169 im_img_16_new(pIMCTX, i_img_dim x, i_img_dim y, int ch) {
170   i_img *im;
171   size_t bytes, line_bytes;
172
173   im_log((aIMCTX, 1,"i_img_16_new(x %" i_DF ", y %" i_DF ", ch %d)\n",
174           i_DFc(x), i_DFc(y), ch));
175
176   if (x < 1 || y < 1) {
177     im_push_error(aIMCTX, 0, "Image sizes must be positive");
178     return NULL;
179   }
180   if (ch < 1 || ch > MAXCHANNELS) {
181     im_push_errorf(aIMCTX, 0, "channels must be between 1 and %d", MAXCHANNELS);
182     return NULL;
183   }
184   bytes =  x * y * ch * 2;
185   if (bytes / y / ch / 2 != x) {
186     im_push_errorf(aIMCTX, 0, "integer overflow calculating image allocation");
187     return NULL;
188   }
189   
190   /* basic assumption: we can always allocate a buffer representing a
191      line from the image, otherwise we're going to have trouble
192      working with the image */
193   line_bytes = sizeof(i_fcolor) * x;
194   if (line_bytes / x != sizeof(i_fcolor)) {
195     im_push_error(aIMCTX, 0, "integer overflow calculating scanline allocation");
196     return NULL;
197   }
198
199   im = im_img_alloc(aIMCTX);
200   *im = IIM_base_16bit_direct;
201   i_tags_new(&im->tags);
202   im->xsize = x;
203   im->ysize = y;
204   im->channels = ch;
205   im->bytes = bytes;
206   im->ext_data = NULL;
207   im->idata = mymalloc(im->bytes);
208   memset(im->idata, 0, im->bytes);
209
210   im_img_init(aIMCTX, im);
211
212   return im;
213 }
214
215 /*
216 =item i_img_to_rgb16(im)
217
218 =category Image creation
219
220 Returns a 16-bit/sample version of the supplied image.
221
222 Returns the image on success, or NULL on failure.
223
224 =cut
225 */
226
227 i_img *
228 i_img_to_rgb16(i_img *im) {
229   i_img *targ;
230   i_fcolor *line;
231   i_img_dim y;
232   dIMCTXim(im);
233
234   targ = im_img_16_new(aIMCTX, im->xsize, im->ysize, im->channels);
235   if (!targ)
236     return NULL;
237   line = mymalloc(sizeof(i_fcolor) * im->xsize);
238   for (y = 0; y < im->ysize; ++y) {
239     i_glinf(im, 0, im->xsize, y, line);
240     i_plinf(targ, 0, im->xsize, y, line);
241   }
242
243   myfree(line);
244
245   return targ;
246 }
247
248 static int i_ppix_d16(i_img *im, i_img_dim x, i_img_dim y, const i_color *val) {
249   i_img_dim off;
250   int ch;
251
252   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
253     return -1;
254
255   off = (x + y * im->xsize) * im->channels;
256   if (I_ALL_CHANNELS_WRITABLE(im)) {
257     for (ch = 0; ch < im->channels; ++ch)
258       STORE8as16(im->idata, off+ch, val->channel[ch]);
259   }
260   else {
261     for (ch = 0; ch < im->channels; ++ch)
262       if (im->ch_mask & (1 << ch))
263         STORE8as16(im->idata, off+ch, val->channel[ch]);
264   }
265
266   return 0;
267 }
268
269 static int i_gpix_d16(i_img *im, i_img_dim x, i_img_dim y, i_color *val) {
270   i_img_dim off;
271   int ch;
272
273   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
274     return -1;
275
276   off = (x + y * im->xsize) * im->channels;
277   for (ch = 0; ch < im->channels; ++ch)
278     val->channel[ch] = GET16as8(im->idata, off+ch);
279
280   return 0;
281 }
282
283 static int i_ppixf_d16(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *val) {
284   i_img_dim off;
285   int ch;
286
287   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
288     return -1;
289
290   off = (x + y * im->xsize) * im->channels;
291   if (I_ALL_CHANNELS_WRITABLE(im)) {
292     for (ch = 0; ch < im->channels; ++ch)
293       STORE16(im->idata, off+ch, SampleFTo16(val->channel[ch]));
294   }
295   else {
296     for (ch = 0; ch < im->channels; ++ch)
297       if (im->ch_mask & (1 << ch))
298         STORE16(im->idata, off+ch, SampleFTo16(val->channel[ch]));
299   }
300
301   return 0;
302 }
303
304 static int i_gpixf_d16(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *val) {
305   i_img_dim off;
306   int ch;
307
308   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
309     return -1;
310
311   off = (x + y * im->xsize) * im->channels;
312   for (ch = 0; ch < im->channels; ++ch)
313     val->channel[ch] = Sample16ToF(GET16(im->idata, off+ch));
314
315   return 0;
316 }
317
318 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) {
319   int ch;
320   i_img_dim count, i;
321   i_img_dim off;
322   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
323     if (r > im->xsize)
324       r = im->xsize;
325     off = (l+y*im->xsize) * im->channels;
326     count = r - l;
327     for (i = 0; i < count; ++i) {
328       for (ch = 0; ch < im->channels; ++ch) {
329         vals[i].channel[ch] = GET16as8(im->idata, off);
330         ++off;
331       }
332     }
333     return count;
334   }
335   else {
336     return 0;
337   }
338 }
339
340 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) {
341   int ch;
342   i_img_dim count, i;
343   i_img_dim off;
344   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
345     if (r > im->xsize)
346       r = im->xsize;
347     off = (l+y*im->xsize) * im->channels;
348     count = r - l;
349     if (I_ALL_CHANNELS_WRITABLE(im)) {
350       for (i = 0; i < count; ++i) {
351         for (ch = 0; ch < im->channels; ++ch) {
352           STORE8as16(im->idata, off, vals[i].channel[ch]);
353           ++off;
354         }
355       }
356     }
357     else {
358       for (i = 0; i < count; ++i) {
359         for (ch = 0; ch < im->channels; ++ch) {
360           if (im->ch_mask & (1 << ch))
361             STORE8as16(im->idata, off, vals[i].channel[ch]);
362           ++off;
363         }
364       }
365     }
366     return count;
367   }
368   else {
369     return 0;
370   }
371 }
372
373 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) {
374   int ch;
375   i_img_dim count, i;
376   i_img_dim off;
377   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
378     if (r > im->xsize)
379       r = im->xsize;
380     off = (l+y*im->xsize) * im->channels;
381     count = r - l;
382     for (i = 0; i < count; ++i) {
383       for (ch = 0; ch < im->channels; ++ch) {
384         vals[i].channel[ch] = Sample16ToF(GET16(im->idata, off));
385         ++off;
386       }
387     }
388     return count;
389   }
390   else {
391     return 0;
392   }
393 }
394
395 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) {
396   int ch;
397   i_img_dim count, i;
398   i_img_dim off;
399   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
400     if (r > im->xsize)
401       r = im->xsize;
402     off = (l+y*im->xsize) * im->channels;
403     count = r - l;
404     if (I_ALL_CHANNELS_WRITABLE(im)) {
405       for (i = 0; i < count; ++i) {
406         for (ch = 0; ch < im->channels; ++ch) {
407           STORE16(im->idata, off, SampleFTo16(vals[i].channel[ch]));
408           ++off;
409         }
410       }
411     }
412     else {
413       for (i = 0; i < count; ++i) {
414         for (ch = 0; ch < im->channels; ++ch) {
415           if (im->ch_mask & (1 << ch))
416             STORE16(im->idata, off, SampleFTo16(vals[i].channel[ch]));
417           ++off;
418         }
419       }
420     }
421     return count;
422   }
423   else {
424     return 0;
425   }
426 }
427
428 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, 
429                        int const *chans, int chan_count) {
430   int ch;
431   i_img_dim count, i, w;
432   i_img_dim off;
433
434   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
435     if (r > im->xsize)
436       r = im->xsize;
437     off = (l+y*im->xsize) * im->channels;
438     w = r - l;
439     count = 0;
440
441     if (chans) {
442       /* make sure we have good channel numbers */
443       for (ch = 0; ch < chan_count; ++ch) {
444         if (chans[ch] < 0 || chans[ch] >= im->channels) {
445           dIMCTXim(im);
446           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
447           return 0;
448         }
449       }
450       for (i = 0; i < w; ++i) {
451         for (ch = 0; ch < chan_count; ++ch) {
452           *samps++ = GET16as8(im->idata, off+chans[ch]);
453           ++count;
454         }
455         off += im->channels;
456       }
457     }
458     else {
459       if (chan_count <= 0 || chan_count > im->channels) {
460         dIMCTXim(im);
461         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
462                       chan_count);
463         return 0;
464       }
465       for (i = 0; i < w; ++i) {
466         for (ch = 0; ch < chan_count; ++ch) {
467           *samps++ = GET16as8(im->idata, off+ch);
468           ++count;
469         }
470         off += im->channels;
471       }
472     }
473
474     return count;
475   }
476   else {
477     return 0;
478   }
479 }
480
481 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, 
482                         int const *chans, int chan_count) {
483   int ch;
484   i_img_dim count, i, w;
485   i_img_dim off;
486
487   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
488     if (r > im->xsize)
489       r = im->xsize;
490     off = (l+y*im->xsize) * im->channels;
491     w = r - l;
492     count = 0;
493
494     if (chans) {
495       /* make sure we have good channel numbers */
496       for (ch = 0; ch < chan_count; ++ch) {
497         if (chans[ch] < 0 || chans[ch] >= im->channels) {
498           dIMCTXim(im);
499           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
500           return 0;
501         }
502       }
503       for (i = 0; i < w; ++i) {
504         for (ch = 0; ch < chan_count; ++ch) {
505           *samps++ = Sample16ToF(GET16(im->idata, off+chans[ch]));
506           ++count;
507         }
508         off += im->channels;
509       }
510     }
511     else {
512       if (chan_count <= 0 || chan_count > im->channels) {
513         dIMCTXim(im);
514         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
515                       chan_count);
516         return 0;
517       }
518       for (i = 0; i < w; ++i) {
519         for (ch = 0; ch < chan_count; ++ch) {
520           *samps++ = Sample16ToF(GET16(im->idata, off+ch));
521           ++count;
522         }
523         off += im->channels;
524       }
525     }
526
527     return count;
528   }
529   else {
530     return 0;
531   }
532 }
533
534 static i_img_dim 
535 i_gsamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *samps, 
536                             int const *chans, int chan_count, int bits) {
537   int ch;
538   i_img_dim count, i, w;
539   i_img_dim off;
540
541   if (bits != 16) {
542     return i_gsamp_bits_fb(im, l, r, y, samps, chans, chan_count, bits);
543   }
544
545   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
546     if (r > im->xsize)
547       r = im->xsize;
548     off = (l+y*im->xsize) * im->channels;
549     w = r - l;
550     count = 0;
551
552     if (chans) {
553       /* make sure we have good channel numbers */
554       for (ch = 0; ch < chan_count; ++ch) {
555         if (chans[ch] < 0 || chans[ch] >= im->channels) {
556           dIMCTXim(im);
557           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
558           return -1;
559         }
560       }
561       for (i = 0; i < w; ++i) {
562         for (ch = 0; ch < chan_count; ++ch) {
563           *samps++ = GET16(im->idata, off+chans[ch]);
564           ++count;
565         }
566         off += im->channels;
567       }
568     }
569     else {
570       if (chan_count <= 0 || chan_count > im->channels) {
571         dIMCTXim(im);
572         i_push_error(0, "Invalid channel count");
573         return -1;
574       }
575       for (i = 0; i < w; ++i) {
576         for (ch = 0; ch < chan_count; ++ch) {
577           *samps++ = GET16(im->idata, off+ch);
578           ++count;
579         }
580         off += im->channels;
581       }
582     }
583
584     return count;
585   }
586   else {
587     dIMCTXim(im);
588     i_push_error(0, "Image position outside of image");
589     return -1;
590   }
591 }
592
593 static i_img_dim 
594 i_psamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned const *samps, 
595                             int const *chans, int chan_count, int bits) {
596   int ch;
597   i_img_dim count, i, w;
598   i_img_dim off;
599
600   if (bits != 16) {
601     dIMCTXim(im);
602     i_push_error(0, "Invalid bits for 16-bit image");
603     return -1;
604   }
605
606   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
607     if (r > im->xsize)
608       r = im->xsize;
609     off = (l+y*im->xsize) * im->channels;
610     w = r - l;
611     count = 0;
612
613     if (chans) {
614       /* make sure we have good channel numbers */
615       for (ch = 0; ch < chan_count; ++ch) {
616         if (chans[ch] < 0 || chans[ch] >= im->channels) {
617           dIMCTXim(im);
618           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
619           return -1;
620         }
621       }
622       for (i = 0; i < w; ++i) {
623         for (ch = 0; ch < chan_count; ++ch) {
624           if (im->ch_mask & (1 << ch))
625             STORE16(im->idata, off+chans[ch], *samps);
626           ++samps;
627           ++count;
628         }
629         off += im->channels;
630       }
631     }
632     else {
633       if (chan_count <= 0 || chan_count > im->channels) {
634         dIMCTXim(im);
635         i_push_error(0, "Invalid channel count");
636         return -1;
637       }
638       for (i = 0; i < w; ++i) {
639         for (ch = 0; ch < chan_count; ++ch) {
640           if (im->ch_mask & (1 << ch)) 
641             STORE16(im->idata, off+ch, *samps);
642           ++samps;
643           ++count;
644         }
645         off += im->channels;
646       }
647     }
648
649     return count;
650   }
651   else {
652     dIMCTXim(im);
653     i_push_error(0, "Image position outside of image");
654     return -1;
655   }
656 }
657
658 /*
659 =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)
660
661 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
662 for the channels specified by chans, an array of int with chan_count
663 elements.
664
665 Returns the number of samples written (which should be (r-l) *
666 bits_set(chan_mask)
667
668 =cut
669 */
670
671 static
672 i_img_dim
673 i_psamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
674           const i_sample_t *samps, const int *chans, int chan_count) {
675   int ch;
676   i_img_dim count, i, w;
677
678   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
679     i_img_dim offset;
680     if (r > im->xsize)
681       r = im->xsize;
682     offset = (l+y*im->xsize) * im->channels;
683     w = r - l;
684     count = 0;
685
686     if (chans) {
687       /* make sure we have good channel numbers */
688       /* and test if all channels specified are in the mask */
689       int all_in_mask = 1;
690       for (ch = 0; ch < chan_count; ++ch) {
691         if (chans[ch] < 0 || chans[ch] >= im->channels) {
692           dIMCTXim(im);
693           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
694           return -1;
695         }
696         if (!((1 << chans[ch]) & im->ch_mask))
697           all_in_mask = 0;
698       }
699       if (all_in_mask) {
700         for (i = 0; i < w; ++i) {
701           for (ch = 0; ch < chan_count; ++ch) {
702             STORE8as16(im->idata, offset + chans[ch], *samps);
703             ++samps;
704             ++count;
705           }
706           offset += im->channels;
707         }
708       }
709       else {
710         for (i = 0; i < w; ++i) {
711           for (ch = 0; ch < chan_count; ++ch) {
712             if (im->ch_mask & (1 << (chans[ch])))
713             STORE8as16(im->idata, offset + chans[ch], *samps);
714             ++samps;
715             ++count;
716           }
717           offset += im->channels;
718         }
719       }
720     }
721     else {
722       if (chan_count <= 0 || chan_count > im->channels) {
723         dIMCTXim(im);
724         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
725                       chan_count);
726         return -1;
727       }
728       for (i = 0; i < w; ++i) {
729         unsigned mask = 1;
730         for (ch = 0; ch < chan_count; ++ch) {
731           if (im->ch_mask & mask)
732             STORE8as16(im->idata, offset + ch, *samps);
733           ++samps;
734           ++count;
735           mask <<= 1;
736         }
737         offset += im->channels;
738       }
739     }
740
741     return count;
742   }
743   else {
744     dIMCTXim(im);
745     i_push_error(0, "Image position outside of image");
746     return -1;
747   }
748 }
749
750 /*
751 =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)
752
753 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
754 for the channels specified by chans, an array of int with chan_count
755 elements.
756
757 Returns the number of samples written (which should be (r-l) *
758 bits_set(chan_mask)
759
760 =cut
761 */
762
763 static
764 i_img_dim
765 i_psampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
766           const i_fsample_t *samps, const int *chans, int chan_count) {
767   int ch;
768   i_img_dim count, i, w;
769
770   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
771     i_img_dim offset;
772     if (r > im->xsize)
773       r = im->xsize;
774     offset = (l+y*im->xsize) * im->channels;
775     w = r - l;
776     count = 0;
777
778     if (chans) {
779       /* make sure we have good channel numbers */
780       /* and test if all channels specified are in the mask */
781       int all_in_mask = 1;
782       for (ch = 0; ch < chan_count; ++ch) {
783         if (chans[ch] < 0 || chans[ch] >= im->channels) {
784           dIMCTXim(im);
785           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
786           return -1;
787         }
788         if (!((1 << chans[ch]) & im->ch_mask))
789           all_in_mask = 0;
790       }
791       if (all_in_mask) {
792         for (i = 0; i < w; ++i) {
793           for (ch = 0; ch < chan_count; ++ch) {
794             unsigned samp16 = SampleFTo16(*samps);
795             STORE16(im->idata, offset + chans[ch], samp16);
796             ++samps;
797             ++count;
798           }
799           offset += im->channels;
800         }
801       }
802       else {
803         for (i = 0; i < w; ++i) {
804           for (ch = 0; ch < chan_count; ++ch) {
805             if (im->ch_mask & (1 << (chans[ch]))) {
806               unsigned samp16 = SampleFTo16(*samps);
807               STORE16(im->idata, offset + chans[ch], samp16);
808             }
809             ++samps;
810             ++count;
811           }
812           offset += im->channels;
813         }
814       }
815     }
816     else {
817       if (chan_count <= 0 || chan_count > im->channels) {
818         dIMCTXim(im);
819         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
820                       chan_count);
821         return -1;
822       }
823       for (i = 0; i < w; ++i) {
824         unsigned mask = 1;
825         for (ch = 0; ch < chan_count; ++ch) {
826           if (im->ch_mask & mask) {
827             unsigned samp16 = SampleFTo16(*samps);
828             STORE16(im->idata, offset + ch, samp16);
829           }
830           ++samps;
831           ++count;
832           mask <<= 1;
833         }
834         offset += im->channels;
835       }
836     }
837
838     return count;
839   }
840   else {
841     dIMCTXim(im);
842     i_push_error(0, "Image position outside of image");
843     return -1;
844   }
845 }
846
847 /*
848 =back
849
850 =head1 AUTHOR
851
852 Tony Cook <tony@develop-help.com>
853
854 =head1 SEE ALSO
855
856 Imager(3)
857
858 =cut
859 */