random => 2,
circle => 3,
},
+ combine => {
+ none => 0,
+ normal => 1,
+ multiply => 2, mult => 2,
+ dissolve => 3,
+ add => 4,
+ subtract => 5, sub => 5,
+ diff => 6,
+ lighten => 7,
+ darken => 8,
+ hue => 9,
+ sat => 10,
+ value => 11,
+ color => 12,
+ },
},
defaults => { ftype => 0, repeat => 0, combine => 0,
super_sample => 0, ssample_param => 4,
bumpmap bump elevation(0) lightx lighty st(2)
contrast intensity
conv coef
- fountain xa ya xb yb ftype(linear) repeat(none) combine(0)
+ fountain xa ya xb yb ftype(linear) repeat(none) combine(none)
super_sample(none) ssample_param(4) segments(see below)
gaussian stddev
gradgen xo yo colors dist
By default the fill simply overwrites the whole image (unless you have
parts of the range 0 through 1 that aren't covered by a segment), if
any segments of your fill have any transparency, you can set the
-I<combine> option to 1 to have the fill combined with the existing pixels.
+I<combine> option to 'normal' to have the fill combined with the
+existing pixels. See the description of I<combine> in L<Imager/Fill>.
If your fill has sharp edges, for example between steps if you use
repeat set to 'triangle', you may see some aliased or ragged edges.
PUSHs(sv_2mortal(newSVnv(cl->rgba.b)));
PUSHs(sv_2mortal(newSVnv(cl->rgba.a)));
-
+Imager::Color
+i_hsv_to_rgb(c)
+ Imager::Color c
+ CODE:
+ RETVAL = mymalloc(sizeof(i_color));
+ *RETVAL = *c;
+ i_hsv_to_rgb(RETVAL);
+ OUTPUT:
+ RETVAL
+
+Imager::Color
+i_rgb_to_hsv(c)
+ Imager::Color c
+ CODE:
+ RETVAL = mymalloc(sizeof(i_color));
+ *RETVAL = *c;
+ i_rgb_to_hsv(RETVAL);
+ OUTPUT:
+ RETVAL
+
MODULE = Imager PACKAGE = Imager::Color::Float PREFIX=ICLF_
EXTEND(SP, 1);
PUSHs(ST(0));
+Imager::Color::Float
+i_hsv_to_rgb(c)
+ Imager::Color::Float c
+ CODE:
+ RETVAL = mymalloc(sizeof(i_fcolor));
+ *RETVAL = *c;
+ i_hsv_to_rgbf(RETVAL);
+ OUTPUT:
+ RETVAL
+
+Imager::Color::Float
+i_rgb_to_hsv(c)
+ Imager::Color::Float c
+ CODE:
+ RETVAL = mymalloc(sizeof(i_fcolor));
+ *RETVAL = *c;
+ i_rgb_to_hsvf(RETVAL);
+ OUTPUT:
+ RETVAL
+
+
MODULE = Imager PACKAGE = Imager::ImgRaw PREFIX = IIM_
Imager::ImgRaw
OUTPUT:
RETVAL
+Imager::FillHandle
+i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy)
+ Imager::Color::Float fg
+ Imager::Color::Float bg
+ int combine
+ int hatch
+ int dx
+ int dy
+ PREINIT:
+ unsigned char *cust_hatch;
+ STRLEN len;
+ CODE:
+ if (SvOK(ST(4))) {
+ cust_hatch = SvPV(ST(4), len);
+ }
+ else
+ cust_hatch = NULL;
+ RETVAL = i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy);
+ OUTPUT:
+ RETVAL
+
MANIFEST
README
Makefile.PL
-bmp.c
+bmp.c Reading and writing Windows BMP files
color.c Color translation and handling
conv.c
convert.c
lib/Imager/Color/Float.pm
lib/Imager/Expr.pm
lib/Imager/Expr/Assem.pm
+lib/Imager/Fill.pm
lib/Imager/Font.pm
lib/Imager/Font/Type1.pm
lib/Imager/Font/Truetype.pm
t/t106tiff.t
t/t107bmp.t
t/t15color.t
+t/t20fill.t Tests fills
t/t30t1font.t
t/t35ttfont.t
t/t36oofont.t
t/t61filters.t
t/t65crop.t
t/t66paste.t
+t/t67convert.t
+t/t68map.t
t/t69rubthru.t
t/t70newgif.t
t/t75polyaa.t
case 16:
im = read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression);
break;
+
+ default:
+ i_push_errorf(0, "unknown bit count for BMP file (%d)", bit_count);
+ return NULL;
}
/* store the resolution */
#include "image.h"
+#include <math.h>
/*
=head1 NAME
#define EPSILON (1e-8)
+#define my_max(a, b) ((a) < (b) ? (b) : (a))
+#define my_min(a, b) ((a) > (b) ? (b) : (a))
+
/*
=item i_rgb2hsvf(&color)
double temp;
double Cr, Cg, Cb;
- v = max(max(color->rgb.r, color->rgb.g), color->rgb.b);
- temp = min(min(color->rgb.r, color->rgb.g), color->rgb.b);
+ v = my_max(my_max(color->rgb.r, color->rgb.g), color->rgb.b);
+ temp = my_min(my_min(color->rgb.r, color->rgb.g), color->rgb.b);
if (v < EPSILON)
s = 0;
else
color->channel[2] = v;
}
+/*
+=item i_rgb2hsv(&color)
+
+Converts the first 3 channels of color into hue, saturation and value.
+
+Each value is scaled into the range 0 to 255.
+
+=cut
+*/
+void i_rgb_to_hsv(i_color *color) {
+ double h, s, v;
+ double temp;
+ double Cr, Cg, Cb;
+
+ v = my_max(my_max(color->rgb.r, color->rgb.g), color->rgb.b);
+ temp = my_min(my_min(color->rgb.r, color->rgb.g), color->rgb.b);
+ if (v == 0)
+ s = 0;
+ else
+ s = (v-temp)*255/v;
+ if (s == 0)
+ h = 0;
+ else {
+ Cr = (v - color->rgb.r)/(v-temp);
+ Cg = (v - color->rgb.g)/(v-temp);
+ Cb = (v - color->rgb.b)/(v-temp);
+ if (color->rgb.r == v)
+ h = Cb - Cg;
+ else if (color->rgb.g == v)
+ h = 2 + Cr - Cb;
+ else if (color->rgb.b == v)
+ h = 4 + Cg - Cr;
+ h = h * 60.0;
+ if (h < 0)
+ h += 360;
+ }
+ color->channel[0] = h * 255 / 360.0;
+ color->channel[1] = s;
+ color->channel[2] = v;
+}
+
/*
=item i_hsv_to_rgbf(&color)
int i;
double f, m, n, k;
h = fmod(h, 1.0) * 6;
- i = h;
+ i = floor(h);
f = h - i;
m = v * (1 - s);
n = v * (1 - s * f);
}
}
+/*
+=item i_hsv_to_rgb(&color)
+
+Convert a HSV value to an RGB value, each value ranges from 0 to 1.
+
+=cut
+*/
+
+void i_hsv_to_rgb(i_color *color) {
+ double h = color->channel[0];
+ double s = color->channel[1];
+ double v = color->channel[2];
+
+ if (color->channel[1] == 0) {
+ /* ignore h in this case */
+ color->rgb.r = color->rgb.g = color->rgb.b = v;
+ }
+ else {
+ int i;
+ double f, m, n, k;
+ h = h / 255.0 * 6;
+ i = h;
+ f = h - i;
+ m = v * (255 - s) / 255;
+ n = v * (255 - s * f) / 255;
+ k = v * (255 - s * (1 - f)) / 255;
+ switch (i) {
+ case 0:
+ color->rgb.r = v; color->rgb.g = k; color->rgb.b = m;
+ break;
+ case 1:
+ color->rgb.r = n; color->rgb.g = v; color->rgb.b = m;
+ break;
+ case 2:
+ color->rgb.r = m; color->rgb.g = v; color->rgb.b = k;
+ break;
+ case 3:
+ color->rgb.r = m; color->rgb.g = n; color->rgb.b = v;
+ break;
+ case 4:
+ color->rgb.r = k; color->rgb.g = m; color->rgb.b = v;
+ break;
+ case 5:
+ color->rgb.r = v; color->rgb.g = m; color->rgb.b = n;
+ break;
+ }
+ }
+}
+
/*
=back
int x, w, y;
if (im->bits == i_8_bits && fill->fill_with_color) {
i_color *line = mymalloc(sizeof(i_color) * im->xsize);
+ i_color *work = NULL;
+ if (fill->combine)
+ work = mymalloc(sizeof(i_color) * im->xsize);
for(y=0;y<ar->lines;y++) {
if (ar->data[y].max!=-1) {
x = ar->data[y].min;
w = ar->data[y].max-ar->data[y].min;
- if (fill->combines)
+ if (fill->combine)
i_glin(im, x, x+w, y, line);
- (fill->fill_with_color)(fill, x, y, w, im->channels, line);
+ (fill->fill_with_color)(fill, x, y, w, im->channels, line, work);
i_plin(im, x, x+w, y, line);
}
}
myfree(line);
+ if (work)
+ myfree(work);
}
else {
i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize);
+ i_fcolor *work = NULL;
+ if (fill->combinef)
+ work = mymalloc(sizeof(i_fcolor) * im->xsize);
for(y=0;y<ar->lines;y++) {
if (ar->data[y].max!=-1) {
x = ar->data[y].min;
w = ar->data[y].max-ar->data[y].min;
- if (fill->combines)
+ if (fill->combinef)
i_glinf(im, x, x+w, y, line);
- (fill->fill_with_fcolor)(fill, x, y, w, im->channels, line);
+ (fill->fill_with_fcolor)(fill, x, y, w, im->channels, line, work);
i_plinf(im, x, x+w, y, line);
}
}
myfree(line);
+ if (work)
+ myfree(work);
}
}
++x2;
if (im->bits == i_8_bits && fill->fill_with_color) {
i_color *line = mymalloc(sizeof(i_color) * (x2 - x1));
+ i_color *work = NULL;
+ if (fill->combine)
+ work = mymalloc(sizeof(i_color) * (x2-x1));
while (y1 <= y2) {
- if (fill->combines)
+ if (fill->combine)
i_glin(im, x1, x2, y1, line);
- (fill->fill_with_color)(fill, x1, y1, x2-x1, im->channels, line);
+ (fill->fill_with_color)(fill, x1, y1, x2-x1, im->channels, line, work);
i_plin(im, x1, x2, y1, line);
++y1;
}
myfree(line);
+ if (work)
+ myfree(work);
}
else {
i_fcolor *line = mymalloc(sizeof(i_fcolor) * (x2 - x1));
+ i_fcolor *work;
+ work = mymalloc(sizeof(i_fcolor) * (x2 - x1));
+
while (y1 <= y2) {
- if (fill->combines)
+ if (fill->combinef)
i_glinf(im, x1, x2, y1, line);
- (fill->fill_with_fcolor)(fill, x1, y1, x2-x1, im->channels, line);
+ (fill->fill_with_fcolor)(fill, x1, y1, x2-x1, im->channels, line, work);
i_plinf(im, x1, x2, y1, line);
++y1;
}
myfree(line);
+ if (work)
+ myfree(work);
}
}
if (im->bits == i_8_bits && fill->fill_with_color) {
i_color *line = mymalloc(sizeof(i_color) * (bxmax - bxmin));
+ i_color *work = NULL;
+ if (fill->combine)
+ work = mymalloc(sizeof(i_color) * (bxmax - bxmin));
for(y=bymin;y<=bymax;y++) {
x = bxmin;
while (x < bxmax && btm_test(btm, x, y)) {
++x;
}
- if (fill->combines)
+ if (fill->combine)
i_glin(im, start, x, y, line);
- (fill->fill_with_color)(fill, start, y, x-start, im->channels, line);
+ (fill->fill_with_color)(fill, start, y, x-start, im->channels,
+ line, work);
i_plin(im, start, x, y, line);
}
}
}
myfree(line);
+ if (work)
+ myfree(work);
}
else {
i_fcolor *line = mymalloc(sizeof(i_fcolor) * (bxmax - bxmin));
+ i_fcolor *work = NULL;
+ if (fill->combinef)
+ work = mymalloc(sizeof(i_fcolor) * (bxmax - bxmin));
for(y=bymin;y<=bymax;y++) {
x = bxmin;
while (x < bxmax && btm_test(btm, x, y)) {
++x;
}
- if (fill->combines)
+ if (fill->combinef)
i_glinf(im, start, x, y, line);
- (fill->fill_with_fcolor)(fill, start, y, x-start, im->channels, line);
+ (fill->fill_with_fcolor)(fill, start, y, x-start, im->channels,
+ line, work);
i_plinf(im, start, x, y, line);
}
}
}
myfree(line);
+ if (work)
+ myfree(work);
}
btm_destroy(btm);
Fountain fill is implemented by L<filters.c>.
+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
out.channel[ch] = Sample8ToF(c->channel[ch]);
}
-typedef struct
-{
- i_fill_t base;
- i_color c;
- i_fcolor fc;
-} i_fill_solid_t;
-
+/* alpha combine in with out */
#define COMBINE(out, in, channels) \
{ \
int ch; \
} \
}
+/* 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; \
} \
}
+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 *);
+ i_color *, i_color *);
static void fill_solidf(i_fill_t *, int x, int y, int width, int channels,
- i_fcolor *);
+ i_fcolor *, i_fcolor *);
static void fill_solid_comb(i_fill_t *, int x, int y, int width, int channels,
- i_color *);
+ i_color *, i_color *);
static void fill_solidf_comb(i_fill_t *, int x, int y, int width,
- int channels, i_fcolor *);
+ int channels, i_fcolor *, i_fcolor *);
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 =
fill_solid_comb,
fill_solidf_comb,
NULL,
- 1
+ NULL,
+ NULL,
},
};
int ch;
i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t));
- if (combine && c->channel[3] < 1.0)
+ if (combine && c->channel[3] < 1.0) {
*fill = base_solid_fill_comb;
+ i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
+ }
else
*fill = base_solid_fill;
fill->fc = *c;
int ch;
i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t));
- if (combine && c->channel[3] < 255)
+ if (combine && c->channel[3] < 255) {
*fill = base_solid_fill_comb;
+ i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
+ }
else
*fill = base_solid_fill;
fill->c = *c;
} i_fill_hatch_t;
static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels,
- i_color *data);
+ i_color *data, i_color *work);
static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data);
+ i_fcolor *data, i_fcolor *work);
static
i_fill_t *
i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg,
*/
static void
fill_solid(i_fill_t *fill, int x, int y, int width, int channels,
- i_color *data) {
+ i_color *data, i_color *work) {
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) {
+ i_fcolor *data, i_fcolor *work) {
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 *data, i_color *work) {
i_color c = T_SOLID_FILL(fill)->c;
+ int count = width;
+ i_color *wstart = work;
while (width-- > 0) {
- COMBINE(*data, c, channels);
- ++data;
+ *work++ = c;
}
+ (fill->combine)(data, wstart, channels, count);
}
/*
*/
static void
fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data) {
+ i_fcolor *data, i_fcolor *work) {
i_fcolor c = T_SOLID_FILL(fill)->fc;
+ int count = width;
+ i_fcolor *wstart = work;
while (width-- > 0) {
- COMBINEF(*data, c, channels);
- ++data;
+ *work++ = c;
}
+ (fill->combinef)(data, wstart, channels, count);
}
/*
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);
+ if (combine && (fill->ffg.channel[0] < 1 || fill->fbg.channel[0] < 1)) {
+ 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);
}
=back
*/
static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels,
- i_color *data) {
+ i_color *data, i_color *work) {
i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
int byte = f->hatch[(y + f->dy) & 7];
int xpos = (x + f->dx) & 7;
int mask = 128 >> xpos;
- while (width-- > 0) {
- i_color c = (byte & mask) ? f->fg : f->bg;
+ if (fill->combine) {
+ int count = width;
+ i_color *wstart = work;
- if (f->base.combines) {
- COMBINE(*data, c, channels);
+ while (count-- > 0) {
+ *work++ = (byte & mask) ? f->fg : f->bg;
+
+ if ((mask >>= 1) == 0)
+ mask = 128;
}
- else {
- *data = c;
+ (fill->combine)(data, wstart, channels, width);
+ }
+ else {
+ while (width-- > 0) {
+ *data++ = (byte & mask) ? f->fg : f->bg;
+
+ if ((mask >>= 1) == 0)
+ mask = 128;
}
- ++data;
- if ((mask >>= 1) == 0)
- mask = 128;
}
}
=back
*/
static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data) {
+ i_fcolor *data, i_fcolor *work) {
i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
int byte = f->hatch[(y + f->dy) & 7];
int xpos = (x + f->dx) & 7;
int mask = 128 >> xpos;
- while (width-- > 0) {
- i_fcolor c = (byte & mask) ? f->ffg : f->fbg;
+ if (fill->combinef) {
+ int count = width;
+ i_fcolor *wstart = work;
+
+ while (count-- > 0) {
+ *work++ = (byte & mask) ? f->ffg : f->fbg;
+
+ if ((mask >>= 1) == 0)
+ mask = 128;
+ }
+ (fill->combinef)(data, wstart, channels, width);
+ }
+ else {
+ while (width-- > 0) {
+ *data++ = (byte & mask) ? f->ffg : f->fbg;
- if (f->base.combines) {
- COMBINE(*data, c, channels);
+ if ((mask >>= 1) == 0)
+ mask = 128;
}
- else {
- *data = c;
+ }
+}
+
+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);
+
+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--) {
+ i_color c = *in;
+ 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) {
+ int ch;
+
+ 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) {
+ int ch;
+
+ 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]);
}
- ++data;
- if ((mask >>= 1) == 0)
- mask = 128;
+ 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);
+ 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
struct fount_state state;
int x, y;
i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize);
+ i_fcolor *work = NULL;
int ch;
i_fountain_seg *my_segs;
+ i_fill_combine_f combine_func = NULL;
+ i_fill_combinef_f combinef_func = NULL;
+
+ i_get_combine(combine, &combine_func, &combinef_func);
+ if (combinef_func)
+ work = mymalloc(sizeof(i_fcolor) * im->xsize);
fount_init_state(&state, xa, ya, xb, yb, type, repeat, combine,
super_sample, ssample_param, count, segs);
else
got_one = state.ssfunc(&c, x, y, &state);
if (got_one) {
- if (combine) {
- for (ch = 0; ch < im->channels; ++ch) {
- line[x].channel[ch] = line[x].channel[ch] * (1.0 - c.channel[3])
- + c.channel[ch] * c.channel[3];
- }
- }
+ if (combine)
+ work[x] = c;
else
line[x] = c;
}
}
+ if (combine)
+ combinef_func(line, work, im->channels, im->xsize);
i_plinf(im, 0, im->xsize, y, line);
}
fount_finish_state(&state);
static void
fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data);
+ i_fcolor *data, i_fcolor *work);
static void
fount_fill_destroy(i_fill_t *fill);
fill->base.fill_with_color = NULL;
fill->base.fill_with_fcolor = fill_fountf;
fill->base.destroy = fount_fill_destroy;
- fill->base.combines = combine;
+ if (combine)
+ i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
+ else {
+ fill->base.combine = NULL;
+ fill->base.combinef = NULL;
+ }
fount_init_state(&fill->state, xa, ya, xb, yb, type, repeat, combine,
super_sample, ssample_param, count, segs);
*/
static void
fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data) {
+ i_fcolor *data, i_fcolor *work) {
i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
- int ch;
+
+ if (fill->combinef) {
+ i_fcolor *wstart = work;
+ int count = width;
- while (width--) {
- i_fcolor c;
- int got_one;
- double v;
- if (f->state.ssfunc)
- got_one = f->state.ssfunc(&c, x, y, &f->state);
- else
- got_one = fount_getat(&c, x, y, &f->state);
-
- if (got_one) {
- if (f->base.combines) {
- for (ch = 0; ch < channels; ++ch) {
- data->channel[ch] = data->channel[ch] * (1.0 - c.channel[3])
- + c.channel[ch] * c.channel[3];
- }
- }
- else
- *data = c;
+ while (width--) {
+ i_fcolor c;
+ int got_one;
+ double v;
+ if (f->state.ssfunc)
+ got_one = f->state.ssfunc(&c, x, y, &f->state);
+ else
+ got_one = fount_getat(&c, x, y, &f->state);
+
+ *work++ = c;
+
+ ++x;
+ }
+ (fill->combinef)(data, wstart, channels, count);
+ }
+ else {
+ while (width--) {
+ i_fcolor c;
+ int got_one;
+ double v;
+ if (f->state.ssfunc)
+ got_one = f->state.ssfunc(&c, x, y, &f->state);
+ else
+ got_one = fount_getat(&c, x, y, &f->state);
+
+ *data++ = c;
+
+ ++x;
}
-
- ++x;
- ++data;
}
}
extern void i_rgb_to_hsvf(i_fcolor *color);
extern void i_hsv_to_rgbf(i_fcolor *color);
+extern void i_rgb_to_hsv(i_color *color);
+extern void i_hsv_to_rgb(i_color *color);
i_img *IIM_new(int x,int y,int ch);
void IIM_DESTROY(i_img *im);
typedef void (*i_fill_with_color_f)
(struct i_fill_tag *fill, int x, int y, int width, int channels,
- i_color *data);
+ i_color *data, i_color *work);
typedef void (*i_fill_with_fcolor_f)
(struct i_fill_tag *fill, int x, int y, int width, int channels,
- i_fcolor *data);
+ i_fcolor *data, i_fcolor *work);
typedef void (*i_fill_destroy_f)(struct i_fill_tag *fill);
+typedef void (*i_fill_combine_f)(i_color *out, i_color *in, int channels,
+ int count);
+typedef void (*i_fill_combinef_f)(i_fcolor *out, i_fcolor *in, int channels,
+ int count);
+
typedef struct i_fill_tag
{
/* if non-zero the caller will fill data with the original data
from the image */
- int combines;
+ i_fill_combine_f combine;
+ i_fill_combinef_f combinef;
} i_fill_t;
+typedef enum {
+ ic_none,
+ ic_normal,
+ ic_multiply,
+ ic_dissolve,
+ ic_add,
+ ic_subtract,
+ ic_diff,
+ ic_lighten,
+ ic_darken,
+ ic_hue,
+ ic_sat,
+ ic_value,
+ ic_color
+} i_combine_t;
+
extern i_fill_t *i_new_fill_solidf(i_fcolor *c, int combine);
extern i_fill_t *i_new_fill_solid(i_color *c, int combine);
extern i_fill_t *
#define Sample16To8(num) ((num) / 257)
#define Sample8To16(num) ((num) * 257)
+extern void i_get_combine(int combine, i_fill_combine_f *, i_fill_combinef_f *);
+
#endif
package Imager::Fill;
-
+use strict;
# this needs to be kept in sync with the array of hatches in fills.c
my @hatch_types =
qw/check1x1 check2x2 check4x4 vline1 vline2 vline4
my %hatch_types;
@hatch_types{@hatch_types} = 0..$#hatch_types;
+my @combine_types =
+ qw/none normal multiply dissolve add subtract diff lighten darken
+ hue saturation value color/;
+my %combine_types;
+@combine_types{@combine_types} = 0 .. $#combine_types;
+$combine_types{mult} = $combine_types{multiply};
+$combine_types{sub} = $combine_types{subtract};
+$combine_types{sat} = $combine_types{saturation};
+
+# this function tries to DWIM for color parameters
+# color objects are used as is
+# simple scalars are simply treated as single parameters to Imager::Color->new
+# hashrefs are treated as named argument lists to Imager::Color->new
+# arrayrefs are treated as list arguments to Imager::Color->new iff any
+# parameter is > 1
+# other arrayrefs are treated as list arguments to Imager::Color::Float
+
+sub _color {
+ my $arg = shift;
+ my $result;
+
+ if (ref $arg) {
+ if (UNIVERSAL::isa($arg, "Imager::Color")
+ || UNIVERSAL::isa($arg, "Imager::Color::Float")) {
+ $result = $arg;
+ }
+ else {
+ if ($arg =~ /^HASH\(/) {
+ $result = Imager::Color->new(%$arg);
+ }
+ elsif ($arg =~ /^ARRAY\(/) {
+ if (grep $_ > 1, @$arg) {
+ $result = Imager::Color->new(@$arg);
+ }
+ else {
+ $result = Imager::Color::Float->new(@$arg);
+ }
+ }
+ else {
+ $Imager::ERRSTR = "Not a color";
+ }
+ }
+ }
+ else {
+ # assume Imager::Color::new knows how to handle it
+ $result = Imager::Color->new($arg);
+ }
+
+ return $result;
+}
+
sub new {
my ($class, %hsh) = @_;
my $self = bless { }, $class;
$hsh{combine} ||= 0;
+ if (exists $combine_types{$hsh{combine}}) {
+ $hsh{combine} = $combine_types{$hsh{combine}};
+ }
if ($hsh{solid}) {
- if (UNIVERSAL::isa($hsh{solid}, 'Imager::Color')) {
- $self->{fill} = Imager::i_new_fill_solid($hsh{solid}, $hsh{combine});
+ my $solid = _color($hsh{solid});
+ if (UNIVERSAL::isa($solid, 'Imager::Color')) {
+ $self->{fill} =
+ Imager::i_new_fill_solid($solid, $hsh{combine});
}
- elsif (UNIVERSAL::isa($hsh{colid}, 'Imager::Color::Float')) {
- $self->{fill} = Imager::i_new_fill_solidf($hsh{solid}, $hsh{combine});
+ elsif (UNIVERSAL::isa($solid, 'Imager::Color::Float')) {
+ $self->{fill} =
+ Imager::i_new_fill_solidf($solid, $hsh{combine});
}
else {
$Imager::ERRSTR = "solid isn't a color";
}
$hsh{hatch} = $hatch_types{$hsh{hatch}};
}
- if (UNIVERSAL::isa($hsh{fg}, 'Imager::Color')) {
- $hsh{bg} ||= Imager::Color->new(255, 255, 255);
+ my $fg = _color($hsh{fg});
+ if (UNIVERSAL::isa($fg, 'Imager::Color')) {
+ my $bg = _color($hsh{bg} || Imager::Color->new(255, 255, 255));
$self->{fill} =
- Imager::i_new_fill_hatch($hsh{fg}, $hsh{bg}, $hsh{combine},
+ Imager::i_new_fill_hatch($fg, $bg, $hsh{combine},
$hsh{hatch}, $hsh{cust_hatch},
$hsh{dx}, $hsh{dy});
}
- elsif (UNIVERSAL::isa($hsh{bg}, 'Imager::Color::Float')) {
- $hsh{bg} ||= Imager::Color::Float->new(1, 1, 1);
+ elsif (UNIVERSAL::isa($fg, 'Imager::Color::Float')) {
+ my $bg = _color($hsh{bg} || Imager::Color::Float->new(1, 1, 1));
$self->{fill} =
- Imager::i_new_fill_hatchf($hsh{fg}, $hsh{bg}, $hsh{combine},
+ Imager::i_new_fill_hatchf($fg, $bg, $hsh{combine},
$hsh{hatch}, $hsh{cust_hatch},
$hsh{dx}, $hsh{dy});
}
return @hatch_types;
}
+sub combines {
+ return @combine_types;
+}
+
1;
=head1 NAME
=item combine
-If this is non-zero the fill combines the given colors or samples (if
-the fill is an image) with the underlying image.
+The way in which the fill data is combined with the underlying image,
+possible values include:
+
+=over
+
+=item none
+
+The fill pixel replaces the target pixel.
+
+=item normal
+
+The fill pixels alpha value is used to combine it with the target pixel.
+
+=item multiply
+
+=item mult
+
+Each channel of fill and target is multiplied, and the result is
+combined using the alpha channel of the fill pixel.
+
+=item dissolve
+
+If the alpha of the fill pixel is greater than a random number, the
+fill pixel is alpha combined with the target pixel.
-This this is missing or zero then the target image pixels are simply
-overwritten.
+=item add
+
+The channels of the fill and target are added together, clamped to the range of the samples and alpha combined with the target.
+
+=item subtract
+
+The channels of the fill are subtracted from the target, clamped to be
+>= 0, and alpha combined with the target.
+
+=item diff
+
+The channels of the fill are subtracted from the target and the
+absolute value taken this is alpha combined with the target.
+
+=item lighten
+
+The higher value is taken from each channel of the fill and target pixels, which is then alpha combined with the target.
+
+=item darken
+
+The higher value is taken from each channel of the fill and target pixels, which is then alpha combined with the target.
+
+=item hue
+
+The combination of the saturation and value of the target is combined
+with the hue of the fill pixel, and is then alpha combined with the
+target.
+
+=item sat
+
+The combination of the hue and value of the target is combined
+with the saturation of the fill pixel, and is then alpha combined with the
+target.
+
+=item value
+
+The combination of the hue and value of the target is combined
+with the value of the fill pixel, and is then alpha combined with the
+target.
+
+=item color
+
+The combination of the value of the target is combined with the hue
+and saturation of the fill pixel, and is then alpha combined with the
+target.
+
+=back
=back
you are drawing, and the fountain parameter supplies the fill type,
and is required.
+=head1 OTHER METHODS
+
+=over
+
+=item Imager::Fill->hatches
+
+A list of all defined hatch names.
+
+=item Imager::Fill->combines
+
+A list of all combine types.
+
+=back
+
=head1 FUTURE PLANS
I'm planning on adding the following types of fills:
# Change 1..1 below to 1..last_test_to_print .
# (It may become useful if the test is moved to ./t subdirectory.)
-BEGIN { $| = 1; print "1..22\n"; }
+BEGIN { $| = 1; print "1..42\n"; }
END {print "not ok 1\n" unless $loaded;}
use Imager;
$loaded = 1;
Imager::Color->new(channels=>[ 200, 201, 203, 204 ]));
color_ok(22, 255, 250, 250, 255,
Imager::Color->new(name=>'snow', palette=>'testimg/test_gimp_pal'));
+
+# test the internal HSV <=> RGB conversions
+# these values were generated using the GIMP
+# all but hue is 0..360, saturation and value from 0 to 1
+# rgb from 0 to 255
+my @hsv_vs_rgb =
+ (
+ { hsv => [ 0, 0.2, 0.1 ], rgb=> [ 25, 20, 20 ] },
+ { hsv => [ 0, 0.5, 1.0 ], rgb => [ 255, 127, 127 ] },
+ { hsv => [ 100, 0.5, 1.0 ], rgb => [ 170, 255, 127 ] },
+ { hsv => [ 100, 1.0, 1.0 ], rgb=> [ 85, 255, 0 ] },
+ { hsv => [ 335, 0.5, 0.5 ], rgb=> [127, 63, 90 ] },
+ );
+
+use Imager::Color::Float;
+my $test_num = 23;
+my $index = 0;
+for my $entry (@hsv_vs_rgb) {
+ print "# color index $index\n";
+ my $hsv = $entry->{hsv};
+ my $rgb = $entry->{rgb};
+ my $fhsvo = Imager::Color::Float->new($hsv->[0]/360.0, $hsv->[1], $hsv->[2]);
+ my $fc = Imager::Color::Float::i_hsv_to_rgb($fhsvo);
+ fcolor_close_enough($test_num++, $rgb->[0]/255, $rgb->[1]/255,
+ $rgb->[2]/255, $fc);
+ my $fc2 = Imager::Color::Float::i_rgb_to_hsv($fc);
+ fcolor_close_enough($test_num++, $hsv->[0]/360.0, $hsv->[1], $hsv->[2],
+ $fc2);
+
+ my $hsvo = Imager::Color->new($hsv->[0]*255/360.0, $hsv->[1] * 255,
+ $hsv->[2] * 255);
+ my $c = Imager::Color::i_hsv_to_rgb($hsvo);
+ color_close_enough($test_num++, @$rgb, $c);
+ my $c2 = Imager::Color::i_rgb_to_hsv($c);
+ color_close_enough_hsv($test_num++, $hsv->[0]*255/360.0, $hsv->[1] * 255,
+ $hsv->[2] * 255, $c2);
+ ++$index;
+}
+
+
+
sub test_col {
my ($c, $r, $g, $b, $a) = @_;
unless ($c) {
return $r == $cr && $g == $cg && $b == $cb && $a == $ca;
}
+sub color_close_enough {
+ my ($test_num, $r, $g, $b, $c) = @_;
+
+ my ($cr, $cg, $cb) = $c->rgba;
+ if (abs($cr-$r) <= 5 && abs($cg-$g) <= 5 && abs($cb-$b) <= 5) {
+ print "ok $test_num\n";
+ }
+ else {
+ print "not ok $test_num # ($cr, $cg, $cb) <=> ($r, $g, $b)\n";
+ }
+}
+
+sub color_close_enough_hsv {
+ my ($test_num, $h, $s, $v, $c) = @_;
+
+ my ($ch, $cs, $cv) = $c->rgba;
+ if ($ch < 5 && $h > 250) {
+ $ch += 255;
+ }
+ elsif ($ch > 250 && $h < 5) {
+ $h += 255;
+ }
+ if (abs($ch-$h) <= 5 && abs($cs-$s) <= 5 && abs($cv-$v) <= 5) {
+ print "ok $test_num\n";
+ }
+ else {
+ print "not ok $test_num # ($ch, $cs, $cv) <=> ($h, $s, $v)\n";
+ }
+}
+
+sub fcolor_close_enough {
+ my ($test_num, $r, $g, $b, $c) = @_;
+
+ my ($cr, $cg, $cb) = $c->rgba;
+ if (abs($cr-$r) <= 0.01 && abs($cg-$g) <= 0.01 && abs($cb-$b) <= 0.01) {
+ print "ok $test_num\n";
+ }
+ else {
+ print "not ok $test_num # ($cr, $cg, $cb) <=> ($r, $g, $b)\n";
+ }
+}
+
sub color_ok {
my ($test_num, $r, $g, $b, $a, $c) = @_;
#!perl -w
use strict;
+print "1..34\n";
+
use Imager ':handy';
use Imager::Fill;
use Imager::Color::Float;
-Imager::init_log("testout/t20fill.log", 1);
+sub ok ($$$);
-print "1..21\n";
+Imager::init_log("testout/t20fill.log", 1);
my $blue = NC(0,0,255);
my $red = NC(255, 0, 0);
my $im1 = Imager->new(xsize=>100, ysize=>100);
my $im2 = Imager->new(xsize=>100, ysize=>100);
-my $solid = Imager::Fill->new(solid=>$red);
+my $solid = Imager::Fill->new(solid=>'#FF0000');
ok(13, $solid, "creating oo solid fill");
ok(14, $solid->{fill}, "bad oo solid fill");
$im1->box(fill=>$solid);
$im->arc(r=>80, d1=>45, d2=>75,
fill=>{ hatch=>'stipple2',
combine=>1,
- fg=>NC(0, 0, 0, 255),
- bg=>NC(255,255,255,192) });
+ fg=>[ 0, 0, 0, 255 ],
+ bg=>{ rgba=>[255,255,255,160] } });
$im->arc(r=>80, d1=>75, d2=>135,
fill=>{ fountain=>'radial', xa=>100, ya=>100, xb=>20, yb=>100 });
$im->write(file=>'testout/t20_sample.ppm');
# });
$ffim->write(file=>'testout/t20_ooflood.ppm');
-sub ok {
+# test combining modes
+my $fill = NC(192, 128, 128, 128);
+my $target = NC(64, 32, 64);
+my %comb_tests =
+ (
+ none=>{ result=>$fill },
+ normal=>{ result=>NC(128, 80, 96) },
+ multiply => { result=>NC(56, 24, 48) },
+ dissolve => { result=>[ $target, NC(128, 80, 96) ] },
+ add => { result=>NC(159, 96, 128) },
+ subtract => { result=>NC(31, 15, 31) }, # 31.87, 15.9, 31.87
+ diff => { result=>NC(96, 64, 64) },
+ lighten => { result=>NC(128, 80, 96) },
+ darken => { result=>$target },
+ # the following results are based on the results of the tests and
+ # are suspect for that reason (and were broken at one point <sigh>)
+ # but trying to work them out manually just makes my head hurt - TC
+ hue => { result=>NC(64, 32, 55) },
+ saturation => { result=>NC(63, 37, 64) },
+ value => { result=>NC(127, 64, 128) },
+ color => { result=>NC(64, 37, 52) },
+ );
+
+my $testnum = 22; # from 22 to 34
+for my $comb (Imager::Fill->combines) {
+ my $test = $comb_tests{$comb};
+ my $targim = Imager->new(xsize=>1, ysize=>1);
+ $targim->box(filled=>1, color=>$target);
+ my $fillobj = Imager::Fill->new(solid=>$fill, combine=>$comb);
+ $targim->box(fill=>$fillobj);
+ my $c = Imager::i_get_pixel($targim->{IMG}, 0, 0);
+ if ($test->{result} =~ /ARRAY/) {
+ ok($testnum++, scalar grep(color_close($_, $c), @{$test->{result}}),
+ "combine '$comb'")
+ or print "# got:",join(",", $c->rgba)," allowed: ",
+ join("|", map { join(",", $_->rgba) } @{$test->{result}}),"\n";
+ }
+ else {
+ ok($testnum++, color_close($c, $test->{result}), "combine '$comb'")
+ or print "# got: ",join(",", $c->rgba),
+ " allowed: ",join(",", $test->{result}->rgba),"\n";
+ }
+}
+
+sub ok ($$$) {
my ($num, $test, $desc) = @_;
if ($test) {
else {
print "not ok $num # $desc\n";
}
+ $test;
+}
+
+sub color_close {
+ my ($c1, $c2) = @_;
+
+ my @c1 = $c1->rgba;
+ my @c2 = $c2->rgba;
+
+ for my $i (0..2) {
+ if (abs($c1[$i]-$c2[$i]) > 2) {
+ return 0;
+ }
+ }
+ return 1;
}
# for use during testing
my $f3 = Imager::Fountain->read(gimp=>'testimg/gimpgrad')
or print "not ";
print "ok 33\n";
-test($imbase, 34, { type=>'fountain', xa=>75, ya=>75, xb=>90, yb=>10,
+test($imbase, 34, { type=>'fountain', xa=>75, ya=>75, xb=>90, yb=>15,
segments=>$f3, super_sample=>'grid',
- ftype=>'radial_square', combine=>1 },
+ ftype=>'radial_square', combine=>'color' },
'testout/t61_fount_gimp.ppm');
sub test {
GIMP Gradient
2
-0.000000 0.130000 0.546377 0.000000 0.000000 1.000000 1.000000 0.000000 1.000000 0.000000 1.000000 4 1
-0.546377 0.773188 1.000000 0.000000 1.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 0 0
+0.000000 0.130000 0.546377 0.900000 0.900000 1.000000 1.000000 0.000000 1.000000 0.000000 1.000000 4 1
+0.546377 0.773188 1.000000 0.000000 1.000000 0.000000 1.000000 0.200000 0.200000 0.200000 0.000000 0 0