/*
=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
*/
#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;
mm_log((1,"i_convert(im %p, src %p, coeff %p,outchan %d, inchan %d)\n",
im, src, coeff, outchan, inchan));
i_clear_error();
ilimit = inchan;
if (ilimit > src->channels)
ilimit = src->channels;
if (outchan > MAXCHANNELS) {
i_push_error(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 = i_img_pal_new(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 <tony@develop-help.com>
=cut
*/