#define RENDER_MAGIC 0x765AE
-typedef void (*render_color_f)(i_render *, int, int, int, unsigned char const *src, i_color const *color);
+typedef void (*render_color_f)(i_render *, i_img_dim, i_img_dim, i_img_dim, unsigned char const *src, i_color const *color);
+
+#define i_has_alpha(channels) ((channels) == 2 || (channels) == 4)
+
+#define i_color_channels(channels) (i_has_alpha(channels) ? (channels)-1 : (channels))
#code
-static void IM_SUFFIX(render_color_alpha)(i_render *r, int x, int y, int width, unsigned char const *src, i_color const *color);
-static void IM_SUFFIX(render_color_13)(i_render *r, int x, int y, int width, unsigned char const *src, i_color const *color);
+static void IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width, unsigned char const *src, i_color const *color);
+static void IM_SUFFIX(render_color_13)(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width, unsigned char const *src, i_color const *color);
static render_color_f IM_SUFFIX(render_color_tab)[] =
{
IM_SUFFIX(render_color_alpha),
};
+static void IM_SUFFIX(combine_line_noalpha)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
+static void IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
+/* the copy variant copies the source alpha to the the output alpha channel */
+static void IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
+
+static void IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
+static void IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
+
#/code
+/*
+=item i_render_new(im, width)
+=category Blit tools
+
+Allocate a new C<i_render> object and initialize it.
+
+=cut
+*/
+
+i_render *
+i_render_new(i_img *im, i_img_dim width) {
+ i_render *r = mymalloc(sizeof(i_render));
+
+ i_render_init(r, im, width);
+
+ return r;
+}
+
+/*
+=item i_render_delete(r)
+=category Blit tools
+
+Release an C<i_render> object.
+
+=cut
+*/
+
void
-i_render_init(i_render *r, i_img *im, int width) {
+i_render_delete(i_render *r) {
+ i_render_done(r);
+ myfree(r);
+}
+
+void
+i_render_init(i_render *r, i_img *im, i_img_dim width) {
r->magic = RENDER_MAGIC;
r->im = im;
- r->width = width;
+ r->line_width = width;
r->line_8 = NULL;
r->line_double = NULL;
-#code im->bits <= 8
- r->IM_SUFFIX(line) = mymalloc(sizeof(i_fcolor) * width);
-#/code
+ r->fill_width = width;
+ r->fill_line_8 = NULL;
+ r->fill_line_double = NULL;
}
void
i_render_done(i_render *r) {
if (r->line_8)
myfree(r->line_8);
- else
+ if (r->line_double)
myfree(r->line_double);
+ if (r->fill_line_8)
+ myfree(r->fill_line_8);
+ if (r->fill_line_double)
+ myfree(r->fill_line_double);
r->magic = 0;
}
+static void
+alloc_line(i_render *r, i_img_dim width, i_img_dim eight_bit) {
+ if (width > r->line_width) {
+ i_img_dim new_width = r->line_width * 2;
+ if (new_width < width)
+ new_width = width;
+
+ if (eight_bit) {
+ if (r->line_8)
+ r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
+ else
+ r->line_8 = mymalloc(sizeof(i_color) * new_width);
+ if (r->line_double) {
+ myfree(r->line_double);
+ r->line_double = NULL;
+ }
+ }
+ else {
+ if (r->line_double)
+ r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
+ else
+ r->line_double = mymalloc(sizeof(i_fcolor) * new_width);
+ if (r->line_8) {
+ myfree(r->line_8);
+ r->line_double = NULL;
+ }
+ }
+
+ r->line_width = new_width;
+ }
+ else {
+ if (eight_bit) {
+ if (!r->line_8)
+ r->line_8 = mymalloc(sizeof(i_color) * r->line_width);
+ if (r->line_double) {
+ myfree(r->line_double);
+ r->line_double = NULL;
+ }
+ }
+ else {
+ if (!r->line_double)
+ r->line_double = mymalloc(sizeof(i_fcolor) * r->line_width);
+ if (r->line_8) {
+ myfree(r->line_8);
+ r->line_8 = NULL;
+ }
+ }
+ }
+}
+
+static void
+alloc_fill_line(i_render *r, i_img_dim width, int eight_bit) {
+ if (width > r->fill_width) {
+ i_img_dim new_width = r->fill_width * 2;
+ if (new_width < width)
+ new_width = width;
+
+ if (eight_bit) {
+ if (r->line_8)
+ r->fill_line_8 = myrealloc(r->fill_line_8, sizeof(i_color) * new_width);
+ else
+ r->fill_line_8 = mymalloc(sizeof(i_color) * new_width);
+ if (r->fill_line_double) {
+ myfree(r->fill_line_double);
+ r->fill_line_double = NULL;
+ }
+ }
+ else {
+ if (r->fill_line_double)
+ r->fill_line_double = myrealloc(r->fill_line_double, sizeof(i_fcolor) * new_width);
+ else
+ r->fill_line_double = mymalloc(sizeof(i_fcolor) * new_width);
+ if (r->fill_line_8) {
+ myfree(r->fill_line_8);
+ r->fill_line_double = NULL;
+ }
+ }
+
+ r->fill_width = new_width;
+ }
+ else {
+ if (eight_bit) {
+ if (!r->fill_line_8)
+ r->fill_line_8 = mymalloc(sizeof(i_color) * r->fill_width);
+ if (r->fill_line_double) {
+ myfree(r->fill_line_double);
+ r->fill_line_double = NULL;
+ }
+ }
+ else {
+ if (!r->fill_line_double)
+ r->fill_line_double = mymalloc(sizeof(i_fcolor) * r->fill_width);
+ if (r->fill_line_8) {
+ myfree(r->fill_line_8);
+ r->fill_line_8 = NULL;
+ }
+ }
+ }
+}
+
+/*
+=item i_render_color(r, x, y, width, source, color)
+=category Blit tools
+
+Render the given color with the coverage specified by C<source[0]> to
+C<source[width-1]>.
+
+Renders in normal combine mode.
+
+=cut
+*/
+
void
-i_render_color(i_render *r, int x, int y, int width, unsigned char const *src,
- i_color const *color) {
+i_render_color(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
+ unsigned char const *src, i_color const *color) {
i_img *im = r->im;
if (y < 0 || y >= im->ysize)
return;
if (!width)
return;
- /* make sure our line buffer is big enough */
- if (width > r->width) {
- int new_width = r->width * 2;
- if (new_width < width)
- new_width = width;
+ alloc_line(r, width, r->im->bits <= 8);
+
+#code r->im->bits <= 8
+ /*if (r->IM_SUFFIX(line) == NULL)
+ r->IM_SUFFIX(line) = mymalloc(sizeof(IM_COLOR) * r->width);*/
+ (IM_SUFFIX(render_color_tab)[im->channels])(r, x, y, width, src, color);
+#/code
+}
+
+/*
+=item i_render_fill(r, x, y, width, source, fill)
+=category Blit tools
+
+Render the given fill with the coverage in C<source[0]> through
+C<source[width-1]>.
+
+=cut
+*/
- if (r->line_8)
- r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
- else
- r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
+void
+i_render_fill(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
+ unsigned char const *src, i_fill_t *fill) {
+ i_img *im = r->im;
+ int fill_channels = im->channels;
+
+ if (fill_channels == 1 || fill_channels == 3)
+ ++fill_channels;
+
+ if (y < 0 || y >= im->ysize)
+ return;
+ if (x < 0) {
+ width += x;
+ src -= x;
+ x = 0;
}
+ if (x + width > im->xsize) {
+ width = im->xsize - x;
+ }
+ if (x >= im->xsize || x + width <= 0 || width <= 0)
+ return;
-#code r->im->bits <= 8
- (IM_SUFFIX(render_color_tab)[im->channels])(r, x, y, width, src, color);
+ if (src) {
+ /* avoid as much work as we can */
+ while (width > 0 && *src == 0) {
+ --width;
+ ++src;
+ ++x;
+ }
+ while (width > 0 && src[width-1] == 0) {
+ --width;
+ }
+ }
+ if (!width)
+ return;
+
+ alloc_line(r, width, r->im->bits <= 8 && fill->f_fill_with_color != NULL);
+ alloc_fill_line(r, width, r->im->bits <= 8 && fill->f_fill_with_color != NULL);
+
+#code r->im->bits <= 8 && fill->f_fill_with_color
+ if (IM_FILL_COMBINE(fill)) {
+ IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
+ IM_COLOR *destc = r->IM_SUFFIX(line);
+ IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(fill_line));
+ if (src) {
+ unsigned char const *srcc = src;
+ IM_COLOR *fillc = r->IM_SUFFIX(fill_line);
+ i_img_dim work_width = width;
+ while (work_width) {
+ if (*srcc == 0) {
+ fillc->channel[fill_channels-1] = 0;
+ }
+ else if (*srcc != 255) {
+ fillc->channel[fill_channels-1] =
+ fillc->channel[fill_channels-1] * *srcc / 255;
+ }
+ --work_width;
+ ++srcc;
+ ++fillc;
+ }
+ }
+ IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
+ IM_FILL_COMBINE(fill)(destc, srcc, r->im->channels, width);
+ }
+ else {
+ if (src) {
+ i_img_dim work_width = width;
+ IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
+ IM_COLOR *destc = r->IM_SUFFIX(line);
+ int ch;
+
+ IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(fill_line));
+ IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
+ while (work_width) {
+ if (*src == 255) {
+ /* just replace it */
+ *destc = *srcc;
+ }
+ else if (*src) {
+ for (ch = 0; ch < im->channels; ++ch) {
+ IM_WORK_T work = (destc->channel[ch] * (255 - *src)
+ + srcc->channel[ch] * *src) / 255.0;
+ destc->channel[ch] = IM_LIMIT(work);
+ }
+ }
+
+ ++srcc;
+ ++destc;
+ ++src;
+ --work_width;
+ }
+ }
+ else { /* if (src) */
+ IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(line));
+ }
+ }
+ IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
#/code
}
+#if 0
+
+/* for debuggin */
+
static void
-dump_src(const char *note, unsigned char const *src, int width) {
- int i;
- printf("%s - %p/%d\n", note, src, width);
+dump_src(const char *note, unsigned char const *src, i_img_dim width) {
+ i_img_dim i;
+ printf("%s - %p/%" i_DF "\n", note, src, i_DFc(width));
for (i = 0; i < width; ++i) {
printf("%02x ", src[i]);
}
putchar('\n');
}
+#endif
+
#code
+/*
+=item i_render_line(r, x, y, width, source, fill)
+=category Blit tools
+
+Render the given fill with the coverage in C<source[0]> through
+C<source[width-1]>.
+
+=cut
+
+=item i_render_linef(r, x, y, width, source, fill)
+=category Blit tools
+
+Render the given fill with the coverage in C<source[0]> through
+C<source[width-1]>.
+
+=cut
+*/
+
+void
+IM_RENDER_LINE(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
+ const IM_SAMPLE_T *src, IM_COLOR *line,
+ IM_FILL_COMBINE_F combine) {
+ i_img *im = r->im;
+ int src_chans = im->channels;
+
+ /* src must always have an alpha channel */
+ if (src_chans == 1 || src_chans == 3)
+ ++src_chans;
+
+ if (y < 0 || y >= im->ysize)
+ return;
+ if (x < 0) {
+ src -= x;
+ line -= x;
+ width += x;
+ x = 0;
+ }
+ if (x + width > im->xsize)
+ width = r->im->xsize - x;
+
+#ifdef IM_EIGHT_BIT
+ alloc_line(r, width, 1);
+#else
+ alloc_line(r, width, 0);
+#endif
+
+ if (combine) {
+ if (src) {
+ i_img_dim work_width = width;
+ IM_COLOR *linep = line;
+ const IM_SAMPLE_T *srcp = src;
+ int alpha_chan = src_chans - 1;
+
+ while (work_width) {
+ if (*srcp) {
+ if (*srcp != IM_SAMPLE_MAX)
+ linep->channel[alpha_chan] =
+ linep->channel[alpha_chan] * *srcp / IM_SAMPLE_MAX;
+ }
+ else {
+ linep->channel[alpha_chan] = 0;
+ }
+ --work_width;
+ ++srcp;
+ ++linep;
+ }
+ }
+ IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
+ combine(r->IM_SUFFIX(line), line, im->channels, width);
+ IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
+ }
+ else {
+ if (src) {
+ i_img_dim work_width = width;
+ IM_COLOR *srcc = line;
+ IM_COLOR *destc = r->IM_SUFFIX(line);
+
+ IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
+ while (work_width) {
+ if (*src == 255) {
+ /* just replace it */
+ *destc = *srcc;
+ }
+ else if (*src) {
+ int ch;
+ for (ch = 0; ch < im->channels; ++ch) {
+ IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
+ + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
+ destc->channel[ch] = IM_LIMIT(work);
+ }
+ }
+
+ ++srcc;
+ ++destc;
+ ++src;
+ --work_width;
+ }
+ IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
+ }
+ else {
+ IM_PLIN(im, x, x+width, y, line);
+ }
+ }
+}
+
static
void
-IM_SUFFIX(render_color_13)(i_render *r, int x, int y, int width,
- unsigned char const *src, i_color const *color) {
+IM_SUFFIX(render_color_13)(i_render *r, i_img_dim x, i_img_dim y,
+ i_img_dim width, unsigned char const *src,
+ i_color const *color) {
i_img *im = r->im;
IM_COLOR *linep = r->IM_SUFFIX(line);
int ch, channels = im->channels;
- int fetch_offset;
+ i_img_dim fetch_offset;
+ int color_alpha = color->channel[im->channels];
#undef STORE_COLOR
#ifdef IM_EIGHT_BIT
#define STORE_COLOR (*color)
#endif
fetch_offset = 0;
- while (fetch_offset < width && *src == 0xFF) {
- *linep++ = STORE_COLOR;
- ++src;
- ++fetch_offset;
+ if (color_alpha == 0xFF) {
+ while (fetch_offset < width && *src == 0xFF) {
+ *linep++ = STORE_COLOR;
+ ++src;
+ ++fetch_offset;
+ }
}
IM_GLIN(im, x+fetch_offset, x+width, y, linep);
while (fetch_offset < width) {
#ifdef IM_EIGHT_BIT
- IM_WORK_T alpha = *src++;
+ IM_WORK_T alpha = *src++ * color_alpha / 255;
#else
- IM_WORK_T alpha = *src++ / 255.0;
+ IM_WORK_T alpha = *src++ * color_alpha / (255.0 * 255.0);
#endif
if (alpha == IM_SAMPLE_MAX)
*linep = STORE_COLOR;
static
void
-IM_SUFFIX(render_color_alpha)(i_render *r, int x, int y, int width,
- unsigned char const *src, i_color const *color) {
+IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y,
+ i_img_dim width, unsigned char const *src,
+ i_color const *color) {
IM_COLOR *linep = r->IM_SUFFIX(line);
int ch;
int alpha_channel = r->im->channels - 1;
- int fetch_offset;
+ i_img_dim fetch_offset;
+ int color_alpha = color->channel[alpha_channel];
#undef STORE_COLOR
#ifdef IM_EIGHT_BIT
#define STORE_COLOR (*color)
#endif
fetch_offset = 0;
- while (fetch_offset < width && *src == 0xFF) {
- *linep++ = STORE_COLOR;
- ++src;
- ++fetch_offset;
+ if (color->channel[alpha_channel] == 0xFF) {
+ while (fetch_offset < width && *src == 0xFF) {
+ *linep++ = STORE_COLOR;
+ ++src;
+ ++fetch_offset;
+ }
}
IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
while (fetch_offset < width) {
#ifdef IM_EIGHT_BIT
- IM_WORK_T src_alpha = *src++;
+ IM_WORK_T src_alpha = *src++ * color_alpha / 255;
#else
- IM_WORK_T src_alpha = *src++ / 255.0;
+ IM_WORK_T src_alpha = *src++ * color_alpha / (255.0 * 255.0);
#endif
if (src_alpha == IM_SAMPLE_MAX)
*linep = STORE_COLOR;
else if (src_alpha) {
- IM_WORK_T remains = - src_alpha;
+ IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
IM_WORK_T orig_alpha = linep->channel[alpha_channel];
IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
for (ch = 0; ch < alpha_channel; ++ch) {
++fetch_offset;
}
IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
+#undef STORE_COLOR
+}
+
+/* combine a line of image data with an output line, both the input
+ and output lines include an alpha channel.
+
+ Both input and output lines have I<channels> of data, channels
+ should be either 2 or 4.
+*/
+
+static void
+IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in,
+ int channels, i_img_dim count) {
+ int ch;
+ int alpha_channel = channels - 1;
+
+ while (count) {
+ IM_WORK_T src_alpha = in->channel[alpha_channel];
+
+ if (src_alpha == IM_SAMPLE_MAX)
+ *out = *in;
+ else if (src_alpha) {
+ IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
+ IM_WORK_T orig_alpha = out->channel[alpha_channel];
+ IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
+
+ for (ch = 0; ch < alpha_channel; ++ch) {
+ out->channel[ch] = ( src_alpha * in->channel[ch]
+ + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
+ ) / dest_alpha;
+ }
+ out->channel[alpha_channel] = dest_alpha;
+ }
+
+ ++out;
+ ++in;
+ --count;
+ }
+}
+
+/* combine a line of image data with an output line. The input line
+ includes an alpha channel, the output line has no alpha channel.
+
+ The input line has I<channels>+1 of color data. The output line
+ has I<channels> of color data.
+*/
+
+static void
+IM_SUFFIX(combine_line_noalpha)
+ (IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
+ int ch;
+
+ while (count) {
+ IM_WORK_T src_alpha = in->channel[channels];
+
+ if (src_alpha == IM_SAMPLE_MAX)
+ *out = *in;
+ else if (src_alpha) {
+ IM_WORK_T remains;
+
+ remains = IM_SAMPLE_MAX - src_alpha;
+ for (ch = 0; ch < channels; ++ch) {
+ out->channel[ch] = ( in->channel[ch] * src_alpha
+ + out->channel[ch] * remains) / IM_SAMPLE_MAX;
+ }
+ }
+
+ ++out;
+ ++in;
+ --count;
+ }
}
+/* combine a line of image data with an output line, both the input
+ and output lines include an alpha channel.
+
+ Both input and output lines have I<channels> of data, channels
+ should be either 2 or 4.
+
+ This variant does not modify the output alpha channel.
+*/
+
+static void
+IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
+ int channels, i_img_dim count) {
+ int ch;
+ int alpha_channel = channels - 1;
+
+ while (count) {
+ IM_WORK_T src_alpha = in->channel[alpha_channel];
+
+ if (src_alpha == IM_SAMPLE_MAX)
+ *out = *in;
+ else if (src_alpha) {
+ IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
+ IM_WORK_T orig_alpha = out->channel[alpha_channel];
+ IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
+
+ for (ch = 0; ch < alpha_channel; ++ch) {
+ out->channel[ch] = ( src_alpha * in->channel[ch]
+ + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
+ ) / dest_alpha;
+ }
+ }
+
+ ++out;
+ ++in;
+ --count;
+ }
+}
+
+static void
+IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
+ if (channels == 2 || channels == 4)
+ IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
+ else
+ IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
+}
+
+static void
+IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
+ if (channels == 2 || channels == 4)
+ IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
+ else
+ IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
+}
+
+static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+
+static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
+{
+ NULL,
+ IM_SUFFIX(combine_alphablend),
+ IM_SUFFIX(combine_mult),
+ IM_SUFFIX(combine_dissolve),
+ IM_SUFFIX(combine_add),
+ IM_SUFFIX(combine_subtract),
+ IM_SUFFIX(combine_diff),
+ IM_SUFFIX(combine_lighten),
+ IM_SUFFIX(combine_darken),
+ IM_SUFFIX(combine_hue),
+ IM_SUFFIX(combine_sat),
+ IM_SUFFIX(combine_value),
+ IM_SUFFIX(combine_color)
+};
+
+#/code
+
+/*
+=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_8) / sizeof(*combines_8))
+ combine = 0;
+
+ *color_func = combines_8[combine];
+ *fcolor_func = combines_double[combine];
+}
+
+#code
+
+/*
+ Three good references for implementing combining modes:
+
+ http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
+ referenced as [svg1.2]
+
+ http://gimp-savvy.com/BOOK/index.html?node55.html
+ ("The Blending Modes", if it changes)
+ referenced as [savvy]
+
+ http://www.pegtop.net/delphi/articles/blendmodes/
+ referenced as [pegtop]
+
+ Where differences exist, I follow the SVG practice, the gimp
+ practice, and lastly pegtop.
+*/
+
+
+static void
+IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ IM_SUFFIX(combine_line)(out, in, channels, count);
+}
+
+/*
+Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+Da' = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
+ = Sa + Da - Sa.Da
+
+When Da=1
+
+Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
+ */
+static void
+IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int ch;
+ IM_COLOR *inp = in;
+ IM_COLOR *outp = out;
+ i_img_dim work_count = count;
+ int color_channels = i_color_channels(channels);
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+
+ if (src_alpha) {
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha
+ - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
+
+ for (ch = 0; ch < color_channels; ++ch) {
+ outp->channel[ch] =
+ (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
+ * orig_alpha
+ + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
+ + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
+ / IM_SAMPLE_MAX / dest_alpha;
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
+
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ outp->channel[ch] =
+ (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
+ + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
+ }
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+}
+
+static void
+IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int color_channels = i_color_channels(channels);
+ int ch;
+
+ if (i_has_alpha(channels)) {
+ while (count--) {
+ if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ out->channel[ch] = in->channel[ch];
+ }
+ out->channel[color_channels] = IM_SAMPLE_MAX;
+ }
+ ++out;
+ ++in;
+ }
+ }
+ else {
+ while (count--) {
+ if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ out->channel[ch] = in->channel[ch];
+ }
+ }
+ ++out;
+ ++in;
+ }
+ }
+}
+
+/*
+Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
+ = Sca + Dca
+Da' = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
+ = Sa + Da
+*/
+
+static void
+IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int ch;
+ int color_channels = i_color_channels(channels);
+ i_img_dim work_count = count;
+ IM_COLOR *inp = in;
+ IM_COLOR *outp = out;
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha;
+ if (dest_alpha > IM_SAMPLE_MAX)
+ dest_alpha = IM_SAMPLE_MAX;
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
+ if (total > IM_SAMPLE_MAX)
+ total = IM_SAMPLE_MAX;
+ outp->channel[ch] = total;
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
+ if (total > IM_SAMPLE_MAX)
+ total = IM_SAMPLE_MAX;
+ outp->channel[ch] = total;
+ }
+ }
+
+ ++outp;
+ ++inp;
+ }
+ }
+}
+
+/*
+ [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
+ it as max(A-B, 0). [svg1.2] doesn't cover it.
+
+ [savvy] doesn't document how it works with an alpha channel. GIMP
+ actually seems to calculate the final value then use the alpha
+ channel to apply that to the target.
+ */
+static void
+IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ i_img_dim work_count = count;
+ int color_channels = i_color_channels(channels);
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha;
+ if (dest_alpha > IM_SAMPLE_MAX)
+ dest_alpha = IM_SAMPLE_MAX;
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T total =
+ (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha)
+ / dest_alpha;
+ if (total < 0)
+ total = 0;
+ outp->channel[ch] = total;
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
+ if (total < 0)
+ total = 0;
+ outp->channel[ch] = total;
+ }
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+}
+
+#ifdef IM_EIGHT_BIT
+#define IM_abs(x) abs(x)
+#else
+#define IM_abs(x) fabs(x)
+#endif
+
+/*
+Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
+ = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
+Da' = Sa + Da - Sa.Da
+*/
+static void
+IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ i_img_dim work_count = count;
+ int color_channels = i_color_channels(channels);
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha
+ - src_alpha * orig_alpha / IM_SAMPLE_MAX;
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T src = inp->channel[ch] * src_alpha;
+ IM_WORK_T orig = outp->channel[ch] * orig_alpha;
+ IM_WORK_T src_da = src * orig_alpha;
+ IM_WORK_T dest_sa = orig * src_alpha;
+ IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
+ outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+ ++inp;
+ ++outp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T src = inp->channel[ch] * src_alpha;
+ IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
+ IM_WORK_T src_da = src * IM_SAMPLE_MAX;
+ IM_WORK_T dest_sa = orig * src_alpha;
+ IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
+ outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
+ }
+ }
+ ++inp;
+ ++outp;
+ }
+ }
+}
+
+#undef IM_abs
+
+/*
+ Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
+ Da' = Sa + Da - Sa.Da
+
+ To hoist some code:
+
+ Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
+ = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
+
+ When Da=1:
+
+ Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
+ = min(Sca, Dc.Sa) + Dc(1-Sa)
+ = Sa.min(Sc, Dc) + Dc - Dc.Sa
+ Da' = Sa + 1 - Sa.1
+ = 1
+ */
+static void
+IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
+ i_img_dim count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ i_img_dim work_count = count;
+ int color_channels = i_color_channels(channels);
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+
+ if (src_alpha) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha
+ - src_alpha * orig_alpha / IM_SAMPLE_MAX;
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T Sca = inp->channel[ch] * src_alpha;
+ IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
+ IM_WORK_T ScaDa = Sca * orig_alpha;
+ IM_WORK_T DcaSa = Dca * src_alpha;
+ IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
+ outp->channel[ch] =
+ (
+ minc + (Sca + Dca) * IM_SAMPLE_MAX
+ - ScaDa - DcaSa
+ ) / (IM_SAMPLE_MAX * dest_alpha);
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
+ ? outp->channel[ch] : inp->channel[ch];
+ outp->channel[ch] =
+ (
+ src_alpha * minc +
+ outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
+ ) / IM_SAMPLE_MAX;
+ }
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+}
+
+static void
+IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ i_img_dim work_count = count;
+ int color_channels = i_color_channels(channels);
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+
+ if (src_alpha) {
+ IM_WORK_T orig_alpha = outp->channel[color_channels];
+ IM_WORK_T dest_alpha = src_alpha + orig_alpha
+ - src_alpha * orig_alpha / IM_SAMPLE_MAX;
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T Sca = inp->channel[ch] * src_alpha;
+ IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
+ IM_WORK_T ScaDa = Sca * orig_alpha;
+ IM_WORK_T DcaSa = Dca * src_alpha;
+ IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
+ outp->channel[ch] =
+ (
+ maxc + (Sca + Dca) * IM_SAMPLE_MAX
+ - ScaDa - DcaSa
+ ) / (IM_SAMPLE_MAX * dest_alpha);
+ }
+ outp->channel[color_channels] = dest_alpha;
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_WORK_T src_alpha = inp->channel[color_channels];
+
+ if (src_alpha) {
+ for (ch = 0; ch < color_channels; ++ch) {
+ IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
+ ? outp->channel[ch] : inp->channel[ch];
+ outp->channel[ch] =
+ (
+ src_alpha * maxc +
+ outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
+ ) / IM_SAMPLE_MAX;
+ }
+ }
+ ++outp;
+ ++inp;
+ }
+ }
+}
+
+#if IM_EIGHT_BIT
+#define IM_RGB_TO_HSV i_rgb_to_hsv
+#define IM_HSV_TO_RGB i_hsv_to_rgb
+#else
+#define IM_RGB_TO_HSV i_rgb_to_hsvf
+#define IM_HSV_TO_RGB i_hsv_to_rgbf
+#endif
+
+static void
+IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ i_img_dim work_count = count;
+
+ if (i_has_alpha(channels)) {
+ while (work_count--) {
+ IM_COLOR c = *inp;
+ IM_RGB_TO_HSV(&c);
+ /* only transfer hue if there's saturation */
+ if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
+ *inp = *outp;
+ IM_RGB_TO_HSV(inp);
+ /* and no point in setting the target hue if the target has no sat */
+ if (inp->channel[1]) {
+ inp->channel[0] = c.channel[0];
+ IM_HSV_TO_RGB(inp);
+ inp->channel[3] = c.channel[3];
+ }
+ else {
+ inp->channel[3] = 0;
+ }
+ }
+ else {
+ inp->channel[3] = 0;
+ }
+
+ ++outp;
+ ++inp;
+ }
+ }
+ else {
+ while (work_count--) {
+ IM_COLOR c = *inp;
+ IM_RGB_TO_HSV(&c);
+ /* only transfer hue if there's saturation */
+ if (c.channel[1] && inp->channel[3]) {
+ *inp = *outp;
+ IM_RGB_TO_HSV(inp);
+ /* and no point in setting the target hue if the target has no sat */
+ if (inp->channel[1]) {
+ inp->channel[0] = c.channel[0];
+ IM_HSV_TO_RGB(inp);
+ inp->channel[3] = c.channel[3];
+ }
+ }
+ else {
+ inp->channel[3] = 0;
+ }
+
+ ++outp;
+ ++inp;
+ }
+ }
+
+ IM_SUFFIX(combine_line_na)(out, in, channels, count);
+ }
+}
+
+static void
+IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ i_img_dim work_count = count;
+
+ while (work_count--) {
+ IM_COLOR c = *inp;
+ *inp = *outp;
+ IM_RGB_TO_HSV(&c);
+ IM_RGB_TO_HSV(inp);
+ inp->channel[1] = c.channel[1];
+ IM_HSV_TO_RGB(inp);
+ inp->channel[3] = c.channel[3];
+ ++outp;
+ ++inp;
+ }
+
+ IM_SUFFIX(combine_line_na)(out, in, channels, count);
+ }
+}
+
+static void
+IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ i_img_dim work_count = count;
+
+ while (work_count--) {
+ IM_COLOR c = *inp;
+ *inp = *outp;
+ IM_RGB_TO_HSV(&c);
+ IM_RGB_TO_HSV(inp);
+ inp->channel[2] = c.channel[2];
+ IM_HSV_TO_RGB(inp);
+ inp->channel[3] = c.channel[3];
+ ++outp;
+ ++inp;
+ }
+ }
+
+ /* all images have a "value channel" - for greyscale it's the only
+ colour channel */
+ IM_SUFFIX(combine_line_na)(out, in, channels, count);
+}
+
+static void
+IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ i_img_dim work_count = count;
+
+ while (work_count--) {
+ IM_COLOR c = *inp;
+ *inp = *outp;
+ IM_RGB_TO_HSV(&c);
+ IM_RGB_TO_HSV(inp);
+ inp->channel[0] = c.channel[0];
+ inp->channel[1] = c.channel[1];
+ IM_HSV_TO_RGB(inp);
+ inp->channel[3] = c.channel[3];
+ ++outp;
+ ++inp;
+ }
+
+ IM_SUFFIX(combine_line_na)(out, in, channels, count);
+ }
+}
+
+#undef IM_RGB_TO_HSV
+#undef IM_HSV_TO_RGB
+
#/code