fe1ad6248b54ab58610018fb0b8eb916be20fb25
[imager.git] / imgdouble.c
1 /*
2 =head1 NAME
3
4 imgdouble.c - implements double per sample images
5
6 =head1 SYNOPSIS
7
8   i_img *im = i_img_double_new(width, height, channels);
9   # use like a normal image
10
11 =head1 DESCRIPTION
12
13 Implements double/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_ddoub(i_img *im, i_img_dim x, i_img_dim y, const i_color *val);
27 static int i_gpix_ddoub(i_img *im, i_img_dim x, i_img_dim y, i_color *val);
28 static i_img_dim i_glin_ddoub(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_ddoub(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals);
30 static int i_ppixf_ddoub(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *val);
31 static int i_gpixf_ddoub(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *val);
32 static i_img_dim i_glinf_ddoub(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_ddoub(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_ddoub(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_ddoub(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 
39 i_psamp_ddoub(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);
40 static i_img_dim 
41 i_psampf_ddoub(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);
42
43 /*
44 =item IIM_base_16bit_direct
45
46 Base structure used to initialize a 16-bit/sample image.
47
48 Internal.
49
50 =cut
51 */
52 static i_img IIM_base_double_direct =
53 {
54   0, /* channels set */
55   0, 0, 0, /* xsize, ysize, bytes */
56   ~0U, /* ch_mask */
57   i_double_bits, /* bits */
58   i_direct_type, /* type */
59   0, /* virtual */
60   NULL, /* idata */
61   { 0, 0, NULL }, /* tags */
62   NULL, /* ext_data */
63
64   i_ppix_ddoub, /* i_f_ppix */
65   i_ppixf_ddoub, /* i_f_ppixf */
66   i_plin_ddoub, /* i_f_plin */
67   i_plinf_ddoub, /* i_f_plinf */
68   i_gpix_ddoub, /* i_f_gpix */
69   i_gpixf_ddoub, /* i_f_gpixf */
70   i_glin_ddoub, /* i_f_glin */
71   i_glinf_ddoub, /* i_f_glinf */
72   i_gsamp_ddoub, /* i_f_gsamp */
73   i_gsampf_ddoub, /* i_f_gsampf */
74
75   NULL, /* i_f_gpal */
76   NULL, /* i_f_ppal */
77   NULL, /* i_f_addcolors */
78   NULL, /* i_f_getcolors */
79   NULL, /* i_f_colorcount */
80   NULL, /* i_f_maxcolors */
81   NULL, /* i_f_findcolor */
82   NULL, /* i_f_setcolors */
83
84   NULL, /* i_f_destroy */
85
86   i_gsamp_bits_fb,
87   NULL, /* i_f_psamp_bits */
88
89   i_psamp_ddoub, /* i_f_psamp */
90   i_psampf_ddoub /* i_f_psampf */
91 };
92
93 /*
94 =item i_img_double_new(i_img_dim x, i_img_dim y, int ch)
95 =category Image creation/destruction
96 =synopsis i_img *img = i_img_double_new(width, height, channels);
97
98 Creates a new double per sample image.
99
100 =cut
101 */
102 i_img *i_img_double_new(i_img_dim x, i_img_dim y, int ch) {
103   size_t bytes;
104   i_img *im;
105
106   mm_log((1,"i_img_double_new(x %" i_DF ", y %" i_DF ", ch %d)\n",
107           i_DFc(x), i_DFc(y), ch));
108
109   if (x < 1 || y < 1) {
110     i_push_error(0, "Image sizes must be positive");
111     return NULL;
112   }
113   if (ch < 1 || ch > MAXCHANNELS) {
114     i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS);
115     return NULL;
116   }
117   bytes = x * y * ch * sizeof(double);
118   if (bytes / y / ch / sizeof(double) != x) {
119     i_push_errorf(0, "integer overflow calculating image allocation");
120     return NULL;
121   }
122   
123   im = i_img_alloc();
124   *im = IIM_base_double_direct;
125   i_tags_new(&im->tags);
126   im->xsize = x;
127   im->ysize = y;
128   im->channels = ch;
129   im->bytes = bytes;
130   im->ext_data = NULL;
131   im->idata = mymalloc(im->bytes);
132   memset(im->idata, 0, im->bytes);
133   i_img_init(im);
134   
135   return im;
136 }
137
138 static int i_ppix_ddoub(i_img *im, i_img_dim x, i_img_dim y, const i_color *val) {
139   i_img_dim off;
140   int ch;
141
142   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
143     return -1;
144
145   off = (x + y * im->xsize) * im->channels;
146   if (I_ALL_CHANNELS_WRITABLE(im)) {
147     for (ch = 0; ch < im->channels; ++ch)
148       ((double*)im->idata)[off+ch] = Sample8ToF(val->channel[ch]);
149   }
150   else {
151     for (ch = 0; ch < im->channels; ++ch)
152       if (im->ch_mask & (1<<ch))
153         ((double*)im->idata)[off+ch] = Sample8ToF(val->channel[ch]);
154   }
155
156   return 0;
157 }
158
159 static int i_gpix_ddoub(i_img *im, i_img_dim x, i_img_dim y, i_color *val) {
160   i_img_dim off;
161   int ch;
162
163   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
164     return -1;
165
166   off = (x + y * im->xsize) * im->channels;
167   for (ch = 0; ch < im->channels; ++ch)
168     val->channel[ch] = SampleFTo8(((double *)im->idata)[off+ch]);
169
170   return 0;
171 }
172
173 static int i_ppixf_ddoub(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *val) {
174   i_img_dim off;
175   int ch;
176
177   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
178     return -1;
179
180   off = (x + y * im->xsize) * im->channels;
181   if (I_ALL_CHANNELS_WRITABLE(im)) {
182     for (ch = 0; ch < im->channels; ++ch)
183       ((double *)im->idata)[off+ch] = val->channel[ch];
184   }
185   else {
186     for (ch = 0; ch < im->channels; ++ch)
187       if (im->ch_mask & (1 << ch))
188         ((double *)im->idata)[off+ch] = val->channel[ch];
189   }
190
191   return 0;
192 }
193
194 static int i_gpixf_ddoub(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *val) {
195   i_img_dim off;
196   int ch;
197
198   if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) 
199     return -1;
200
201   off = (x + y * im->xsize) * im->channels;
202   for (ch = 0; ch < im->channels; ++ch)
203     val->channel[ch] = ((double *)im->idata)[off+ch];
204
205   return 0;
206 }
207
208 static i_img_dim i_glin_ddoub(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals) {
209   int ch;
210   i_img_dim count, i;
211   i_img_dim off;
212   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
213     if (r > im->xsize)
214       r = im->xsize;
215     off = (l+y*im->xsize) * im->channels;
216     count = r - l;
217     for (i = 0; i < count; ++i) {
218       for (ch = 0; ch < im->channels; ++ch) {
219         vals[i].channel[ch] = SampleFTo8(((double *)im->idata)[off]);
220         ++off;
221       }
222     }
223     return count;
224   }
225   else {
226     return 0;
227   }
228 }
229
230 static i_img_dim i_plin_ddoub(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals) {
231   int ch;
232   i_img_dim count, i;
233   i_img_dim off;
234   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
235     if (r > im->xsize)
236       r = im->xsize;
237     off = (l+y*im->xsize) * im->channels;
238     count = r - l;
239     if (I_ALL_CHANNELS_WRITABLE(im)) {
240       for (i = 0; i < count; ++i) {
241         for (ch = 0; ch < im->channels; ++ch) {
242           ((double *)im->idata)[off] = Sample8ToF(vals[i].channel[ch]);
243           ++off;
244         }
245       }
246     }
247     else {
248       for (i = 0; i < count; ++i) {
249         for (ch = 0; ch < im->channels; ++ch) {
250           if (im->ch_mask & (1 << ch))
251             ((double *)im->idata)[off] = Sample8ToF(vals[i].channel[ch]);
252           ++off;
253         }
254       }
255     }
256     return count;
257   }
258   else {
259     return 0;
260   }
261 }
262
263 static i_img_dim i_glinf_ddoub(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals) {
264   int ch;
265   i_img_dim count, i;
266   i_img_dim off;
267   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
268     if (r > im->xsize)
269       r = im->xsize;
270     off = (l+y*im->xsize) * im->channels;
271     count = r - l;
272     for (i = 0; i < count; ++i) {
273       for (ch = 0; ch < im->channels; ++ch) {
274         vals[i].channel[ch] = ((double *)im->idata)[off];
275         ++off;
276       }
277     }
278     return count;
279   }
280   else {
281     return 0;
282   }
283 }
284
285 static i_img_dim i_plinf_ddoub(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals) {
286   int ch;
287   i_img_dim count, i;
288   i_img_dim off;
289   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
290     if (r > im->xsize)
291       r = im->xsize;
292     off = (l+y*im->xsize) * im->channels;
293     count = r - l;
294     if (I_ALL_CHANNELS_WRITABLE(im)) {
295       for (i = 0; i < count; ++i) {
296         for (ch = 0; ch < im->channels; ++ch) {
297           ((double *)im->idata)[off] = vals[i].channel[ch];
298           ++off;
299         }
300       }
301     }
302     else {
303       for (i = 0; i < count; ++i) {
304         for (ch = 0; ch < im->channels; ++ch) {
305           if (im->ch_mask & (1 << ch))
306             ((double *)im->idata)[off] = vals[i].channel[ch];
307           ++off;
308         }
309       }
310     }
311     return count;
312   }
313   else {
314     return 0;
315   }
316 }
317
318 static i_img_dim i_gsamp_ddoub(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samps, 
319                        int const *chans, int chan_count) {
320   int ch;
321   i_img_dim count, i, w;
322   i_img_dim off;
323
324   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
325     if (r > im->xsize)
326       r = im->xsize;
327     off = (l+y*im->xsize) * im->channels;
328     w = r - l;
329     count = 0;
330
331     if (chans) {
332       /* make sure we have good channel numbers */
333       for (ch = 0; ch < chan_count; ++ch) {
334         if (chans[ch] < 0 || chans[ch] >= im->channels) {
335           i_push_errorf(0, "No channel %d in this image", chans[ch]);
336           return 0;
337         }
338       }
339       for (i = 0; i < w; ++i) {
340         for (ch = 0; ch < chan_count; ++ch) {
341           *samps++ = SampleFTo8(((double *)im->idata)[off+chans[ch]]);
342           ++count;
343         }
344         off += im->channels;
345       }
346     }
347     else {
348       if (chan_count <= 0 || chan_count > im->channels) {
349         i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
350                       chan_count);
351         return 0;
352       }
353       for (i = 0; i < w; ++i) {
354         for (ch = 0; ch < chan_count; ++ch) {
355           *samps++ = SampleFTo8(((double *)im->idata)[off+ch]);
356           ++count;
357         }
358         off += im->channels;
359       }
360     }
361
362     return count;
363   }
364   else {
365     return 0;
366   }
367 }
368
369 static i_img_dim i_gsampf_ddoub(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samps, 
370                         int const *chans, int chan_count) {
371   int ch;
372   i_img_dim count, i, w;
373   i_img_dim off;
374
375   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
376     if (r > im->xsize)
377       r = im->xsize;
378     off = (l+y*im->xsize) * im->channels;
379     w = r - l;
380     count = 0;
381
382     if (chans) {
383       /* make sure we have good channel numbers */
384       for (ch = 0; ch < chan_count; ++ch) {
385         if (chans[ch] < 0 || chans[ch] >= im->channels) {
386           i_push_errorf(0, "No channel %d in this image", chans[ch]);
387           return 0;
388         }
389       }
390       for (i = 0; i < w; ++i) {
391         for (ch = 0; ch < chan_count; ++ch) {
392           *samps++ = ((double *)im->idata)[off+chans[ch]];
393           ++count;
394         }
395         off += im->channels;
396       }
397     }
398     else {
399       if (chan_count <= 0 || chan_count > im->channels) {
400         i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
401                       chan_count);
402         return 0;
403       }
404       for (i = 0; i < w; ++i) {
405         for (ch = 0; ch < chan_count; ++ch) {
406           *samps++ = ((double *)im->idata)[off+ch];
407           ++count;
408         }
409         off += im->channels;
410       }
411     }
412
413     return count;
414   }
415   else {
416     return 0;
417   }
418 }
419
420 /*
421 =item i_psamp_ddoub(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)
422
423 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
424 for the channels specified by chans, an array of int with chan_count
425 elements.
426
427 Returns the number of samples written (which should be (r-l) *
428 bits_set(chan_mask)
429
430 =cut
431 */
432
433 static
434 i_img_dim
435 i_psamp_ddoub(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
436           const i_sample_t *samps, const int *chans, int chan_count) {
437   int ch;
438   i_img_dim count, i, w;
439
440   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
441     i_img_dim offset;
442     if (r > im->xsize)
443       r = im->xsize;
444     offset = (l+y*im->xsize) * im->channels;
445     w = r - l;
446     count = 0;
447
448     if (chans) {
449       /* make sure we have good channel numbers */
450       /* and test if all channels specified are in the mask */
451       int all_in_mask = 1;
452       for (ch = 0; ch < chan_count; ++ch) {
453         if (chans[ch] < 0 || chans[ch] >= im->channels) {
454           i_push_errorf(0, "No channel %d in this image", chans[ch]);
455           return -1;
456         }
457         if (!((1 << chans[ch]) & im->ch_mask))
458           all_in_mask = 0;
459       }
460       if (all_in_mask) {
461         for (i = 0; i < w; ++i) {
462           for (ch = 0; ch < chan_count; ++ch) {
463             ((double*)im->idata)[offset + chans[ch]] = Sample8ToF(*samps);
464             ++samps;
465             ++count;
466           }
467           offset += im->channels;
468         }
469       }
470       else {
471         for (i = 0; i < w; ++i) {
472           for (ch = 0; ch < chan_count; ++ch) {
473             if (im->ch_mask & (1 << (chans[ch])))
474               ((double*)im->idata)[offset + chans[ch]] = Sample8ToF(*samps);
475             
476             ++samps;
477             ++count;
478           }
479           offset += im->channels;
480         }
481       }
482     }
483     else {
484       if (chan_count <= 0 || chan_count > im->channels) {
485         i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
486                       chan_count);
487         return -1;
488       }
489       for (i = 0; i < w; ++i) {
490         unsigned mask = 1;
491         for (ch = 0; ch < chan_count; ++ch) {
492           if (im->ch_mask & mask)
493             ((double*)im->idata)[offset + ch] = Sample8ToF(*samps);
494
495           ++samps;
496           ++count;
497           mask <<= 1;
498         }
499         offset += im->channels;
500       }
501     }
502
503     return count;
504   }
505   else {
506     i_push_error(0, "Image position outside of image");
507     return -1;
508   }
509 }
510
511 /*
512 =item i_psampf_ddoub(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)
513
514 Writes sample values to im for the horizontal line (l, y) to (r-1,y)
515 for the channels specified by chans, an array of int with chan_count
516 elements.
517
518 Returns the number of samples written (which should be (r-l) *
519 bits_set(chan_mask)
520
521 =cut
522 */
523
524 static
525 i_img_dim
526 i_psampf_ddoub(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
527                const i_fsample_t *samps, const int *chans, int chan_count) {
528   int ch;
529   i_img_dim count, i, w;
530
531   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
532     i_img_dim offset;
533     if (r > im->xsize)
534       r = im->xsize;
535     offset = (l+y*im->xsize) * im->channels;
536     w = r - l;
537     count = 0;
538
539     if (chans) {
540       /* make sure we have good channel numbers */
541       /* and test if all channels specified are in the mask */
542       int all_in_mask = 1;
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         if (!((1 << chans[ch]) & im->ch_mask))
549           all_in_mask = 0;
550       }
551       if (all_in_mask) {
552         for (i = 0; i < w; ++i) {
553           for (ch = 0; ch < chan_count; ++ch) {
554             ((double*)im->idata)[offset + chans[ch]] = *samps;
555             ++samps;
556             ++count;
557           }
558           offset += im->channels;
559         }
560       }
561       else {
562         for (i = 0; i < w; ++i) {
563           for (ch = 0; ch < chan_count; ++ch) {
564             if (im->ch_mask & (1 << (chans[ch])))
565               ((double*)im->idata)[offset + chans[ch]] = *samps;
566             
567             ++samps;
568             ++count;
569           }
570           offset += im->channels;
571         }
572       }
573     }
574     else {
575       if (chan_count <= 0 || chan_count > im->channels) {
576         i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
577                       chan_count);
578         return -1;
579       }
580       for (i = 0; i < w; ++i) {
581         unsigned mask = 1;
582         for (ch = 0; ch < chan_count; ++ch) {
583           if (im->ch_mask & mask)
584             ((double*)im->idata)[offset + ch] = *samps;
585
586           ++samps;
587           ++count;
588           mask <<= 1;
589         }
590         offset += im->channels;
591       }
592     }
593
594     return count;
595   }
596   else {
597     i_push_error(0, "Image position outside of image");
598     return -1;
599   }
600 }
601
602 /*
603 =item i_img_to_drgb(im)
604
605 =category Image creation
606
607 Returns a double/sample version of the supplied image.
608
609 Returns the image on success, or NULL on failure.
610
611 =cut
612 */
613
614 i_img *
615 i_img_to_drgb(i_img *im) {
616   i_img *targ;
617   i_fcolor *line;
618   i_img_dim y;
619
620   targ = i_img_double_new(im->xsize, im->ysize, im->channels);
621   if (!targ)
622     return NULL;
623   line = mymalloc(sizeof(i_fcolor) * im->xsize);
624   for (y = 0; y < im->ysize; ++y) {
625     i_glinf(im, 0, im->xsize, y, line);
626     i_plinf(targ, 0, im->xsize, y, line);
627   }
628
629   myfree(line);
630
631   return targ;
632 }
633
634 /*
635 =back
636
637 =head1 AUTHOR
638
639 Tony Cook <tony@develop-help.com>
640
641 =head1 SEE ALSO
642
643 Imager(3)
644
645 =cut
646 */