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