X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/7a606d29ffdfce11301c7582beaf194765b7cf35..167660cd10ca5e3eaef03ce9b067651a75cfb022:/fills.c diff --git a/fills.c b/fills.c index bb9700c6..6e929199 100644 --- a/fills.c +++ b/fills.c @@ -1,41 +1,130 @@ -#include "image.h" -#include "imagei.h" +#include "imager.h" +#include "imageri.h" /* +=head1 NAME -Possible fill types: - - solid colour - - hatched (pattern, fg, bg) - - tiled image - - regmach - - tiling? - - generic? +fills.c - implements the basic general fills +=head1 SYNOPSIS + + i_fill_t *fill; + i_color c1, c2; + i_fcolor fc1, fc2; + int combine; + fill = i_new_fill_solidf(&fc1, combine); + fill = i_new_fill_solid(&c1, combine); + fill = i_new_fill_hatchf(&fc1, &fc2, combine, hatch, cust_hash, dx, dy); + fill = i_new_fill_hatch(&c1, &c2, combine, hatch, cust_hash, dx, dy); + fill = i_new_fill_image(im, matrix, xoff, yoff, combine); + i_fill_destroy(fill); + +=head1 DESCRIPTION + +Implements the basic general fills, which can be used for filling some +shapes and for flood fills. + +Each fill can implement up to 3 functions: + +=over + +=item fill_with_color + +called for fills on 8-bit images. This can be NULL in which case the +fill_with_colorf function is called. + +=item fill_with_fcolor + +called for fills on non-8-bit images or when fill_with_color is NULL. + +=item destroy + +called by i_fill_destroy() if non-NULL, to release any extra resources +that the fill may need. + +=back + +fill_with_color and fill_with_fcolor are basically the same function +except that the first works with lines of i_color and the second with +lines of i_fcolor. + +If the combines member if non-zero the line data is populated from the +target image before calling fill_with_*color. + +fill_with_color needs to fill the I parameter with the fill +pixels. If combines is non-zero it the fill pixels should be combined +with the existing data. + +The current fills are: + +=over + +=item * + +solid fill + +=item * + +hatched fill + +=item * + +fountain fill + +=back + +Fountain fill is implemented by L. + +Other fills that could be implemented include: + +=over + +=item * + +image - an image tiled over the fill area, with an offset either +horizontally or vertically. + +=item * + +checkerboard - combine 2 fills in a checkerboard + +=item * + +combine - combine the levels of 2 other fills based in the levels of +an image + +=item * + +regmach - use the register machine to generate colors + +=back + +=over + +=cut */ -static i_color fcolor_to_color(i_fcolor *c) { +static i_color fcolor_to_color(const i_fcolor *c) { int ch; i_color out; for (ch = 0; ch < MAXCHANNELS; ++ch) out.channel[ch] = SampleFTo8(c->channel[ch]); + + return out; } -static i_fcolor color_to_fcolor(i_color *c) { +static i_fcolor color_to_fcolor(const i_color *c) { int ch; - i_color out; + i_fcolor out; for (ch = 0; ch < MAXCHANNELS; ++ch) out.channel[ch] = Sample8ToF(c->channel[ch]); -} -typedef struct -{ - i_fill_t base; - i_color c; - i_fcolor fc; -} i_fill_solid_t; + return out; +} +/* alpha combine in with out */ #define COMBINE(out, in, channels) \ { \ int ch; \ @@ -45,6 +134,18 @@ typedef struct } \ } +/* alpha combine in with out, in this case in is a simple array of + samples, potentially not integers - the mult combiner uses doubles + for accuracy */ +#define COMBINEA(out, in, channels) \ + { \ + int ch; \ + for (ch = 0; ch < (channels); ++ch) { \ + (out).channel[ch] = ((out).channel[ch] * (255 - (in)[3]) \ + + (in)[ch] * (in)[3]) / 255; \ + } \ + } + #define COMBINEF(out, in, channels) \ { \ int ch; \ @@ -54,6 +155,13 @@ typedef struct } \ } +typedef struct +{ + i_fill_t base; + i_color c; + i_fcolor fc; +} i_fill_solid_t; + static void fill_solid(i_fill_t *, int x, int y, int width, int channels, i_color *); static void fill_solidf(i_fill_t *, int x, int y, int width, int channels, @@ -69,7 +177,8 @@ static i_fill_solid_t base_solid_fill = fill_solid, fill_solidf, NULL, - 0 + NULL, + NULL, }, }; static i_fill_solid_t base_solid_fill_comb = @@ -78,10 +187,21 @@ static i_fill_solid_t base_solid_fill_comb = fill_solid_comb, fill_solidf_comb, NULL, - 1 + NULL, + NULL, }, }; +/* +=item i_fill_destroy(fill) + +=category Fills + +Call to destroy any fill object. + +=cut +*/ + void i_fill_destroy(i_fill_t *fill) { if (fill->destroy) @@ -89,13 +209,27 @@ i_fill_destroy(i_fill_t *fill) { myfree(fill); } +/* +=item i_new_fill_solidf(color, combine) + +=category Fills + +Create a solid fill based on a float color. + +If combine is non-zero then alpha values will be combined. + +=cut +*/ + i_fill_t * -i_new_fill_solidf(i_fcolor *c, int combine) { +i_new_fill_solidf(const i_fcolor *c, int combine) { int ch; - i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); + i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */ - if (combine && c->channel[3] < 1.0) + if (combine) { *fill = base_solid_fill_comb; + i_get_combine(combine, &fill->base.combine, &fill->base.combinef); + } else *fill = base_solid_fill; fill->fc = *c; @@ -106,13 +240,27 @@ i_new_fill_solidf(i_fcolor *c, int combine) { return &fill->base; } +/* +=item i_new_fill_solid(color, combine) + +=category Fills + +Create a solid fill based on an 8-bit color. + +If combine is non-zero then alpha values will be combined. + +=cut +*/ + i_fill_t * -i_new_fill_solid(i_color *c, int combine) { +i_new_fill_solid(const i_color *c, int combine) { int ch; - i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); + i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */ - if (combine && c->channel[3] < 255) + if (combine) { *fill = base_solid_fill_comb; + i_get_combine(combine, &fill->base.combine, &fill->base.combinef); + } else *fill = base_solid_fill; fill->c = *c; @@ -123,46 +271,6 @@ i_new_fill_solid(i_color *c, int combine) { return &fill->base; } -#define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill)) - -static void -fill_solid(i_fill_t *fill, int x, int y, int width, int channels, - i_color *data) { - while (width-- > 0) { - *data++ = T_SOLID_FILL(fill)->c; - } -} - -static void -fill_solidf(i_fill_t *fill, int x, int y, int width, int channels, - i_fcolor *data) { - while (width-- > 0) { - *data++ = T_SOLID_FILL(fill)->fc; - } -} - -static void -fill_solid_comb(i_fill_t *fill, int x, int y, int width, int channels, - i_color *data) { - i_color c = T_SOLID_FILL(fill)->c; - - while (width-- > 0) { - COMBINE(*data, c, channels); - ++data; - } -} - -static void -fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels, - i_fcolor *data) { - i_fcolor c = T_SOLID_FILL(fill)->fc; - - while (width-- > 0) { - COMBINEF(*data, c, channels); - ++data; - } -} - static unsigned char builtin_hatches[][8] = { @@ -290,6 +398,10 @@ builtin_hatches[][8] = /* L-shaped tiles */ 0xFF, 0x84, 0x84, 0x9C, 0x94, 0x9C, 0x90, 0x90, }, + { + /* wider stipple */ + 0x80, 0x40, 0x20, 0x00, 0x02, 0x04, 0x08, 0x00, + }, }; typedef struct @@ -305,23 +417,236 @@ static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, i_color *data); static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, i_fcolor *data); +static +i_fill_t * +i_new_hatch_low(const i_color *fg, const i_color *bg, const i_fcolor *ffg, const i_fcolor *fbg, + int combine, int hatch, const unsigned char *cust_hatch, + int dx, int dy); + +/* +=item i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy) + +=category Fills + +Creates a new hatched fill with the fg color used for the 1 bits in +the hatch and bg for the 0 bits. If combine is non-zero alpha values +will be combined. + +If cust_hatch is non-NULL it should be a pointer to 8 bytes of the +hash definition, with the high-bits to the left. + +If cust_hatch is NULL then one of the standard hatches is used. + +(dx, dy) are an offset into the hatch which can be used to unalign adjoining areas, or to align the origin of a hatch with the the side of a filled area. + +=cut +*/ +i_fill_t * +i_new_fill_hatch(const i_color *fg, const i_color *bg, int combine, int hatch, + const unsigned char *cust_hatch, int dx, int dy) { + return i_new_hatch_low(fg, bg, NULL, NULL, combine, hatch, cust_hatch, + dx, dy); +} + +/* +=item i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy) + +=category Fills + +Creates a new hatched fill with the fg color used for the 1 bits in +the hatch and bg for the 0 bits. If combine is non-zero alpha values +will be combined. + +If cust_hatch is non-NULL it should be a pointer to 8 bytes of the +hash definition, with the high-bits to the left. + +If cust_hatch is NULL then one of the standard hatches is used. + +(dx, dy) are an offset into the hatch which can be used to unalign adjoining areas, or to align the origin of a hatch with the the side of a filled area. + +=cut +*/ +i_fill_t * +i_new_fill_hatchf(const i_fcolor *fg, const i_fcolor *bg, int combine, int hatch, + const unsigned char *cust_hatch, int dx, int dy) { + return i_new_hatch_low(NULL, NULL, fg, bg, combine, hatch, cust_hatch, + dx, dy); +} + +static void fill_image(i_fill_t *fill, int x, int y, int width, int channels, + i_color *data); +static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels, + i_fcolor *data); +struct i_fill_image_t { + i_fill_t base; + i_img *src; + int xoff, yoff; + int has_matrix; + double matrix[9]; +}; + +/* +=item i_new_fill_image(im, matrix, xoff, yoff, combine) + +=category Fills + +Create an image based fill. + +matrix is an array of 9 doubles representing a transformation matrix. + +xoff and yoff are the offset into the image to start filling from. + +=cut +*/ +i_fill_t * +i_new_fill_image(i_img *im, const double *matrix, int xoff, int yoff, int combine) { + struct i_fill_image_t *fill = mymalloc(sizeof(*fill)); /* checked 14jul05 tonyc */ + + fill->base.fill_with_color = fill_image; + fill->base.fill_with_fcolor = fill_imagef; + fill->base.destroy = NULL; + + if (combine) { + i_get_combine(combine, &fill->base.combine, &fill->base.combinef); + } + else { + fill->base.combine = NULL; + fill->base.combinef = NULL; + } + fill->src = im; + if (xoff < 0) + xoff += im->xsize; + fill->xoff = xoff; + if (yoff < 0) + yoff += im->ysize; + fill->yoff = yoff; + if (matrix) { + fill->has_matrix = 1; + memcpy(fill->matrix, matrix, sizeof(fill->matrix)); + } + else + fill->has_matrix = 0; + + return &fill->base; +} + + +#define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill)) + +/* +=back + +=head1 INTERNAL FUNCTIONS + +=over + +=item fill_solid(fill, x, y, width, channels, data) + +The 8-bit sample fill function for non-combining solid fills. + +=cut +*/ +static void +fill_solid(i_fill_t *fill, int x, int y, int width, int channels, + i_color *data) { + while (width-- > 0) { + *data++ = T_SOLID_FILL(fill)->c; + } +} + +/* +=item fill_solid(fill, x, y, width, channels, data) + +The floating sample fill function for non-combining solid fills. + +=cut +*/ +static void +fill_solidf(i_fill_t *fill, int x, int y, int width, int channels, + i_fcolor *data) { + while (width-- > 0) { + *data++ = T_SOLID_FILL(fill)->fc; + } +} + +/* +=item fill_solid_comb(fill, x, y, width, channels, data) + +The 8-bit sample fill function for combining solid fills. + +=cut +*/ +static void +fill_solid_comb(i_fill_t *fill, int x, int y, int width, int channels, + i_color *data) { + i_color c = T_SOLID_FILL(fill)->c; + + while (width-- > 0) { + *data++ = c; + } +} + +/* +=item fill_solidf_comb(fill, x, y, width, channels, data) + +The floating sample fill function for combining solid fills. + +=cut +*/ +static void +fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels, + i_fcolor *data) { + i_fcolor c = T_SOLID_FILL(fill)->fc; + + while (width-- > 0) { + *data++ = c; + } +} + +/* +=item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy) +Implements creation of hatch fill objects. + +=cut +*/ static i_fill_t * -i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg, - int combine, int hatch, unsigned char *cust_hatch, +i_new_hatch_low(const i_color *fg, const i_color *bg, + const i_fcolor *ffg, const i_fcolor *fbg, + int combine, int hatch, const unsigned char *cust_hatch, int dx, int dy) { - i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); + i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); /* checked 14jul05 tonyc */ fill->base.fill_with_color = fill_hatch; fill->base.fill_with_fcolor = fill_hatchf; fill->base.destroy = NULL; - fill->fg = fg ? *fg : fcolor_to_color(ffg); - fill->bg = bg ? *bg : fcolor_to_color(fbg); - fill->ffg = ffg ? *ffg : color_to_fcolor(fg); - fill->fbg = fbg ? *fbg : color_to_fcolor(bg); - fill->base.combines = - combine && (fill->ffg.channel[0] < 1 || fill->fbg.channel[0] < 1); + /* Some Sun C didn't like the condition expressions that were here. + See https://rt.cpan.org/Ticket/Display.html?id=21944 + */ + if (fg) + fill->fg = *fg; + else + fill->fg = fcolor_to_color(ffg); + if (bg) + fill->bg = *bg; + else + fill->bg = fcolor_to_color(fbg); + if (ffg) + fill->ffg = *ffg; + else + fill->ffg = color_to_fcolor(fg); + if (fbg) + fill->fbg = *fbg; + else + fill->fbg = color_to_fcolor(bg); + if (combine) { + i_get_combine(combine, &fill->base.combine, &fill->base.combinef); + } + else { + fill->base.combine = NULL; + fill->base.combinef = NULL; + } if (cust_hatch) { memcpy(fill->hatch, cust_hatch, 8); } @@ -336,20 +661,13 @@ i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg, return &fill->base; } -i_fill_t * -i_new_fill_hatch(i_color *fg, i_color *bg, int combine, int hatch, - unsigned char *cust_hatch, int dx, int dy) { - return i_new_hatch_low(fg, bg, NULL, NULL, combine, hatch, cust_hatch, - dx, dy); -} +/* +=item fill_hatch(fill, x, y, width, channels, data) -i_fill_t * -i_new_fill_hatchf(i_fcolor *fg, i_fcolor *bg, int combine, int hatch, - unsigned char *cust_hatch, int dx, int dy) { - return i_new_hatch_low(NULL, NULL, fg, bg, combine, hatch, cust_hatch, - dx, dy); -} +The 8-bit sample fill function for hatched fills. +=cut +*/ static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, i_color *data) { i_fill_hatch_t *f = (i_fill_hatch_t *)fill; @@ -358,20 +676,23 @@ static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, int mask = 128 >> xpos; while (width-- > 0) { - i_color c = (byte & mask) ? f->fg : f->bg; - - if (f->base.combines) { - COMBINE(*data, c, channels); - } - else { - *data = c; - } - ++data; + if (byte & mask) + *data++ = f->fg; + else + *data++ = f->bg; + if ((mask >>= 1) == 0) mask = 128; } } +/* +=item fill_hatchf(fill, x, y, width, channels, data) + +The floating sample fill function for hatched fills. + +=back +*/ static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, i_fcolor *data) { i_fill_hatch_t *f = (i_fill_hatch_t *)fill; @@ -380,16 +701,725 @@ static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, int mask = 128 >> xpos; while (width-- > 0) { - i_fcolor c = (byte & mask) ? f->ffg : f->fbg; + if (byte & mask) + *data++ = f->ffg; + else + *data++ = f->fbg; + + if ((mask >>= 1) == 0) + mask = 128; + } +} + +/* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */ +/* linear interpolation */ +static i_color interp_i_color(i_color before, i_color after, double pos, + int channels) { + i_color out; + int ch; - if (f->base.combines) { - COMBINE(*data, c, channels); + pos -= floor(pos); + for (ch = 0; ch < channels; ++ch) + out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch]; + if (channels > 3 && out.channel[3]) + for (ch = 0; ch < channels; ++ch) + if (ch != 3) { + int temp = out.channel[ch] * 255 / out.channel[3]; + if (temp > 255) + temp = 255; + out.channel[ch] = temp; + } + + return out; +} + +/* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */ +/* linear interpolation */ +static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos, + int channels) { + i_fcolor out; + int ch; + + pos -= floor(pos); + for (ch = 0; ch < channels; ++ch) + out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch]; + if (out.channel[3]) + for (ch = 0; ch < channels; ++ch) + if (ch != 3) { + int temp = out.channel[ch] / out.channel[3]; + if (temp > 1.0) + temp = 1.0; + out.channel[ch] = temp; + } + + return out; +} + +/* +=item fill_image(fill, x, y, width, channels, data, work) + +=cut +*/ +static void fill_image(i_fill_t *fill, int x, int y, int width, int channels, + i_color *data) { + struct i_fill_image_t *f = (struct i_fill_image_t *)fill; + int i = 0; + i_color *out = data; + + 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_color c[2][2]; + i_color 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_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]); + i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]); + } + else { + i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, + c[dy]); + } + c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels); + } + *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels); + ++i; } - else { - *data = c; + } + 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_gpix(f->src, rx, ry, out); + ++out; + ++i; } - ++data; - if ((mask >>= 1) == 0) - mask = 128; + } + 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; } } + +static void combine_addf(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) { + double total = out->channel[ch] + in->channel[ch]; + if (total > 1.0) + total = 1.0; + out->channel[ch] = total; + } + } + COMBINEF(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_subtract(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 < 0) + total = 0; + c.channel[ch] = total; + } + } + COMBINE(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_subtractf(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) { + double total = out->channel[ch] - in->channel[ch]; + if (total < 0) + total = 0; + c.channel[ch] = total; + } + } + COMBINEF(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_diff(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) + c.channel[ch] = abs(out->channel[ch] - in->channel[ch]); + } + COMBINE(*out, c, channels) + ++out; + ++in; + } +} + +static void combine_difff(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] = fabs(out->channel[ch] - in->channel[ch]); + } + COMBINEF(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_darken(i_color *out, i_color *in, int channels, int count) { + int ch; + + while (count--) { + for (ch = 0; ch < channels; ++ch) { + if (ch != 3 && out->channel[ch] < in->channel[ch]) + in->channel[ch] = out->channel[ch]; + } + COMBINE(*out, *in, channels); + ++out; + ++in; + } +} + +static void combine_darkenf(i_fcolor *out, i_fcolor *in, int channels, int count) { + int ch; + + while (count--) { + for (ch = 0; ch < channels; ++ch) { + if (ch != 3 && out->channel[ch] < in->channel[ch]) + in->channel[ch] = out->channel[ch]; + } + COMBINEF(*out, *in, channels); + ++out; + ++in; + } +} + +static void combine_lighten(i_color *out, i_color *in, int channels, int count) { + int ch; + + while (count--) { + for (ch = 0; ch < channels; ++ch) { + if (ch != 3 && out->channel[ch] > in->channel[ch]) + in->channel[ch] = out->channel[ch]; + } + COMBINE(*out, *in, channels); + ++out; + ++in; + } +} + +static void combine_lightenf(i_fcolor *out, i_fcolor *in, int channels, int count) { + int ch; + + while (count--) { + for (ch = 0; ch < channels; ++ch) { + if (ch != 3 && out->channel[ch] > in->channel[ch]) + in->channel[ch] = out->channel[ch]; + } + COMBINEF(*out, *in, channels); + ++out; + ++in; + } +} + +static void combine_hue(i_color *out, i_color *in, int channels, int count) { + while (count--) { + i_color c = *out; + i_rgb_to_hsv(&c); + i_rgb_to_hsv(in); + c.channel[0] = in->channel[0]; + i_hsv_to_rgb(&c); + c.channel[3] = in->channel[3]; + COMBINE(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_huef(i_fcolor *out, i_fcolor *in, int channels, int count) { + while (count--) { + i_fcolor c = *out; + i_rgb_to_hsvf(&c); + i_rgb_to_hsvf(in); + c.channel[0] = in->channel[0]; + i_hsv_to_rgbf(&c); + c.channel[3] = in->channel[3]; + COMBINEF(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_sat(i_color *out, i_color *in, int channels, int count) { + while (count--) { + i_color c = *out; + i_rgb_to_hsv(&c); + i_rgb_to_hsv(in); + c.channel[1] = in->channel[1]; + i_hsv_to_rgb(&c); + c.channel[3] = in->channel[3]; + COMBINE(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_satf(i_fcolor *out, i_fcolor *in, int channels, int count) { + while (count--) { + i_fcolor c = *out; + i_rgb_to_hsvf(&c); + i_rgb_to_hsvf(in); + c.channel[1] = in->channel[1]; + i_hsv_to_rgbf(&c); + c.channel[3] = in->channel[3]; + COMBINEF(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_value(i_color *out, i_color *in, int channels, int count) { + while (count--) { + i_color c = *out; + i_rgb_to_hsv(&c); + i_rgb_to_hsv(in); + c.channel[2] = in->channel[2]; + i_hsv_to_rgb(&c); + c.channel[3] = in->channel[3]; + COMBINE(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_valuef(i_fcolor *out, i_fcolor *in, int channels, + int count) { + while (count--) { + i_fcolor c = *out; + i_rgb_to_hsvf(&c); + i_rgb_to_hsvf(in); + c.channel[2] = in->channel[2]; + i_hsv_to_rgbf(&c); + c.channel[3] = in->channel[3]; + COMBINEF(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_color(i_color *out, i_color *in, int channels, int count) { + while (count--) { + i_color c = *out; + i_rgb_to_hsv(&c); + i_rgb_to_hsv(in); + c.channel[0] = in->channel[0]; + c.channel[1] = in->channel[1]; + i_hsv_to_rgb(&c); + c.channel[3] = in->channel[3]; + COMBINE(*out, c, channels); + ++out; + ++in; + } +} + +static void combine_colorf(i_fcolor *out, i_fcolor *in, int channels, + int count) { + while (count--) { + i_fcolor c = *out; + i_rgb_to_hsvf(&c); + i_rgb_to_hsvf(in); + c.channel[0] = in->channel[0]; + c.channel[1] = in->channel[1]; + i_hsv_to_rgbf(&c); + c.channel[3] = in->channel[3]; + COMBINEF(*out, c, channels); + ++out; + ++in; + } +} + + +/* +=back + +=head1 AUTHOR + +Tony Cook + +=head1 SEE ALSO + +Imager(3) + +=cut +*/