/* =head1 NAME convert.im - image conversions =head1 SYNOPSIS out = i_convert(srcimage, coeff, outchans, inchans) =head1 DESCRIPTION Converts images from one format to another, typically in this case for converting from RGBA to greyscale and back. =over =cut */ #define IMAGER_NO_CONTEXT #include "imager.h" struct chan_copy { /* channels to copy */ int copy_count; int from[MAXCHANNELS]; int to[MAXCHANNELS]; /* channels to zero */ int zero_count; int zero[MAXCHANNELS]; /* channels to set to maxsample */ int one_count; int one[MAXCHANNELS]; }; static int is_channel_copy(i_img *im, const double *coeff, int outchan, int inchan, struct chan_copy *info); static i_img * convert_via_copy(i_img *im, i_img *src, struct chan_copy *info); /* =item i_convert(src, coeff, outchan, inchan) Converts the image src into another image. coeff contains the co-efficients of an outchan x inchan matrix, for each output pixel: coeff[0], coeff[1] ... im[x,y] = [ coeff[inchan], coeff[inchan+1]... ] * [ src[x,y], 1] ... coeff[inchan*outchan-1] If im has the wrong number of channels or is the wrong size then i_convert() will re-create it. Now handles images with more than 8-bits/sample. =cut */ i_img * i_convert(i_img *src, const double *coeff, int outchan, int inchan) { double work[MAXCHANNELS]; i_img_dim x, y; int i, j; int ilimit; i_img *im = NULL; dIMCTXim(src); im_log((aIMCTX,1,"i_convert(im %p, src %p, coeff %p,outchan %d, inchan %d)\n", im, src, coeff, outchan, inchan)); im_clear_error(aIMCTX); ilimit = inchan; if (ilimit > src->channels) ilimit = src->channels; if (outchan > MAXCHANNELS) { im_push_error(aIMCTX, 0, "cannot have outchan > MAXCHANNELS"); return 0; } if (src->type == i_direct_type) { struct chan_copy info; im = i_sametype_chans(src, src->xsize, src->ysize, outchan); if (is_channel_copy(src, coeff, outchan, inchan, &info)) { return convert_via_copy(im, src, &info); } else { #code src->bits <= i_8_bits IM_COLOR *vals; /* we can always allocate a single scanline of i_color */ vals = mymalloc(sizeof(IM_COLOR) * src->xsize); /* checked 04Jul05 tonyc */ for (y = 0; y < src->ysize; ++y) { IM_GLIN(src, 0, src->xsize, y, vals); for (x = 0; x < src->xsize; ++x) { for (j = 0; j < outchan; ++j) { work[j] = 0; for (i = 0; i < ilimit; ++i) { work[j] += coeff[i+inchan*j] * vals[x].channel[i]; } if (i < inchan) { work[j] += coeff[i+inchan*j] * IM_SAMPLE_MAX; } } for (j = 0; j < outchan; ++j) { if (work[j] < 0) vals[x].channel[j] = 0; else if (work[j] >= IM_SAMPLE_MAX) vals[x].channel[j] = IM_SAMPLE_MAX; else vals[x].channel[j] = work[j]; } } IM_PLIN(im, 0, src->xsize, y, vals); } myfree(vals); #/code } } else { int count; int outcount; int index; i_color *colors; i_palidx *vals; im = im_img_pal_new(aIMCTX, src->xsize, src->ysize, outchan, i_maxcolors(src)); /* just translate the color table */ count = i_colorcount(src); outcount = i_colorcount(im); /* color table allocated for image, so it must fit */ colors = mymalloc(count * sizeof(i_color)); /* check 04Jul05 tonyc */ i_getcolors(src, 0, colors, count); for (index = 0; index < count; ++index) { for (j = 0; j < outchan; ++j) { work[j] = 0; for (i = 0; i < ilimit; ++i) { work[j] += coeff[i+inchan*j] * colors[index].channel[i]; } if (i < inchan) { work[j] += coeff[i+inchan*j] * 255.9; } } for (j = 0; j < outchan; ++j) { if (work[j] < 0) colors[index].channel[j] = 0; else if (work[j] >= 255) colors[index].channel[j] = 255; else colors[index].channel[j] = work[j]; } } if (count < outcount) { i_setcolors(im, 0, colors, count); } else { i_setcolors(im, 0, colors, outcount); i_addcolors(im, colors, count-outcount); } /* and copy the indicies */ /* i_palidx is always unsigned char and will never be bigger than short and since a line of 4-byte i_colors can fit then a line of i_palidx will fit */ vals = mymalloc(sizeof(i_palidx) * im->xsize); /* checked 4jul05 tonyc */ for (y = 0; y < im->ysize; ++y) { i_gpal(src, 0, im->xsize, y, vals); i_ppal(im, 0, im->xsize, y, vals); } myfree(vals); myfree(colors); } return im; } /* =item is_channel_copy(coeff, outchan, inchan, chan_copy_info) Test if the coefficients represent just copying channels around, and initialize lists of the channels to copy, zero or set to max. =cut */ static int is_channel_copy(i_img *im, const double *coeff, int outchan, int inchan, struct chan_copy *info) { int srcchan[MAXCHANNELS]; int onechan[MAXCHANNELS]; int i, j; int ilimit = im->channels > inchan ? inchan : im->channels; for (j = 0; j < outchan; ++j) { srcchan[j] = -1; onechan[j] = 0; } for (j = 0; j < outchan; ++j) { for (i = 0; i < ilimit; ++i) { if (coeff[i+inchan*j] == 1.0) { if (srcchan[j] != -1) { /* from two or more channels, not a copy */ return 0; } srcchan[j] = i; } else if (coeff[i+inchan*j]) { /* some other non-zero value, not a copy */ return 0; } } if (i < inchan) { if (coeff[i+inchan*j] == 1.0) { if (srcchan[j] != -1) { /* can't do both */ return 0; } onechan[j] = 1; } else if (coeff[i+inchan*j]) { /* some other non-zero value, not a copy */ return 0; } } } /* build our working data structures */ info->copy_count = info->zero_count = info->one_count = 0; for (j = 0; j < outchan; ++j) { if (srcchan[j] != -1) { info->from[info->copy_count] = srcchan[j]; info->to[info->copy_count] = j; ++info->copy_count; } else if (onechan[j]) { info->one[info->one_count] = j; ++info->one_count; } else { info->zero[info->zero_count] = j; ++info->zero_count; } } #if 0 { for (i = 0; i < info->copy_count; ++i) { printf("From %d to %d\n", info->from[i], info->to[i]); } for (i = 0; i < info->one_count; ++i) { printf("One %d\n", info->one[i]); } for (i = 0; i < info->zero_count; ++i) { printf("Zero %d\n", info->zero[i]); } fflush(stdout); } #endif return 1; } /* =item convert_via_copy(im, src, chan_copy_info) Perform a convert that only requires channel copies. =cut */ static i_img * convert_via_copy(i_img *im, i_img *src, struct chan_copy *info) { #code src->bits <= i_8_bits IM_COLOR *in_line = mymalloc(sizeof(IM_COLOR) * src->xsize); IM_COLOR *out_line = mymalloc(sizeof(IM_COLOR) * src->xsize); i_img_dim x, y; int i; IM_COLOR *inp, *outp; for (y = 0; y < src->ysize; ++y) { IM_GLIN(src, 0, src->xsize, y, in_line); inp = in_line; outp = out_line; for (x = 0; x < src->xsize; ++x) { for (i = 0; i < info->copy_count; ++i) { outp->channel[info->to[i]] = inp->channel[info->from[i]]; } for (i = 0; i < info->one_count; ++i) { outp->channel[info->one[i]] = IM_SAMPLE_MAX; } for (i = 0; i < info->zero_count; ++i) { outp->channel[info->zero[i]] = 0; } ++inp; ++outp; } IM_PLIN(im, 0, src->xsize, y, out_line); } myfree(in_line); myfree(out_line); #/code return im; } /* =back =head1 SEE ALSO Imager(3) =head1 AUTHOR Tony Cook =cut */