From: Tony Cook Date: Fri, 6 Nov 2009 09:42:03 +0000 (+0000) Subject: add opacity adaption fills X-Git-Tag: Imager-0.71~5 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/52f2b10a2d0eb84f8c300146d57f6643b91619b3 add opacity adaption fills --- diff --git a/Imager.xs b/Imager.xs index 5968c300..539836a3 100644 --- a/Imager.xs +++ b/Imager.xs @@ -3379,6 +3379,11 @@ i_new_fill_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_pa OUTPUT: RETVAL +Imager::FillHandle +i_new_fill_opacity(other_fill, alpha_mult) + Imager::FillHandle other_fill + double alpha_mult + void i_errors() PREINIT: diff --git a/fills.c b/fills.c index 9bb2cb1f..e386d8a4 100644 --- a/fills.c +++ b/fills.c @@ -17,6 +17,7 @@ fills.c - implements the basic general fills 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); + fill = i_new_fill_opacity(fill, alpha_mult); i_fill_destroy(fill); =head1 DESCRIPTION @@ -528,6 +529,38 @@ i_new_fill_image(i_img *im, const double *matrix, int xoff, int yoff, int combin return &fill->base; } +static void fill_opacity(i_fill_t *fill, int x, int y, int width, int channels, + i_color *data); +static void fill_opacityf(i_fill_t *fill, int x, int y, int width, int channels, + i_fcolor *data); + +struct i_fill_opacity_t { + i_fill_t base; + i_fill_t *other_fill; + double alpha_mult; +}; + +static struct i_fill_opacity_t +opacity_fill_proto = + { + fill_opacity, + fill_opacityf, + NULL + }; + +i_fill_t * +i_new_fill_opacity(i_fill_t *base_fill, double alpha_mult) { + struct i_fill_opacity_t *fill = mymalloc(sizeof(*fill)); + *fill = opacity_fill_proto; + + fill->base.combine = base_fill->combine; + fill->base.combinef = base_fill->combinef; + + fill->other_fill = base_fill; + fill->alpha_mult = alpha_mult; + + return &fill->base; +} #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill)) @@ -901,6 +934,45 @@ static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels, i_adapt_fcolors(want_channels, f->src->channels, data, width); } +static void +fill_opacity(i_fill_t *fill, int x, int y, int width, int channels, + i_color *data) { + struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill; + int alpha_chan = channels-1; /* channels is always 2 or 4 */ + i_color *datap = data; + + (f->other_fill->f_fill_with_color)(f->other_fill, x, y, width, channels, data); + while (width--) { + double new_alpha = datap->channel[alpha_chan] * f->alpha_mult; + if (new_alpha < 0) + datap->channel[alpha_chan] = 0; + else if (new_alpha > 255) + datap->channel[alpha_chan] = 255; + else datap->channel[alpha_chan] = (int)(new_alpha + 0.5); + + ++datap; + } +} +static void +fill_opacityf(i_fill_t *fill, int x, int y, int width, int channels, + i_fcolor *data) { + struct i_fill_opacity_t *f = (struct i_fill_alpha_t *)fill; + int alpha_chan = channels-1; /* channels is always 2 or 4 */ + i_fcolor *datap = data; + + (f->other_fill->f_fill_with_fcolor)(f->other_fill, x, y, width, channels, data); + + while (width--) { + double new_alpha = datap->channel[alpha_chan] * f->alpha_mult; + if (new_alpha < 0) + datap->channel[alpha_chan] = 0; + else if (new_alpha > 1.0) + datap->channel[alpha_chan] = 1.0; + else datap->channel[alpha_chan] = new_alpha; + + ++datap; + } +} /* =back diff --git a/imager.h b/imager.h index 2508b86f..7c3d22fe 100644 --- a/imager.h +++ b/imager.h @@ -155,6 +155,7 @@ 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); extern i_fill_t * i_new_fill_image(i_img *im, const double *matrix, int xoff, int yoff, int combine); +extern i_fill_t *i_new_fill_opacity(i_fill_t *, double alpha_mult); extern void i_fill_destroy(i_fill_t *fill); float i_gpix_pch(i_img *im,int x,int y,int ch); diff --git a/lib/Imager/Fill.pm b/lib/Imager/Fill.pm index b4e4fe31..3901fbe4 100644 --- a/lib/Imager/Fill.pm +++ b/lib/Imager/Fill.pm @@ -125,6 +125,24 @@ sub new { $hsh{yoff}, $hsh{combine}); $self->{DEPS} = [ $hsh{image}{IMG} ]; } + elsif (defined $hsh{type} && $hsh{type} eq "opacity") { + my $other_fill = delete $hsh{other}; + unless (defined $other_fill) { + Imager->_set_error("'other' parameter required to create alpha fill"); + return; + } + unless (eval { $other_fill->isa("Imager::Fill") }) { + Imager->_set_error("'other' parameter must be an Imager::Fill object to create an alpha fill"); + return; + } + + my $raw_fill = $other_fill->{fill}; + my $opacity = delete $hsh{opacity}; + defined $opacity or $opacity = 0.5; # some sort of default + $self->{fill} = + Imager::i_new_fill_opacity($raw_fill, $opacity); + $self->{DEPS} = [ $other_fill ]; # keep reference to old fill and its deps + } else { $Imager::ERRSTR = "No fill type specified"; warn "No fill type!"; @@ -158,6 +176,7 @@ sub combines { dx=>$dx, dy=>$dy); my $fill3 = Imager::Fill->new(fountain=>$type, ...); my $fill4 = Imager::Fill->new(image=>$img, ...); + my $fill5 = Imager::Fill->new(type => "alpha", other => $fill, alpha => ...); =head1 DESCRIPTION @@ -341,6 +360,35 @@ the L class to create transformation matrices. The matrix parameter will significantly slow down the fill. +=head2 Opacity modification fill + + my $fill = Imager::Fill->new(type => "opacity" + +This can be used to make a fill that is a more translucent of opaque +version of an existing fill. This is intended for use where you +receive a fill object as a parameter and need to change the opacity. + +Parameters: + +=over + +=item * + +type => "alpha" - Required + +=item * + +other - the fill to produce a modified version of. This must be an +Imager::Fill object. Required. + +=item * + +opacity - multiplier for the source fill opacity. Default: 0.5. + +=back + +The source fill's combine mode is used. + =head1 OTHER METHODS =over diff --git a/lib/Imager/Test.pm b/lib/Imager/Test.pm index e2e27560..2fd64082 100644 --- a/lib/Imager/Test.pm +++ b/lib/Imager/Test.pm @@ -127,10 +127,10 @@ sub is_color4($$$$$$) { && $ca == $alpha, $comment)) { $builder->diag(<diag(< 129; +use Test::More tests => 143; use Imager ':handy'; use Imager::Fill; use Imager::Color::Float; -use Imager::Test qw(is_image); +use Imager::Test qw(is_image is_color4 is_fcolor4); use Config; Imager::init_log("testout/t20fill.log", 1); @@ -466,6 +466,108 @@ SKIP: } } +{ # alpha modifying fills + { # 8-bit/sample + my $base_img = Imager->new(xsize => 4, ysize => 2, channels => 4); + $base_img->setscanline + ( + x => 0, + y => 0, + pixels => + [ + map Imager::Color->new($_), + qw/FF000020 00FF0080 00008040 FFFF00FF/, + ], + ); + $base_img->setscanline + ( + x => 0, + y => 1, + pixels => + [ + map Imager::Color->new($_), + qw/FFFF00FF FF000000 00FF0080 00008040/ + ] + ); + my $base_fill = Imager::Fill->new + ( + image => $base_img, + combine => "normal", + ); + ok($base_fill, "make the base image fill"); + my $fill50 = Imager::Fill->new(type => "opacity", opacity => 0.5, other => $base_fill) + or print "# ", Imager->errstr, "\n"; + ok($fill50, "make 50% alpha translation fill"); + my $out = Imager->new(xsize => 10, ysize => 10, channels => 4); + $out->box(fill => $fill50); + is_color4($out->getpixel(x => 0, y => 0), + 255, 0, 0, 16, "check alpha output"); + is_color4($out->getpixel(x => 2, y => 1), + 0, 255, 0, 64, "check alpha output"); + $out->box(filled => 1, color => "000000"); + is_color4($out->getpixel(x => 0, y => 0), + 0, 0, 0, 255, "check after clear"); + $out->box(fill => $fill50); + is_color4($out->getpixel(x => 4, y => 2), + 16, 0, 0, 255, "check drawn against background"); + is_color4($out->getpixel(x => 6, y => 3), + 0, 64, 0, 255, "check drawn against background"); + } + { # double/sample + use Imager::Color::Float; + my $base_img = Imager->new(xsize => 4, ysize => 2, channels => 4, bits => "double"); + $base_img->setscanline + ( + x => 0, + y => 0, + pixels => + [ + map Imager::Color::Float->new(@$_), + [ 1, 0, 0, 0.125 ], + [ 0, 1, 0, 0.5 ], + [ 0, 0, 0.5, 0.25 ], + [ 1, 1, 0, 1 ], + ], + ); + $base_img->setscanline + ( + x => 0, + y => 1, + pixels => + [ + map Imager::Color::Float->new(@$_), + [ 1, 1, 0, 1 ], + [ 1, 0, 0, 0 ], + [ 0, 1, 0, 0.5 ], + [ 0, 0, 0.5, 0.25 ], + ] + ); + my $base_fill = Imager::Fill->new + ( + image => $base_img, + combine => "normal", + ); + ok($base_fill, "make the base image fill"); + my $fill50 = Imager::Fill->new(type => "opacity", opacity => 0.5, other => $base_fill) + or print "# ", Imager->errstr, "\n"; + ok($fill50, "make 50% alpha translation fill"); + my $out = Imager->new(xsize => 10, ysize => 10, channels => 4, bits => "double"); + $out->box(fill => $fill50); + is_fcolor4($out->getpixel(x => 0, y => 0, type => "float"), + 1, 0, 0, 0.0625, "check alpha output at 0,0"); + is_fcolor4($out->getpixel(x => 2, y => 1, type => "float"), + 0, 1, 0, 0.25, "check alpha output at 2,1"); + $out->box(filled => 1, color => "000000"); + is_fcolor4($out->getpixel(x => 0, y => 0, type => "float"), + 0, 0, 0, 1, "check after clear"); + $out->box(fill => $fill50); + is_fcolor4($out->getpixel(x => 4, y => 2, type => "float"), + 0.0625, 0, 0, 1, "check drawn against background at 4,2"); + is_fcolor4($out->getpixel(x => 6, y => 3, type => "float"), + 0, 0.25, 0, 1, "check drawn against background at 6,3"); + } +} + sub color_close { my ($c1, $c2) = @_;