revert threading changes, they aren't ready for the mainline yet
[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     i_img_dim offset;
662     if (r > im->xsize)
663       r = im->xsize;
664     offset = (l+y*im->xsize) * im->channels;
665     w = r - l;
666     count = 0;
667
668     if (chans) {
669       /* make sure we have good channel numbers */
670       /* and test if all channels specified are in the mask */
671       int all_in_mask = 1;
672       for (ch = 0; ch < chan_count; ++ch) {
673         if (chans[ch] < 0 || chans[ch] >= im->channels) {
674           i_push_errorf(0, "No channel %d in this image", chans[ch]);
675           return -1;
676         }
677         if (!((1 << chans[ch]) & im->ch_mask))
678           all_in_mask = 0;
679       }
680       if (all_in_mask) {
681         for (i = 0; i < w; ++i) {
682           for (ch = 0; ch < chan_count; ++ch) {
683             STORE8as16(im->idata, offset + chans[ch], *samps);
684             ++samps;
685             ++count;
686           }
687           offset += im->channels;
688         }
689       }
690       else {
691         for (i = 0; i < w; ++i) {
692           for (ch = 0; ch < chan_count; ++ch) {
693             if (im->ch_mask & (1 << (chans[ch])))
694             STORE8as16(im->idata, offset + chans[ch], *samps);
695             ++samps;
696             ++count;
697           }
698           offset += im->channels;
699         }
700       }
701     }
702     else {
703       if (chan_count <= 0 || chan_count > im->channels) {
704         i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
705                       chan_count);
706         return -1;
707       }
708       for (i = 0; i < w; ++i) {
709         unsigned mask = 1;
710         for (ch = 0; ch < chan_count; ++ch) {
711           if (im->ch_mask & mask)
712             STORE8as16(im->idata, offset + ch, *samps);
713           ++samps;
714           ++count;
715           mask <<= 1;
716         }
717         offset += im->channels;
718       }
719     }
720
721     return count;
722   }
723   else {
724     i_push_error(0, "Image position outside of image");
725     return -1;
726   }
727 }
728
729 /*
730 =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)
731
732 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
733 for the channels specified by chans, an array of int with chan_count
734 elements.
735
736 Returns the number of samples written (which should be (r-l) *
737 bits_set(chan_mask)
738
739 =cut
740 */
741
742 static
743 i_img_dim
744 i_psampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
745           const i_fsample_t *samps, const int *chans, int chan_count) {
746   int ch;
747   i_img_dim count, i, w;
748
749   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
750     i_img_dim offset;
751     if (r > im->xsize)
752       r = im->xsize;
753     offset = (l+y*im->xsize) * im->channels;
754     w = r - l;
755     count = 0;
756
757     if (chans) {
758       /* make sure we have good channel numbers */
759       /* and test if all channels specified are in the mask */
760       int all_in_mask = 1;
761       for (ch = 0; ch < chan_count; ++ch) {
762         if (chans[ch] < 0 || chans[ch] >= im->channels) {
763           i_push_errorf(0, "No channel %d in this image", chans[ch]);
764           return -1;
765         }
766         if (!((1 << chans[ch]) & im->ch_mask))
767           all_in_mask = 0;
768       }
769       if (all_in_mask) {
770         for (i = 0; i < w; ++i) {
771           for (ch = 0; ch < chan_count; ++ch) {
772             unsigned samp16 = SampleFTo16(*samps);
773             STORE16(im->idata, offset + chans[ch], samp16);
774             ++samps;
775             ++count;
776           }
777           offset += im->channels;
778         }
779       }
780       else {
781         for (i = 0; i < w; ++i) {
782           for (ch = 0; ch < chan_count; ++ch) {
783             if (im->ch_mask & (1 << (chans[ch]))) {
784               unsigned samp16 = SampleFTo16(*samps);
785               STORE16(im->idata, offset + chans[ch], samp16);
786             }
787             ++samps;
788             ++count;
789           }
790           offset += im->channels;
791         }
792       }
793     }
794     else {
795       if (chan_count <= 0 || chan_count > im->channels) {
796         i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
797                       chan_count);
798         return -1;
799       }
800       for (i = 0; i < w; ++i) {
801         unsigned mask = 1;
802         for (ch = 0; ch < chan_count; ++ch) {
803           if (im->ch_mask & mask) {
804             unsigned samp16 = SampleFTo16(*samps);
805             STORE16(im->idata, offset + ch, samp16);
806           }
807           ++samps;
808           ++count;
809           mask <<= 1;
810         }
811         offset += im->channels;
812       }
813     }
814
815     return count;
816   }
817   else {
818     i_push_error(0, "Image position outside of image");
819     return -1;
820   }
821 }
822
823 /*
824 =back
825
826 =head1 AUTHOR
827
828 Tony Cook <tony@develop-help.com>
829
830 =head1 SEE ALSO
831
832 Imager(3)
833
834 =cut
835 */