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