+Internal function.
+
+This is the function kept in the i_f_gpix member of an i_img object.
+It does normal retrieval of a pixel from the image with range checking.
+
+Returns 0 if the pixel could be set, -1 otherwise.
+
+=cut
+*/
+static
+int
+i_gpix_d(i_img *im, int x, int y, i_color *val) {
+ int ch;
+ if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
+ for(ch=0;ch<im->channels;ch++)
+ val->channel[ch]=im->idata[(x+y*im->xsize)*im->channels+ch];
+ return 0;
+ }
+ for(ch=0;ch<im->channels;ch++) val->channel[ch] = 0;
+ return -1; /* error was cliped */
+}
+
+/*
+=item i_glin_d(im, l, r, y, vals)
+
+Reads a line of data from the image, storing the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at space for (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+put the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+static
+int
+i_glin_d(i_img *im, int l, int r, int y, i_color *vals) {
+ int ch, count, i;
+ unsigned char *data;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch)
+ vals[i].channel[ch] = *data++;
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_plin_d(im, l, r, y, vals)
+
+Writes a line of data into the image, using the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+get the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+static
+int
+i_plin_d(i_img *im, int l, int r, int y, const i_color *vals) {
+ int ch, count, i;
+ unsigned char *data;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch) {
+ if (im->ch_mask & (1 << ch))
+ *data = vals[i].channel[ch];
+ ++data;
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_ppixf_d(im, x, y, val)
+
+=cut
+*/
+static
+int
+i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val) {
+ int ch;
+
+ if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
+ for(ch=0;ch<im->channels;ch++)
+ if (im->ch_mask&(1<<ch)) {
+ im->idata[(x+y*im->xsize)*im->channels+ch] =
+ SampleFTo8(val->channel[ch]);
+ }
+ return 0;
+ }
+ return -1; /* error was clipped */
+}
+
+/*
+=item i_gpixf_d(im, x, y, val)
+
+=cut
+*/
+static
+int
+i_gpixf_d(i_img *im, int x, int y, i_fcolor *val) {
+ int ch;
+ if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
+ for(ch=0;ch<im->channels;ch++) {
+ val->channel[ch] =
+ Sample8ToF(im->idata[(x+y*im->xsize)*im->channels+ch]);
+ }
+ return 0;
+ }
+ return -1; /* error was cliped */
+}
+
+/*
+=item i_glinf_d(im, l, r, y, vals)
+
+Reads a line of data from the image, storing the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at space for (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+put the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+static
+int
+i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals) {
+ int ch, count, i;
+ unsigned char *data;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch)
+ vals[i].channel[ch] = Sample8ToF(*data++);
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_plinf_d(im, l, r, y, vals)
+
+Writes a line of data into the image, using the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+get the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+static
+int
+i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals) {
+ int ch, count, i;
+ unsigned char *data;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch) {
+ if (im->ch_mask & (1 << ch))
+ *data = SampleFTo8(vals[i].channel[ch]);
+ ++data;
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count)
+
+Reads sample values from im for the horizontal line (l, y) to (r-1,y)
+for the channels specified by chans, an array of int with chan_count
+elements.
+
+Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
+
+=cut
+*/
+static
+int
+i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps,
+ const int *chans, int chan_count) {
+ int ch, count, i, w;
+ unsigned char *data;
+
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ w = r - l;
+ count = 0;
+
+ if (chans) {
+ /* make sure we have good channel numbers */
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ return 0;
+ }
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = data[chans[ch]];
+ ++count;
+ }
+ data += im->channels;
+ }
+ }
+ else {
+ if (chan_count <= 0 || chan_count > im->channels) {
+ i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels",
+ chan_count);
+ return 0;
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = data[ch];
+ ++count;
+ }
+ data += im->channels;
+ }
+ }
+
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count)
+
+Reads sample values from im for the horizontal line (l, y) to (r-1,y)
+for the channels specified by chan_mask, where bit 0 is the first
+channel.
+
+Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
+
+=cut
+*/
+static
+int
+i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps,
+ const int *chans, int chan_count) {
+ int ch, count, i, w;
+ unsigned char *data;
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ }
+ }
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ w = r - l;
+ count = 0;
+
+ if (chans) {
+ /* make sure we have good channel numbers */
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ return 0;
+ }
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = Sample8ToF(data[chans[ch]]);
+ ++count;
+ }
+ data += im->channels;
+ }
+ }
+ else {
+ if (chan_count <= 0 || chan_count > im->channels) {
+ i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels",
+ chan_count);
+ return 0;
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = Sample8ToF(data[ch]);
+ ++count;
+ }
+ data += im->channels;
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=back
+
+=head2 Image method wrappers
+
+These functions provide i_fsample_t functions in terms of their
+i_sample_t versions.
+
+=over
+
+=item i_ppixf_fp(i_img *im, int x, int y, i_fcolor *pix)
+
+=cut
+*/
+
+int i_ppixf_fp(i_img *im, int x, int y, const i_fcolor *pix) {
+ i_color temp;
+ int ch;
+
+ for (ch = 0; ch < im->channels; ++ch)
+ temp.channel[ch] = SampleFTo8(pix->channel[ch]);
+
+ return i_ppix(im, x, y, &temp);
+}
+
+/*
+=item i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix)
+
+=cut
+*/
+int i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix) {
+ i_color temp;
+ int ch;
+
+ if (i_gpix(im, x, y, &temp)) {
+ for (ch = 0; ch < im->channels; ++ch)
+ pix->channel[ch] = Sample8ToF(temp.channel[ch]);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+/*
+=item i_plinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix)
+
+=cut
+*/
+int i_plinf_fp(i_img *im, int l, int r, int y, const i_fcolor *pix) {
+ i_color *work;
+
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (r > l) {
+ int ret;
+ int i, ch;
+ work = mymalloc(sizeof(i_color) * (r-l));
+ for (i = 0; i < r-l; ++i) {
+ for (ch = 0; ch < im->channels; ++ch)
+ work[i].channel[ch] = SampleFTo8(pix[i].channel[ch]);
+ }
+ ret = i_plin(im, l, r, y, work);
+ myfree(work);
+
+ return ret;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix)
+
+=cut
+*/
+int i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix) {
+ i_color *work;
+
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (r > l) {
+ int ret;
+ int i, ch;
+ work = mymalloc(sizeof(i_color) * (r-l));
+ ret = i_plin(im, l, r, y, work);
+ for (i = 0; i < r-l; ++i) {
+ for (ch = 0; ch < im->channels; ++ch)
+ pix[i].channel[ch] = Sample8ToF(work[i].channel[ch]);
+ }
+ myfree(work);
+
+ return ret;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp, int *chans, int chan_count)
+
+=cut
+*/
+int i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp,
+ int const *chans, int chan_count) {
+ i_sample_t *work;
+
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (r > l) {
+ int ret;
+ int i;
+ work = mymalloc(sizeof(i_sample_t) * (r-l));
+ ret = i_gsamp(im, l, r, y, work, chans, chan_count);
+ for (i = 0; i < ret; ++i) {
+ samp[i] = Sample8ToF(work[i]);
+ }
+ myfree(work);
+
+ return ret;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=back
+
+=head2 Palette wrapper functions
+
+Used for virtual images, these forward palette calls to a wrapped image,
+assuming the wrapped image is the first pointer in the structure that
+im->ext_data points at.
+
+=over
+
+=item i_addcolors_forward(i_img *im, const i_color *colors, int count)
+
+=cut
+*/
+int i_addcolors_forward(i_img *im, const i_color *colors, int count) {
+ return i_addcolors(*(i_img **)im->ext_data, colors, count);
+}
+
+/*
+=item i_getcolors_forward(i_img *im, int i, i_color *color, int count)
+
+=cut
+*/
+int i_getcolors_forward(i_img *im, int i, i_color *color, int count) {
+ return i_getcolors(*(i_img **)im->ext_data, i, color, count);
+}
+
+/*
+=item i_setcolors_forward(i_img *im, int i, const i_color *color, int count)
+
+=cut
+*/
+int i_setcolors_forward(i_img *im, int i, const i_color *color, int count) {
+ return i_setcolors(*(i_img **)im->ext_data, i, color, count);
+}
+
+/*
+=item i_colorcount_forward(i_img *im)
+
+=cut
+*/
+int i_colorcount_forward(i_img *im) {
+ return i_colorcount(*(i_img **)im->ext_data);
+}
+
+/*
+=item i_maxcolors_forward(i_img *im)
+
+=cut
+*/
+int i_maxcolors_forward(i_img *im) {
+ return i_maxcolors(*(i_img **)im->ext_data);
+}
+
+/*
+=item i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry)
+
+=cut
+*/
+int i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry) {
+ return i_findcolor(*(i_img **)im->ext_data, color, entry);
+}
+
+/*
+=back
+
+=head2 Fallback handler
+
+=over
+
+=item i_gsamp_bits_fb
+
+=cut
+*/
+
+int
+i_gsamp_bits_fb(i_img *im, int l, int r, int y, unsigned *samps,
+ const int *chans, int chan_count, int bits) {
+ if (bits < 1 || bits > 32) {
+ i_push_error(0, "Invalid bits, must be 1..32");
+ return -1;
+ }
+
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ double scale;
+ int ch, count, i, w;
+
+ if (bits == 32)
+ scale = 4294967295.0;
+ else
+ scale = (double)(1 << bits) - 1;
+
+ if (r > im->xsize)
+ r = im->xsize;
+ w = r - l;
+ count = 0;
+
+ if (chans) {
+ /* make sure we have good channel numbers */
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ return -1;
+ }
+ }
+ for (i = 0; i < w; ++i) {
+ i_fcolor c;
+ i_gpixf(im, l+i, y, &c);
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = (unsigned)(c.channel[ch] * scale + 0.5);
+ ++count;
+ }
+ }
+ }
+ else {
+ if (chan_count <= 0 || chan_count > im->channels) {
+ i_push_error(0, "Invalid channel count");
+ return -1;
+ }
+ for (i = 0; i < w; ++i) {
+ i_fcolor c;
+ i_gpixf(im, l+i, y, &c);
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = (unsigned)(c.channel[ch] * scale + 0.5);
+ ++count;
+ }
+ }
+ }
+
+ return count;
+ }
+ else {
+ i_push_error(0, "Image position outside of image");
+ return -1;
+ }
+}
+
+/*
+=back
+
+=head2 Stream reading and writing wrapper functions
+
+=over
+
+=item i_gen_reader(i_gen_read_data *info, char *buf, int length)
+
+Performs general read buffering for file readers that permit reading
+to be done through a callback.
+
+The final callback gets two parameters, a I<need> value, and a I<want>
+value, where I<need> is the amount of data that the file library needs
+to read, and I<want> is the amount of space available in the buffer
+maintained by these functions.
+
+This means if you need to read from a stream that you don't know the
+length of, you can return I<need> bytes, taking the performance hit of
+possibly expensive callbacks (eg. back to perl code), or if you are
+reading from a stream where it doesn't matter if some data is lost, or
+if the total length of the stream is known, you can return I<want>
+bytes.
+
+=cut
+*/
+
+int
+i_gen_reader(i_gen_read_data *gci, char *buf, int length) {
+ int total;
+
+ if (length < gci->length - gci->cpos) {
+ /* simplest case */
+ memcpy(buf, gci->buffer+gci->cpos, length);
+ gci->cpos += length;
+ return length;
+ }
+
+ total = 0;
+ memcpy(buf, gci->buffer+gci->cpos, gci->length-gci->cpos);
+ total += gci->length - gci->cpos;
+ length -= gci->length - gci->cpos;
+ buf += gci->length - gci->cpos;
+ if (length < (int)sizeof(gci->buffer)) {
+ int did_read;
+ int copy_size;
+ while (length
+ && (did_read = (gci->cb)(gci->userdata, gci->buffer, length,
+ sizeof(gci->buffer))) > 0) {
+ gci->cpos = 0;
+ gci->length = did_read;
+
+ copy_size = i_min(length, gci->length);
+ memcpy(buf, gci->buffer, copy_size);
+ gci->cpos += copy_size;
+ buf += copy_size;
+ total += copy_size;
+ length -= copy_size;
+ }
+ }
+ else {
+ /* just read the rest - too big for our buffer*/
+ int did_read;
+ while ((did_read = (gci->cb)(gci->userdata, buf, length, length)) > 0) {
+ length -= did_read;
+ total += did_read;
+ buf += did_read;
+ }
+ }
+ return total;
+}
+
+/*
+=item i_gen_read_data_new(i_read_callback_t cb, char *userdata)
+
+For use by callback file readers to initialize the reader buffer.
+
+Allocates, initializes and returns the reader buffer.
+
+See also L<image.c/free_gen_read_data> and L<image.c/i_gen_reader>.
+
+=cut
+*/
+i_gen_read_data *
+i_gen_read_data_new(i_read_callback_t cb, char *userdata) {
+ i_gen_read_data *self = mymalloc(sizeof(i_gen_read_data));
+ self->cb = cb;
+ self->userdata = userdata;
+ self->length = 0;
+ self->cpos = 0;
+
+ return self;
+}
+
+/*
+=item i_free_gen_read_data(i_gen_read_data *)
+
+Cleans up.
+
+=cut
+*/
+void i_free_gen_read_data(i_gen_read_data *self) {
+ myfree(self);
+}
+
+/*
+=item i_gen_writer(i_gen_write_data *info, char const *data, int size)
+
+Performs write buffering for a callback based file writer.
+
+Failures are considered fatal, if a write fails then data will be
+dropped.