update img16.c to avoid mm_log()
[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   im_log((aIMCTX, 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           dIMCTXim(im);
443           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
444           return 0;
445         }
446       }
447       for (i = 0; i < w; ++i) {
448         for (ch = 0; ch < chan_count; ++ch) {
449           *samps++ = GET16as8(im->idata, off+chans[ch]);
450           ++count;
451         }
452         off += im->channels;
453       }
454     }
455     else {
456       if (chan_count <= 0 || chan_count > im->channels) {
457         dIMCTXim(im);
458         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
459                       chan_count);
460         return 0;
461       }
462       for (i = 0; i < w; ++i) {
463         for (ch = 0; ch < chan_count; ++ch) {
464           *samps++ = GET16as8(im->idata, off+ch);
465           ++count;
466         }
467         off += im->channels;
468       }
469     }
470
471     return count;
472   }
473   else {
474     return 0;
475   }
476 }
477
478 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, 
479                         int const *chans, int chan_count) {
480   int ch;
481   i_img_dim count, i, w;
482   i_img_dim off;
483
484   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
485     if (r > im->xsize)
486       r = im->xsize;
487     off = (l+y*im->xsize) * im->channels;
488     w = r - l;
489     count = 0;
490
491     if (chans) {
492       /* make sure we have good channel numbers */
493       for (ch = 0; ch < chan_count; ++ch) {
494         if (chans[ch] < 0 || chans[ch] >= im->channels) {
495           dIMCTXim(im);
496           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
497           return 0;
498         }
499       }
500       for (i = 0; i < w; ++i) {
501         for (ch = 0; ch < chan_count; ++ch) {
502           *samps++ = Sample16ToF(GET16(im->idata, off+chans[ch]));
503           ++count;
504         }
505         off += im->channels;
506       }
507     }
508     else {
509       if (chan_count <= 0 || chan_count > im->channels) {
510         dIMCTXim(im);
511         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
512                       chan_count);
513         return 0;
514       }
515       for (i = 0; i < w; ++i) {
516         for (ch = 0; ch < chan_count; ++ch) {
517           *samps++ = Sample16ToF(GET16(im->idata, off+ch));
518           ++count;
519         }
520         off += im->channels;
521       }
522     }
523
524     return count;
525   }
526   else {
527     return 0;
528   }
529 }
530
531 static i_img_dim 
532 i_gsamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *samps, 
533                             int const *chans, int chan_count, int bits) {
534   int ch;
535   i_img_dim count, i, w;
536   i_img_dim off;
537
538   if (bits != 16) {
539     return i_gsamp_bits_fb(im, l, r, y, samps, chans, chan_count, bits);
540   }
541
542   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
543     if (r > im->xsize)
544       r = im->xsize;
545     off = (l+y*im->xsize) * im->channels;
546     w = r - l;
547     count = 0;
548
549     if (chans) {
550       /* make sure we have good channel numbers */
551       for (ch = 0; ch < chan_count; ++ch) {
552         if (chans[ch] < 0 || chans[ch] >= im->channels) {
553           dIMCTXim(im);
554           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
555           return -1;
556         }
557       }
558       for (i = 0; i < w; ++i) {
559         for (ch = 0; ch < chan_count; ++ch) {
560           *samps++ = GET16(im->idata, off+chans[ch]);
561           ++count;
562         }
563         off += im->channels;
564       }
565     }
566     else {
567       if (chan_count <= 0 || chan_count > im->channels) {
568         dIMCTXim(im);
569         i_push_error(0, "Invalid channel count");
570         return -1;
571       }
572       for (i = 0; i < w; ++i) {
573         for (ch = 0; ch < chan_count; ++ch) {
574           *samps++ = GET16(im->idata, off+ch);
575           ++count;
576         }
577         off += im->channels;
578       }
579     }
580
581     return count;
582   }
583   else {
584     dIMCTXim(im);
585     i_push_error(0, "Image position outside of image");
586     return -1;
587   }
588 }
589
590 static i_img_dim 
591 i_psamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned const *samps, 
592                             int const *chans, int chan_count, int bits) {
593   int ch;
594   i_img_dim count, i, w;
595   i_img_dim off;
596
597   if (bits != 16) {
598     dIMCTXim(im);
599     i_push_error(0, "Invalid bits for 16-bit image");
600     return -1;
601   }
602
603   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
604     if (r > im->xsize)
605       r = im->xsize;
606     off = (l+y*im->xsize) * im->channels;
607     w = r - l;
608     count = 0;
609
610     if (chans) {
611       /* make sure we have good channel numbers */
612       for (ch = 0; ch < chan_count; ++ch) {
613         if (chans[ch] < 0 || chans[ch] >= im->channels) {
614           dIMCTXim(im);
615           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
616           return -1;
617         }
618       }
619       for (i = 0; i < w; ++i) {
620         for (ch = 0; ch < chan_count; ++ch) {
621           if (im->ch_mask & (1 << ch))
622             STORE16(im->idata, off+chans[ch], *samps);
623           ++samps;
624           ++count;
625         }
626         off += im->channels;
627       }
628     }
629     else {
630       if (chan_count <= 0 || chan_count > im->channels) {
631         dIMCTXim(im);
632         i_push_error(0, "Invalid channel count");
633         return -1;
634       }
635       for (i = 0; i < w; ++i) {
636         for (ch = 0; ch < chan_count; ++ch) {
637           if (im->ch_mask & (1 << ch)) 
638             STORE16(im->idata, off+ch, *samps);
639           ++samps;
640           ++count;
641         }
642         off += im->channels;
643       }
644     }
645
646     return count;
647   }
648   else {
649     dIMCTXim(im);
650     i_push_error(0, "Image position outside of image");
651     return -1;
652   }
653 }
654
655 /*
656 =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)
657
658 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
659 for the channels specified by chans, an array of int with chan_count
660 elements.
661
662 Returns the number of samples written (which should be (r-l) *
663 bits_set(chan_mask)
664
665 =cut
666 */
667
668 static
669 i_img_dim
670 i_psamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
671           const i_sample_t *samps, const int *chans, int chan_count) {
672   int ch;
673   i_img_dim count, i, w;
674
675   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
676     i_img_dim offset;
677     if (r > im->xsize)
678       r = im->xsize;
679     offset = (l+y*im->xsize) * im->channels;
680     w = r - l;
681     count = 0;
682
683     if (chans) {
684       /* make sure we have good channel numbers */
685       /* and test if all channels specified are in the mask */
686       int all_in_mask = 1;
687       for (ch = 0; ch < chan_count; ++ch) {
688         if (chans[ch] < 0 || chans[ch] >= im->channels) {
689           dIMCTXim(im);
690           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
691           return -1;
692         }
693         if (!((1 << chans[ch]) & im->ch_mask))
694           all_in_mask = 0;
695       }
696       if (all_in_mask) {
697         for (i = 0; i < w; ++i) {
698           for (ch = 0; ch < chan_count; ++ch) {
699             STORE8as16(im->idata, offset + chans[ch], *samps);
700             ++samps;
701             ++count;
702           }
703           offset += im->channels;
704         }
705       }
706       else {
707         for (i = 0; i < w; ++i) {
708           for (ch = 0; ch < chan_count; ++ch) {
709             if (im->ch_mask & (1 << (chans[ch])))
710             STORE8as16(im->idata, offset + chans[ch], *samps);
711             ++samps;
712             ++count;
713           }
714           offset += im->channels;
715         }
716       }
717     }
718     else {
719       if (chan_count <= 0 || chan_count > im->channels) {
720         dIMCTXim(im);
721         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
722                       chan_count);
723         return -1;
724       }
725       for (i = 0; i < w; ++i) {
726         unsigned mask = 1;
727         for (ch = 0; ch < chan_count; ++ch) {
728           if (im->ch_mask & mask)
729             STORE8as16(im->idata, offset + ch, *samps);
730           ++samps;
731           ++count;
732           mask <<= 1;
733         }
734         offset += im->channels;
735       }
736     }
737
738     return count;
739   }
740   else {
741     dIMCTXim(im);
742     i_push_error(0, "Image position outside of image");
743     return -1;
744   }
745 }
746
747 /*
748 =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)
749
750 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
751 for the channels specified by chans, an array of int with chan_count
752 elements.
753
754 Returns the number of samples written (which should be (r-l) *
755 bits_set(chan_mask)
756
757 =cut
758 */
759
760 static
761 i_img_dim
762 i_psampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
763           const i_fsample_t *samps, const int *chans, int chan_count) {
764   int ch;
765   i_img_dim count, i, w;
766
767   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
768     i_img_dim offset;
769     if (r > im->xsize)
770       r = im->xsize;
771     offset = (l+y*im->xsize) * im->channels;
772     w = r - l;
773     count = 0;
774
775     if (chans) {
776       /* make sure we have good channel numbers */
777       /* and test if all channels specified are in the mask */
778       int all_in_mask = 1;
779       for (ch = 0; ch < chan_count; ++ch) {
780         if (chans[ch] < 0 || chans[ch] >= im->channels) {
781           dIMCTXim(im);
782           im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
783           return -1;
784         }
785         if (!((1 << chans[ch]) & im->ch_mask))
786           all_in_mask = 0;
787       }
788       if (all_in_mask) {
789         for (i = 0; i < w; ++i) {
790           for (ch = 0; ch < chan_count; ++ch) {
791             unsigned samp16 = SampleFTo16(*samps);
792             STORE16(im->idata, offset + chans[ch], samp16);
793             ++samps;
794             ++count;
795           }
796           offset += im->channels;
797         }
798       }
799       else {
800         for (i = 0; i < w; ++i) {
801           for (ch = 0; ch < chan_count; ++ch) {
802             if (im->ch_mask & (1 << (chans[ch]))) {
803               unsigned samp16 = SampleFTo16(*samps);
804               STORE16(im->idata, offset + chans[ch], samp16);
805             }
806             ++samps;
807             ++count;
808           }
809           offset += im->channels;
810         }
811       }
812     }
813     else {
814       if (chan_count <= 0 || chan_count > im->channels) {
815         dIMCTXim(im);
816         im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
817                       chan_count);
818         return -1;
819       }
820       for (i = 0; i < w; ++i) {
821         unsigned mask = 1;
822         for (ch = 0; ch < chan_count; ++ch) {
823           if (im->ch_mask & mask) {
824             unsigned samp16 = SampleFTo16(*samps);
825             STORE16(im->idata, offset + ch, samp16);
826           }
827           ++samps;
828           ++count;
829           mask <<= 1;
830         }
831         offset += im->channels;
832       }
833     }
834
835     return count;
836   }
837   else {
838     dIMCTXim(im);
839     i_push_error(0, "Image position outside of image");
840     return -1;
841   }
842 }
843
844 /*
845 =back
846
847 =head1 AUTHOR
848
849 Tony Cook <tony@develop-help.com>
850
851 =head1 SEE ALSO
852
853 Imager(3)
854
855 =cut
856 */