+ }
+ if (f->src->channels == 3) {
+ /* just set the alpha */
+ for (i = 0; i < width; ++i) {
+ data->channel[3] = 255;
+ data++;
+ }
+ }
+ else if (f->src->channels == 2) {
+ /* copy the alpha to channel 3, duplicate the grey value */
+ for (i = 0; i < width; ++i) {
+ data->channel[3] = data->channel[1];
+ data->channel[1] = data->channel[2] = data->channel[0];
+ data++;
+ }
+ }
+ else if (f->src->channels == 1) {
+ /* set the alpha, duplicate grey */
+ for (i = 0; i < width; ++i) {
+ data->channel[3] = 255;
+ data->channel[1] = data->channel[2] = data->channel[0];
+ data++;
+ }
+ }
+}
+
+/*
+=item fill_image(fill, x, y, width, channels, data, work)
+
+=cut
+*/
+static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
+ i_fcolor *data) {
+ struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
+ int i = 0;
+
+ if (f->has_matrix) {
+ /* the hard way */
+ while (i < width) {
+ double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
+ double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
+ double ix = floor(rx / f->src->xsize);
+ double iy = floor(ry / f->src->ysize);
+ i_fcolor c[2][2];
+ i_fcolor c2[2];
+ int dy;
+
+ if (f->xoff) {
+ rx += iy * f->xoff;
+ ix = floor(rx / f->src->xsize);
+ }
+ else if (f->yoff) {
+ ry += ix * f->yoff;
+ iy = floor(ry / f->src->ysize);
+ }
+ rx -= ix * f->src->xsize;
+ ry -= iy * f->src->ysize;
+
+ for (dy = 0; dy < 2; ++dy) {
+ if ((int)rx == f->src->xsize-1) {
+ i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
+ i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
+ }
+ else {
+ i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize,
+ c[dy]);
+ }
+ c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
+ }
+ *data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
+ ++i;
+ }
+ }
+ else {
+ /* the easy way */
+ /* this should be possible to optimize to use i_glin() */
+ while (i < width) {
+ int rx = x+i;
+ int ry = y;
+ int ix = rx / f->src->xsize;
+ int iy = ry / f->src->ysize;
+
+ if (f->xoff) {
+ rx += iy * f->xoff;
+ ix = rx / f->src->xsize;
+ }
+ else if (f->yoff) {
+ ry += ix * f->yoff;
+ iy = ry / f->src->xsize;
+ }
+ rx -= ix * f->src->xsize;
+ ry -= iy * f->src->ysize;
+ i_gpixf(f->src, rx, ry, data);
+ ++data;
+ ++i;
+ }
+ }
+ if (f->src->channels == 3) {
+ /* just set the alpha */
+ for (i = 0; i < width; ++i) {
+ data->channel[3] = 1.0;
+ data++;
+ }
+ }
+ else if (f->src->channels == 2) {
+ /* copy the alpha to channel 3, duplicate the grey value */
+ for (i = 0; i < width; ++i) {
+ data->channel[3] = data->channel[1];
+ data->channel[1] = data->channel[2] = data->channel[0];
+ data++;
+ }
+ }
+ else if (f->src->channels == 1) {
+ /* set the alpha, duplicate grey */
+ for (i = 0; i < width; ++i) {
+ data->channel[3] = 1.0;
+ data->channel[1] = data->channel[2] = data->channel[0];
+ data++;
+ }
+ }
+}
+
+static void combine_replace(i_color *, i_color *, int, int);
+static void combine_replacef(i_fcolor *, i_fcolor *, int, int);
+static void combine_alphablend(i_color *, i_color *, int, int);
+static void combine_alphablendf(i_fcolor *, i_fcolor *, int, int);
+static void combine_mult(i_color *, i_color *, int, int);
+static void combine_multf(i_fcolor *, i_fcolor *, int, int);
+static void combine_dissolve(i_color *, i_color *, int, int);
+static void combine_dissolvef(i_fcolor *, i_fcolor *, int, int);
+static void combine_add(i_color *, i_color *, int, int);
+static void combine_addf(i_fcolor *, i_fcolor *, int, int);
+static void combine_subtract(i_color *, i_color *, int, int);
+static void combine_subtractf(i_fcolor *, i_fcolor *, int, int);
+static void combine_diff(i_color *, i_color *, int, int);
+static void combine_difff(i_fcolor *, i_fcolor *, int, int);
+static void combine_darken(i_color *, i_color *, int, int);
+static void combine_darkenf(i_fcolor *, i_fcolor *, int, int);
+static void combine_lighten(i_color *, i_color *, int, int);
+static void combine_lightenf(i_fcolor *, i_fcolor *, int, int);
+static void combine_hue(i_color *, i_color *, int, int);
+static void combine_huef(i_fcolor *, i_fcolor *, int, int);
+static void combine_sat(i_color *, i_color *, int, int);
+static void combine_satf(i_fcolor *, i_fcolor *, int, int);
+static void combine_value(i_color *, i_color *, int, int);
+static void combine_valuef(i_fcolor *, i_fcolor *, int, int);
+static void combine_color(i_color *, i_color *, int, int);
+static void combine_colorf(i_fcolor *, i_fcolor *, int, int);
+
+static struct i_combines {
+ i_fill_combine_f combine;
+ i_fill_combinef_f combinef;
+} combines[] =
+{
+ { /* replace */
+ combine_replace,
+ combine_replacef,
+ },
+ { /* alpha blend */
+ combine_alphablend,
+ combine_alphablendf,
+ },
+ {
+ /* multiply */
+ combine_mult,
+ combine_multf,
+ },
+ {
+ /* dissolve */
+ combine_dissolve,
+ combine_dissolvef,
+ },
+ {
+ /* add */
+ combine_add,
+ combine_addf,
+ },
+ {
+ /* subtract */
+ combine_subtract,
+ combine_subtractf,
+ },
+ {
+ /* diff */
+ combine_diff,
+ combine_difff,
+ },
+ {
+ combine_lighten,
+ combine_lightenf,
+ },
+ {
+ combine_darken,
+ combine_darkenf,
+ },
+ {
+ combine_hue,
+ combine_huef,
+ },
+ {
+ combine_sat,
+ combine_satf,
+ },
+ {
+ combine_value,
+ combine_valuef,
+ },
+ {
+ combine_color,
+ combine_colorf,
+ },
+};
+
+/*
+=item i_get_combine(combine, color_func, fcolor_func)
+
+=cut
+*/
+
+void i_get_combine(int combine, i_fill_combine_f *color_func,
+ i_fill_combinef_f *fcolor_func) {
+ if (combine < 0 || combine > sizeof(combines) / sizeof(*combines))
+ combine = 0;
+
+ *color_func = combines[combine].combine;
+ *fcolor_func = combines[combine].combinef;
+}
+
+static void combine_replace(i_color *out, i_color *in, int channels, int count) {
+ while (count--) {
+ *out++ = *in++;
+ }
+}
+
+static void combine_replacef(i_fcolor *out, i_fcolor *in, int channels, int count) {
+ while (count--) {
+ *out++ = *in++;
+ }
+}
+
+static void combine_alphablend(i_color *out, i_color *in, int channels, int count) {
+ while (count--) {
+ COMBINE(*out, *in, channels);
+ ++out;
+ ++in;
+ }
+}
+
+static void combine_alphablendf(i_fcolor *out, i_fcolor *in, int channels, int count) {
+ while (count--) {
+ COMBINEF(*out, *in, channels);
+ ++out;
+ ++in;
+ }
+}
+
+static void combine_mult(i_color *out, i_color *in, int channels, int count) {
+ int ch;
+
+ while (count--) {
+ double mult[MAXCHANNELS];
+ mult[3] = in->channel[3];
+ for (ch = 0; ch < (channels); ++ch) {
+ if (ch != 3)
+ mult[ch] = (out->channel[ch] * in->channel[ch]) * (1.0 / 255);
+ }
+ COMBINEA(*out, mult, channels);
+ ++out;
+ ++in;
+ }
+}
+
+static void combine_multf(i_fcolor *out, i_fcolor *in, int channels, int count) {
+ int ch;
+
+ while (count--) {
+ i_fcolor c = *in;
+ for (ch = 0; ch < channels; ++ch) {
+ if (ch != 3)
+ c.channel[ch] = out->channel[ch] * in->channel[ch];
+ }
+ COMBINEF(*out, c, channels);
+ ++out;
+ ++in;
+ }
+}
+
+static void combine_dissolve(i_color *out, i_color *in, int channels, int count) {
+ while (count--) {
+ if (in->channel[3] > rand() * (255.0 / RAND_MAX))
+ COMBINE(*out, *in, channels);
+ ++out;
+ ++in;
+ }
+}
+
+static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) {
+ while (count--) {
+ if (in->channel[3] > rand() * (1.0 / RAND_MAX))
+ COMBINEF(*out, *in, channels);
+ ++out;
+ ++in;
+ }
+}
+
+static void combine_add(i_color *out, i_color *in, int channels, int count) {
+ int ch;
+
+ while (count--) {
+ i_color c = *in;
+ for (ch = 0; ch < (channels); ++ch) {
+ if (ch != 3) {
+ int total = out->channel[ch] + in->channel[ch];
+ if (total > 255)
+ total = 255;
+ c.channel[ch] = total;
+ }
+ }
+ COMBINE(*out, c, channels);
+ ++out;
+ ++in;