- the font libraries are now only initialized when needed.
http://rt.cpan.org/Ticket/Display.html?id=28825
+ - moved the imtoc.perl code into Imager::Preprocess
+
+ - paste() and rubthrough() now adapt the source image data to the
+ destination, so you can now safely paste/rubthrough from greyscale
+ images to color images or back, or from alpha channel images to
+ noalpha channels or back.
+ https://rt.cpan.org/Ticket/Display.html?id=30908
+
+ - rubthrough() now falls back to pasting when the source doesn't have
+ an alpha channel. This effectively treats the source as having a
+ max alpha channel, the right thing to do.
+ http://rt.cpan.org/Ticket/Display.html?id=29944
+
+ - re-worked most of the area filling code to use a common set of
+ functions when filling.
+ Corrected normal combine mode.
+ Rewrote most of the combine modes to match the way the SVG draft
+ defines them with respect to a translucent source and destination.
+ Added tests for translucent source and destination.
+ Added tests to check 8-bit/sample and double/sample combines work
+ similarly.
+ https://rt.cpan.org/Ticket/Display.html?id=29879
+
Bug fixes:
- Imager::Matrix2d->translate() now only requires one of the x or y
# library keys that are image file formats
my %file_formats = map { $_ => 1 } qw/tiff pnm gif png jpeg raw bmp tga/;
+# image pixel combine 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 will be used to store global defaults at some point
+my %defaults;
+
BEGIN {
require Exporter;
@ISA = qw(Exporter);
return $result;
}
+sub _combine {
+ my ($self, $combine, $default) = @_;
+
+ if (!defined $combine && ref $self) {
+ $combine = $self->{combine};
+ }
+ defined $combine or $combine = $defaults{combine};
+ defined $combine or $combine = $default;
+
+ if (exists $combine_types{$combine}) {
+ $combine = $combine_types{$combine};
+ }
+
+ return $combine;
+}
+
sub _valid_image {
my ($self) = @_;
sub rubthrough {
my $self=shift;
- my %opts=(tx => 0,ty => 0, @_);
+ my %opts= @_;
unless ($self->{IMG}) {
$self->{ERRSTR}='empty input image';
src_maxy => $opts{src}->getheight(),
%opts);
- unless (i_rubthru($self->{IMG}, $opts{src}->{IMG}, $opts{tx}, $opts{ty},
+ my $tx = $opts{tx};
+ defined $tx or $tx = $opts{left};
+ defined $tx or $tx = 0;
+
+ my $ty = $opts{ty};
+ defined $ty or $ty = $opts{top};
+ defined $ty or $ty = 0;
+
+ unless (i_rubthru($self->{IMG}, $opts{src}->{IMG}, $tx, $ty,
$opts{src_minx}, $opts{src_miny},
$opts{src_maxx}, $opts{src_maxy})) {
$self->_set_error($self->_error_as_msg());
return undef;
}
+
return $self;
}
+sub compose {
+ my $self = shift;
+ my %opts =
+ (
+ opacity => 1.0,
+ mask_left => 0,
+ mask_top => 0,
+ @_
+ );
+
+ unless ($self->{IMG}) {
+ $self->_set_error("compose: empty input image");
+ return;
+ }
+
+ unless ($opts{src}) {
+ $self->_set_error("compose: src parameter missing");
+ return;
+ }
+
+ unless ($opts{src}{IMG}) {
+ $self->_set_error("compose: src parameter empty image");
+ return;
+ }
+ my $src = $opts{src};
+
+ my $left = $opts{left};
+ defined $left or $left = $opts{tx};
+ defined $left or $left = 0;
+
+ my $top = $opts{top};
+ defined $top or $top = $opts{ty};
+ defined $top or $top = 0;
+
+ my $src_left = $opts{src_left};
+ defined $src_left or $src_left = $opts{src_minx};
+ defined $src_left or $src_left = 0;
+
+ my $src_top = $opts{src_top};
+ defined $src_top or $src_top = $opts{src_miny};
+ defined $src_top or $src_top = 0;
+
+ my $width = $opts{width};
+ if (!defined $width && defined $opts{src_maxx}) {
+ $width = $opts{src_maxx} - $src_left;
+ }
+ defined $width or $width = $src->getwidth() - $src_left;
+
+ my $height = $opts{height};
+ if (!defined $height && defined $opts{src_maxy}) {
+ $height = $opts{src_maxy} - $src_top;
+ }
+ defined $height or $height = $src->getheight() - $src_top;
+
+ my $combine = $self->_combine($opts{combine}, 'normal');
+
+ if ($opts{mask}) {
+ unless ($opts{mask}{IMG}) {
+ $self->_set_error("compose: mask parameter empty image");
+ return;
+ }
+
+ my $mask_left = $opts{mask_left};
+ defined $mask_left or $mask_left = $opts{mask_minx};
+ defined $mask_left or $mask_left = 0;
+
+ my $mask_top = $opts{mask_top};
+ defined $mask_top or $mask_top = $opts{mask_miny};
+ defined $mask_top or $mask_top = 0;
+
+ i_compose_mask($self->{IMG}, $src->{IMG}, $opts{mask}{IMG},
+ $left, $top, $src_left, $src_top,
+ $mask_left, $mask_top, $width, $height,
+ $combine, $opts{opacity})
+ or return;
+ }
+ else {
+ i_compose($self->{IMG}, $src->{IMG}, $left, $top, $src_left, $src_top,
+ $width, $height, $combine, $opts{opacity})
+ or return;
+ }
+
+ return $self;
+}
sub flip {
my $self = shift;
return ();
}
+sub combines {
+ return @combine_types;
+}
+
# get the minimum of a list
sub _min {
=head1 ERROR HANDLING
-In general a method will return false when it fails, if it does use the errstr() method to find out why:
+In general a method will return false when it fails, if it does use
+the errstr() method to find out why:
=over
colorcount() - L<Imager::Draw/colorcount>
+combines() - L<Imager::Draw/combines>
+
+compose() - L<Imager::Transformations/compose>
+
convert() - L<Imager::Transformations/"Color transformations"> -
transform the color space
int src_maxx
int src_maxy
+undef_int
+i_compose(out, src, out_left, out_top, src_left, src_top, width, height, combine = ic_normal, opacity = 0.0)
+ Imager::ImgRaw out
+ Imager::ImgRaw src
+ int out_left
+ int out_top
+ int src_left
+ int src_top
+ int width
+ int height
+ int combine
+ double opacity
+
+undef_int
+i_compose_mask(out, src, mask, out_left, out_top, src_left, src_top, mask_left, mask_top, width, height, combine = ic_normal, opacity = 0.0)
+ Imager::ImgRaw out
+ Imager::ImgRaw src
+ Imager::ImgRaw mask
+ int out_left
+ int out_top
+ int src_left
+ int src_top
+ int mask_left
+ int mask_top
+ int width
+ int height
+ int combine
+ double opacity
undef_int
i_flipxy(im, direction)
bigtest.perl Library selection tester
bmp.c Reading and writing Windows BMP files
color.c Color translation and handling
+compose.im
conv.im
convert.c
doco.perl
immacros.h
imperl.h
imrender.h Buffer rending engine function declarations
-imtoc.perl Sample size adapter pre-processor
io.c
iolayer.c
iolayer.h
lib/Imager/Inline.pod Using Imager with Inline::C
lib/Imager/LargeSamples.pod Track large sample support
lib/Imager/Matrix2d.pm
+lib/Imager/Preprocess.pm
lib/Imager/Regops.pm
lib/Imager/Test.pm
lib/Imager/Transform.pm
map.c
maskimg.c
palimg.c
+paste.im
plug.h
png.c
pnm.c
regmach.o trans2.o quant.o error.o convert.o
map.o tags.o palimg.o maskimg.o img16.o rotate.o
bmp.o tga.o color.o fills.o imgdouble.o limits.o hlines.o
- imext.o scale.o rubthru.o render.o);
+ imext.o scale.o rubthru.o render.o paste.o compose.o);
$Recommends{Imager} =
{ 'Parse::RecDescent' => 0 };
'DEFINE' => "$OSDEF $CFLAGS",
'INC' => "$lib_cflags $DFLAGS $F_INC",
'OBJECT' => join(' ', @objs, $F_OBJECT),
- clean => { FILES=>'testout meta.tmp rubthru.c scale.c' },
+ clean => { FILES=>'testout meta.tmp rubthru.c scale.c conv.c filters.c gaussian.c render.c rubthru.c' },
PM => gen_PM(),
PREREQ_PM => { 'Test::More' => 0.47 },
);
(my $c = $im) =~ s/\.im$/.c/;
return <<MAKE;
-$c: $im imtoc.perl
- $perl imtoc.perl $im $c
+$c: $im lib/Imager/Preprocess.pm
+ $perl -Ilib -MImager::Preprocess -epreprocess $im $c
MAKE
--- /dev/null
+#include "imager.h"
+#include "imrender.h"
+#include "imageri.h"
+
+int
+i_compose_mask(i_img *out, i_img *src, i_img *mask,
+ int out_left, int out_top,
+ int src_left, int src_top,
+ int mask_left, int mask_top,
+ int width, int height,
+ int combine,
+ double opacity) {
+ i_render r;
+ int dy;
+ i_fill_combine_f combinef_8;
+ i_fill_combinef_f combinef_double;
+ int channel_zero = 0;
+
+ i_clear_error();
+ if (out_left >= out->xsize
+ || out_top >= out->ysize
+ || src_left >= src->xsize
+ || src_top >= src->ysize
+ || width <= 0
+ || height <= 0
+ || out_left + width <= 0
+ || out_top + height <= 0
+ || src_left + width <= 0
+ || src_top + height <= 0
+ || mask_left >= mask->xsize
+ || mask_top >= mask->ysize
+ || mask_left + width <= 0
+ || mask_top + height <= 0)
+ return 0;
+
+ if (out_left < 0) {
+ width = out_left + width;
+ out_left = 0;
+ }
+ if (out_left + width > out->xsize)
+ width = out->xsize - out_left;
+
+ if (out_top < 0) {
+ height = out_top + height;
+ out_top = 0;
+ }
+ if (out_top + height > out->ysize)
+ height = out->ysize - out_top;
+
+ if (src_left < 0) {
+ width = src_left + width;
+ src_left = 0;
+ }
+ if (src_left + width > src->xsize)
+ width = src->xsize - src_left;
+
+ if (src_top < 0) {
+ height = src_top + height;
+ src_top = 0;
+ }
+ if (src_top + height > src->ysize)
+ height = src->ysize - src_left;
+
+ if (mask_left < 0) {
+ width = mask_left + width;
+ mask_left = 0;
+ }
+ if (mask_left + width > mask->xsize)
+ width = mask->xsize - mask_left;
+
+ if (mask_top < 0) {
+ height = mask->ysize + height;
+ mask_top = 0;
+ }
+ if (mask_top + height > mask->ysize)
+ height = mask->xsize - mask_top;
+
+ if (opacity > 1.0)
+ opacity = 1.0;
+ else if (opacity <= 0)
+ return 0;
+
+ i_get_combine(combine, &combinef_8, &combinef_double);
+
+ i_render_init(&r, out, width);
+#code out->bits <= 8 && src->bits<= 8 && mask->bits <= 8
+ IM_COLOR *src_line = mymalloc(sizeof(IM_COLOR) * width);
+ IM_SAMPLE_T *mask_line = mymalloc(sizeof(IM_SAMPLE_T) * width);
+ int adapt_channels = out->channels;
+
+ if (adapt_channels == 1 || adapt_channels == 3)
+ ++adapt_channels;
+
+ for (dy = 0; dy < height; ++dy) {
+ IM_GLIN(src, src_left, src_left + width, src_top + dy, src_line);
+ IM_ADAPT_COLORS(adapt_channels, src->channels, src_line, width);
+ IM_GSAMP(mask, mask_left, mask_left + width, mask_top + dy,
+ mask_line, &channel_zero, 1);
+ if (opacity < 1.0) {
+ int i;
+ IM_SAMPLE_T *maskp = mask_line;
+ for (i = 0; i < width; ++i) {
+ *maskp = IM_ROUND(*maskp * opacity);
+ ++maskp;
+ }
+ }
+ IM_RENDER_LINE(&r, out_left, out_top+dy, width, mask_line, src_line,
+ IM_SUFFIX(combinef));
+ }
+ myfree(src_line);
+ myfree(mask_line);
+
+#/code
+ i_render_done(&r);
+
+ return 1;
+}
+
+int
+i_compose(i_img *out, i_img *src,
+ int out_left, int out_top,
+ int src_left, int src_top,
+ int width, int height,
+ int combine,
+ double opacity) {
+ i_render r;
+ int dy;
+ i_fill_combine_f combinef_8;
+ i_fill_combinef_f combinef_double;
+
+ i_clear_error();
+ if (out_left >= out->xsize
+ || out_top >= out->ysize
+ || src_left >= src->xsize
+ || src_top >= src->ysize
+ || width <= 0
+ || height <= 0
+ || out_left + width <= 0
+ || out_top + height <= 0
+ || src_left + width <= 0
+ || src_top + height <= 0)
+ return 0;
+
+ if (out_left < 0) {
+ width = out_left + width;
+ out_left = 0;
+ }
+ if (out_left + width > out->xsize)
+ width = out->xsize - out_left;
+
+ if (out_top < 0) {
+ height = out_top + height;
+ out_top = 0;
+ }
+ if (out_top + height > out->ysize)
+ height = out->ysize - out_top;
+
+ if (src_left < 0) {
+ width = src_left + width;
+ src_left = 0;
+ }
+ if (src_left + width > src->xsize)
+ width = src->xsize - src_left;
+
+ if (src_top < 0) {
+ height = src_top + height;
+ src_top = 0;
+ }
+ if (src_top + height > src->ysize)
+ height = src->ysize - src_left;
+
+ if (opacity > 1.0)
+ opacity = 1.0;
+ else if (opacity <= 0)
+ return 0;
+
+ i_get_combine(combine, &combinef_8, &combinef_double);
+
+ i_render_init(&r, out, width);
+#code out->bits <= 8 && src->bits <= 8
+ IM_COLOR *src_line = mymalloc(sizeof(IM_COLOR) * width);
+ IM_SAMPLE_T *mask_line = NULL;
+ int adapt_channels = out->channels;
+
+ if (opacity != 1.0) {
+ int i;
+ IM_SAMPLE_T mask_value = IM_ROUND(opacity * IM_SAMPLE_MAX);
+ mask_line = mymalloc(sizeof(IM_SAMPLE_T) * width);
+
+ for (i = 0; i < width; ++i)
+ mask_line[i] = mask_value;
+ }
+
+ if (adapt_channels == 1 || adapt_channels == 3)
+ ++adapt_channels;
+
+ for (dy = 0; dy < height; ++dy) {
+ IM_GLIN(src, src_left, src_left + width, src_top + dy, src_line);
+ IM_ADAPT_COLORS(adapt_channels, src->channels, src_line, width);
+ IM_RENDER_LINE(&r, out_left, out_top+dy, width, mask_line, src_line,
+ IM_SUFFIX(combinef));
+ }
+ myfree(src_line);
+ if (mask_line)
+ myfree(mask_line);
+
+#/code
+ i_render_done(&r);
+
+ return 1;
+}
--- /dev/null
+=head1 NAME
+
+draw.pod - overview of planned drawing changes
+
+=head1 SYNOPSIS
+
+ fix the processing done by the current combine modes
+ combine modes for most drawing operations
+ thick lines
+
+=head1 combine mode fixes
+
+Currently:
+
+=over
+
+=item *
+
+the calculations are just wrong
+
+=item *
+
+they don't handle adapting to the target channel count
+
+=back
+
+To do:
+
+=over
+
+=item *
+
+add a render function that calls the fill function then applies it to
+the target based on the combine mode.
+
+=item *
+
+modify fill functions to always produce RGBA
+
+=item *
+
+have the combine mode functions just generate the color, the render
+function can then alpha adjust it and apply it to the target
+
+=back
+
+=head1 combine modes for drawing functions
+
+=over
+
+=item *
+
+write versions of each function that take a combine mode
+
+=back
+
+=head1 thick lines
+
+Two possible approaches:
+
+=over
+
+=item *
+
+given a polyline, generate a polygon for the entire shape - more
+complex, overlap problems
+
+=item *
+
+given a polyline, generate polygons for each segment, draw to a work
+image and use that to compose the fill - possible problems joining the
+segments
+
+=back
#include "draw.h"
#include "log.h"
#include "imageri.h"
-
+#include "imrender.h"
#include <limits.h>
static void
for(i=0;i<ar->lines;i++) if (ar->data[i].max!=-1) for(x=ar->data[i].min;x<ar->data[i].max;x++) i_ppix(im,x,i,val);
}
-void
-i_mmarray_render_fill(i_img *im,i_mmarray *ar,i_fill_t *fill) {
- int x, w, y;
- if (im->bits == i_8_bits && fill->fill_with_color) {
- i_color *line = mymalloc(sizeof(i_color) * im->xsize); /* checked 5jul05 tonyc */
- i_color *work = NULL;
- if (fill->combine)
- work = mymalloc(sizeof(i_color) * im->xsize); /* checked 5jul05 tonyc */
- 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->combine) {
- i_glin(im, x, x+w, y, line);
- (fill->fill_with_color)(fill, x, y, w, im->channels, work);
- (fill->combine)(line, work, im->channels, w);
- }
- else {
- (fill->fill_with_color)(fill, x, y, w, im->channels, line);
- }
- i_plin(im, x, x+w, y, line);
- }
- }
-
- myfree(line);
- if (work)
- myfree(work);
- }
- else {
- i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize); /* checked 5jul05 tonyc */
- i_fcolor *work = NULL;
- if (fill->combinef)
- work = mymalloc(sizeof(i_fcolor) * im->xsize); /* checked 5jul05 tonyc */
- 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->combinef) {
- i_glinf(im, x, x+w, y, line);
- (fill->fill_with_fcolor)(fill, x, y, w, im->channels, work);
- (fill->combinef)(line, work, im->channels, w);
- }
- else {
- (fill->fill_with_fcolor)(fill, x, y, w, im->channels, line);
- }
- i_plinf(im, x, x+w, y, line);
- }
- }
-
- myfree(line);
- if (work)
- myfree(work);
- }
-}
-
-
static
void
i_arcdraw(int x1, int y1, int x2, int y2, i_mmarray *ar) {
void
i_box_cfill(i_img *im,int x1,int y1,int x2,int y2,i_fill_t *fill) {
+ i_render r;
mm_log((1,"i_box_cfill(im* 0x%x,x1 %d,y1 %d,x2 %d,y2 %d,fill 0x%x)\n",im,x1,y1,x2,y2,fill));
++x2;
y2 = im->ysize-1;
if (x1 >= x2 || y1 > y2)
return;
- if (im->bits == i_8_bits && fill->fill_with_color) {
- i_color *line = mymalloc(sizeof(i_color) * (x2 - x1)); /* checked 5jul05 tonyc */
- i_color *work = NULL;
- if (fill->combine)
- work = mymalloc(sizeof(i_color) * (x2-x1)); /* checked 5jul05 tonyc */
- while (y1 <= y2) {
- if (fill->combine) {
- i_glin(im, x1, x2, y1, line);
- (fill->fill_with_color)(fill, x1, y1, x2-x1, im->channels, work);
- (fill->combine)(line, work, im->channels, x2-x1);
- }
- else {
- (fill->fill_with_color)(fill, x1, y1, x2-x1, im->channels, line);
- }
- i_plin(im, x1, x2, y1, line);
- ++y1;
- }
- myfree(line);
- if (work)
- myfree(work);
- }
- else {
- i_fcolor *line = mymalloc(sizeof(i_fcolor) * (x2 - x1)); /* checked 5jul05 tonyc */
- i_fcolor *work;
- work = mymalloc(sizeof(i_fcolor) * (x2 - x1)); /* checked 5jul05 tonyc */
-
- while (y1 <= y2) {
- if (fill->combine) {
- i_glinf(im, x1, x2, y1, line);
- (fill->fill_with_fcolor)(fill, x1, y1, x2-x1, im->channels, work);
- (fill->combinef)(line, work, im->channels, x2-x1);
- }
- else {
- (fill->fill_with_fcolor)(fill, x1, y1, x2-x1, im->channels, line);
- }
- i_plinf(im, x1, x2, y1, line);
- ++y1;
- }
- myfree(line);
- if (work)
- myfree(work);
+
+ i_render_init(&r, im, x2-x1);
+ while (y1 <= y2) {
+ i_render_fill(&r, x1, y1, x2-x1, NULL, fill);
+ ++y1;
}
+ i_render_done(&r);
}
-
/*
=item i_line(im, x1, y1, x2, y2, val, endp)
}
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-void
-i_line_aa3(i_img *im,int x1,int y1,int x2,int y2,i_color *val) {
- i_color tval;
- float alpha;
- float dsec,dfrac;
- int temp,dx,dy,isec,ch;
-
- mm_log((1,"i_line_aa(im* 0x%x,x1 %d,y1 %d,x2 %d,y2 %d,val 0x%x)\n",im,x1,y1,x2,y2,val));
-
- dy=y2-y1;
- dx=x2-x1;
-
- if (abs(dx)>abs(dy)) { /* alpha < 1 */
- if (x2<x1) { temp=x1; x1=x2; x2=temp; temp=y1; y1=y2; y2=temp; }
- alpha=(float)(y2-y1)/(float)(x2-x1);
-
- dsec=y1;
- while(x1<=x2) {
- isec=(int)dsec;
- dfrac=dsec-isec;
- /* dfrac=1-(1-dfrac)*(1-dfrac); */
- /* This is something we can play with to try to get better looking lines */
-
- i_gpix(im,x1,isec,&tval);
- for(ch=0;ch<im->channels;ch++) tval.channel[ch]=(unsigned char)(dfrac*(float)tval.channel[ch]+(1-dfrac)*(float)val->channel[ch]);
- i_ppix(im,x1,isec,&tval);
-
- i_gpix(im,x1,isec+1,&tval);
- for(ch=0;ch<im->channels;ch++) tval.channel[ch]=(unsigned char)((1-dfrac)*(float)tval.channel[ch]+dfrac*(float)val->channel[ch]);
- i_ppix(im,x1,isec+1,&tval);
-
- dsec+=alpha;
- x1++;
- }
- } else {
- if (y2<y1) { temp=y1; y1=y2; y2=temp; temp=x1; x1=x2; x2=temp; }
- alpha=(float)(x2-x1)/(float)(y2-y1);
- dsec=x1;
- while(y1<=y2) {
- isec=(int)dsec;
- dfrac=dsec-isec;
- /* dfrac=sqrt(dfrac); */
- /* This is something we can play with */
- i_gpix(im,isec,y1,&tval);
- for(ch=0;ch<im->channels;ch++) tval.channel[ch]=(unsigned char)(dfrac*(float)tval.channel[ch]+(1-dfrac)*(float)val->channel[ch]);
- i_ppix(im,isec,y1,&tval);
-
- i_gpix(im,isec+1,y1,&tval);
- for(ch=0;ch<im->channels;ch++) tval.channel[ch]=(unsigned char)((1-dfrac)*(float)tval.channel[ch]+dfrac*(float)val->channel[ch]);
- i_ppix(im,isec+1,y1,&tval);
-
- dsec+=alpha;
- y1++;
- }
- }
-}
-
-
/*
=item i_line_aa(im, x1, x2, y1, y2, color, endp)
int x, y;
int start;
- if (im->bits == i_8_bits && fill->fill_with_color) {
- /* bxmax/bxmin are inside the image, hence this won't overflow */
- i_color *line = mymalloc(sizeof(i_color) * (bxmax - bxmin)); /* checked 5jul05 tonyc */
- i_color *work = NULL;
- if (fill->combine)
- work = mymalloc(sizeof(i_color) * (bxmax - bxmin)); /* checked 5jul05 tonyc */
-
- for(y=bymin; y<=bymax; y++) {
- x = bxmin;
- while (x < bxmax) {
- while (x < bxmax && !btm_test(btm, x, y)) {
- ++x;
- }
- if (btm_test(btm, x, y)) {
- start = x;
- while (x < bxmax && btm_test(btm, x, y)) {
- ++x;
- }
- if (fill->combine) {
- i_glin(im, start, x, y, line);
- (fill->fill_with_color)(fill, start, y, x-start, im->channels,
- work);
- (fill->combine)(line, work, im->channels, x-start);
- }
- else {
- (fill->fill_with_color)(fill, start, y, x-start, im->channels,
- line);
- }
- i_plin(im, start, x, y, line);
- }
+ i_render r;
+
+ i_render_init(&r, im, bxmax - bxmin + 1);
+
+ for(y=bymin; y<=bymax; y++) {
+ x = bxmin;
+ while (x <= bxmax) {
+ while (x <= bxmax && !btm_test(btm, x, y)) {
+ ++x;
}
- }
- myfree(line);
- if (work)
- myfree(work);
- }
- else {
- /* bxmax/bxmin are inside the image, hence this won't overflow */
- i_fcolor *line = mymalloc(sizeof(i_fcolor) * (bxmax - bxmin)); /* checked 5jul05 tonyc */
- i_fcolor *work = NULL;
- if (fill->combinef)
- work = mymalloc(sizeof(i_fcolor) * (bxmax - bxmin)); /* checked 5jul05 tonyc */
-
- for(y=bymin;y<=bymax;y++) {
- x = bxmin;
- while (x < bxmax) {
- while (x < bxmax && !btm_test(btm, x, y)) {
- ++x;
- }
- if (btm_test(btm, x, y)) {
- start = x;
- while (x < bxmax && btm_test(btm, x, y)) {
- ++x;
- }
- if (fill->combinef) {
- i_glinf(im, start, x, y, line);
- (fill->fill_with_fcolor)(fill, start, y, x-start, im->channels,
- work);
- (fill->combinef)(line, work, im->channels, x-start);
- }
- else {
- (fill->fill_with_fcolor)(fill, start, y, x-start, im->channels,
- line);
- }
- i_plinf(im, start, x, y, line);
- }
+ if (btm_test(btm, x, y)) {
+ start = x;
+ while (x <= bxmax && btm_test(btm, x, y)) {
+ ++x;
+ }
+ i_render_fill(&r, start, y, x-start, NULL, fill);
}
}
- myfree(line);
- if (work)
- myfree(work);
}
+ i_render_done(&r);
}
i_color *);
static void fill_solidf(i_fill_t *, int x, int y, int width, int channels,
i_fcolor *);
-static void fill_solid_comb(i_fill_t *, int x, int y, int width, int channels,
- i_color *);
-static void fill_solidf_comb(i_fill_t *, int x, int y, int width,
- int channels, i_fcolor *);
static i_fill_solid_t base_solid_fill =
{
NULL,
},
};
-static i_fill_solid_t base_solid_fill_comb =
-{
- {
- fill_solid_comb,
- fill_solidf_comb,
- NULL,
- NULL,
- NULL,
- },
-};
/*
=item i_fill_destroy(fill)
int ch;
i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
+ *fill = base_solid_fill;
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;
for (ch = 0; ch < MAXCHANNELS; ++ch) {
fill->c.channel[ch] = SampleFTo8(c->channel[ch]);
int ch;
i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
+ *fill = base_solid_fill;
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;
for (ch = 0; ch < MAXCHANNELS; ++ch) {
fill->fc.channel[ch] = Sample8ToF(c->channel[ch]);
double matrix[9];
};
+static struct i_fill_image_t
+image_fill_proto =
+ {
+ {
+ fill_image,
+ fill_imagef,
+ NULL
+ }
+ };
+
/*
=item i_new_fill_image(im, matrix, xoff, yoff, combine)
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;
+ *fill = image_fill_proto;
if (combine) {
i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
static void
fill_solid(i_fill_t *fill, int x, int y, int width, int channels,
i_color *data) {
+ i_color c = T_SOLID_FILL(fill)->c;
+ i_adapt_colors(channels > 2 ? 4 : 2, 4, &c, 1);
while (width-- > 0) {
- *data++ = T_SOLID_FILL(fill)->c;
+ *data++ = 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;
- }
-}
-
-/*
-=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;
-
+ i_adapt_fcolors(channels > 2 ? 4 : 2, 4, &c, 1);
while (width-- > 0) {
*data++ = c;
}
}
+static i_fill_hatch_t
+hatch_fill_proto =
+ {
+ {
+ fill_hatch,
+ fill_hatchf,
+ NULL
+ }
+ };
+
/*
=item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy)
int dx, int dy) {
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 = hatch_fill_proto;
/* Some Sun C didn't like the condition expressions that were here.
See https://rt.cpan.org/Ticket/Display.html?id=21944
*/
}
}
-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
static void
fount_fill_destroy(i_fill_t *fill);
+static i_fill_fountain_t
+fount_fill_proto =
+ {
+ {
+ NULL,
+ fill_fountf,
+ fount_fill_destroy
+ }
+ };
+
+
/*
=item i_new_fill_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, count, segs)
int count, i_fountain_seg *segs) {
i_fill_fountain_t *fill = mymalloc(sizeof(i_fill_fountain_t));
- fill->base.fill_with_color = NULL;
- fill->base.fill_with_fcolor = fill_fountf;
- fill->base.destroy = fount_fill_destroy;
+ *fill = fount_fill_proto;
if (combine)
i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
else {
use strict;
use Imager;
use Getopt::Long;
+use Time::HiRes qw(time);
my $count = 1000;
GetOptions("c=i"=>\$count)
print " replace $offset/$len: ", unpack("H*", $ins), "\n";
substr($data, $offset, $len, $ins);
}
+ my $start = time;
my $im = Imager->new;
- if ($im->read(data => $data)) {
+ my $result = $im->read(data => $data);
+ my $dur = time() - $start;
+ if ($dur > 1.0) {
+ print "***Took too long to load\n";
+ }
+ printf " Took %f seconds\n", time() - $start;
+ if ($result) {
print "<< Success\n";
}
else {
*/
void
i_int_hlines_fill_fill(i_img *im, i_int_hlines *hlines, i_fill_t *fill) {
+ i_render r;
int y, i;
+ i_render_init(&r, im, im->xsize);
+
+ for (y = hlines->start_y; y < hlines->limit_y; ++y) {
+ i_int_hline_entry *entry = hlines->entries[y - hlines->start_y];
+ if (entry) {
+ for (i = 0; i < entry->count; ++i) {
+ i_int_hline_seg *seg = entry->segs + i;
+ int width = seg->x_limit-seg->minx;
+
+ i_render_fill(&r, seg->minx, y, width, NULL, fill);
+ }
+ }
+ }
+ i_render_done(&r);
+
+#if 1
+#else
if (im->bits == i_8_bits && fill->fill_with_color) {
i_color *line = mymalloc(sizeof(i_color) * im->xsize);
i_color *work = NULL;
if (work)
myfree(work);
}
+#endif
}
/*
}
}
-/*
-=item i_copyto(dest, src, x1, y1, x2, y2, tx, ty)
-
-=category Image
-
-Copies image data from the area (x1,y1)-[x2,y2] in the source image to
-a rectangle the same size with it's top-left corner at (tx,ty) in the
-destination image.
-
-If x1 > x2 or y1 > y2 then the corresponding co-ordinates are swapped.
-
-=cut
-*/
-
-void
-i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty) {
- int x, y, t, ttx, tty;
-
- if (x2<x1) { t=x1; x1=x2; x2=t; }
- if (y2<y1) { t=y1; y1=y2; y2=t; }
- if (tx < 0) {
- /* adjust everything equally */
- x1 += -tx;
- x2 += -tx;
- tx = 0;
- }
- if (ty < 0) {
- y1 += -ty;
- y2 += -ty;
- ty = 0;
- }
- if (x1 >= src->xsize || y1 >= src->ysize)
- return; /* nothing to do */
- if (x2 > src->xsize)
- x2 = src->xsize;
- if (y2 > src->ysize)
- y2 = src->ysize;
- if (x1 == x2 || y1 == y2)
- return; /* nothing to do */
-
- mm_log((1,"i_copyto(im* %p, src %p, x1 %d, y1 %d, x2 %d, y2 %d, tx %d, ty %d)\n",
- im, src, x1, y1, x2, y2, tx, ty));
-
- if (im->bits == i_8_bits) {
- i_color *row = mymalloc(sizeof(i_color) * (x2-x1));
- tty = ty;
- for(y=y1; y<y2; y++) {
- ttx = tx;
- i_glin(src, x1, x2, y, row);
- i_plin(im, tx, tx+x2-x1, tty, row);
- tty++;
- }
- myfree(row);
- }
- else {
- i_fcolor pv;
- tty = ty;
- for(y=y1; y<y2; y++) {
- ttx = tx;
- for(x=x1; x<x2; x++) {
- i_gpixf(src, x, y, &pv);
- i_ppixf(im, ttx, tty, &pv);
- ttx++;
- }
- tty++;
- }
- }
-}
-
/*
=item i_copy(src)
void i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,const i_color *trans);
i_img* i_copy (i_img *src);
int i_rubthru (i_img *im, i_img *src, int tx, int ty, int src_minx, int src_miny, int src_maxx, int src_maxy);
-
+extern int
+i_compose_mask(i_img *out, i_img *src, i_img *mask,
+ int out_left, int out_top, int src_left, int src_top,
+ int mask_left, int mask_top, int width, int height,
+ int combine, double opacity);
+extern int
+i_compose(i_img *out, i_img *src,
+ int out_left, int out_top, int src_left, int src_top,
+ int width, int height, int combine, double opacity);
undef_int i_flipxy (i_img *im, int direction);
extern i_img *i_rotate90(i_img *im, int degrees);
#include "imrender.h"
#include "immacros.h"
+extern void
+i_adapt_colors(int dest_channels, int src_channels, i_color *colors,
+ size_t count);
+extern void
+i_adapt_fcolors(int dest_channels, int src_channels, i_fcolor *colors,
+ size_t count);
+
#endif
(struct i_fill_tag *fill, int x, int y, int width, int channels,
i_fcolor *data);
typedef void (*i_fill_destroy_f)(struct i_fill_tag *fill);
+
+/* combine functions modify their target and are permitted to modify
+ the source to prevent having to perform extra copying/memory
+ allocations, etc
+ The out array has I<channels> channels.
+
+ The in array has I<channels> channels + an alpha channel if one
+ isn't included in I<channels>.
+*/
+
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,
{
/* called for 8-bit/sample image (and maybe lower) */
/* this may be NULL, if so call fill_with_fcolor */
- i_fill_with_color_f fill_with_color;
+ i_fill_with_color_f f_fill_with_color;
/* called for other sample sizes */
/* this must be non-NULL */
- i_fill_with_fcolor_f fill_with_fcolor;
+ i_fill_with_fcolor_f f_fill_with_fcolor;
/* called if non-NULL to release any extra resources */
i_fill_destroy_f destroy;
extern void
i_render_color(i_render *r, int x, int y, int width, unsigned char const *src,
i_color const *color);
+extern void
+i_render_fill(i_render *r, int x, int y, int width, unsigned char const *src,
+ i_fill_t *fill);
+extern void
+i_render_line(i_render *r, int x, int y, int width, const i_sample_t *src,
+ i_color *line, i_fill_combine_f combine);
+extern void
+i_render_linef(i_render *r, int x, int y, int width, const double *src,
+ i_fcolor *line, i_fill_combinef_f combine);
#endif
+++ /dev/null
-#!perl -w
-use strict;
-
-my $src = shift;
-my $dest = shift
- or usage();
-
-open SRC, "< $src"
- or die "Cannot open $src: $!\n";
-
-my $cond;
-my $cond_line;
-my $save_code;
-my @code;
-my $code_line;
-my @out;
-my $failed;
-
-push @out,
- "#define IM_ROUND_8(x) ((int)((x)+0.5))\n",
- "#define IM_ROUND_double(x) (x)\n",
- "#define IM_LIMIT_8(x) ((x) < 0 ? 0 : (x) > 255 ? 255 : (x))\n",
- "#define IM_LIMIT_double(x) ((x) < 0.0 ? 0.0 : (x) > 1.0 ? 1.0 : (x))\n",
- "#line 1 \"$src\"\n";
-while (defined(my $line = <SRC>)) {
- if ($line =~ /^\#code\s+(\S.+)$/) {
- $save_code
- and do { warn "$src:$code_line:Unclosed #code block\n"; ++$failed; };
-
- $cond = $1;
- $cond_line = $.;
- $code_line = $. + 1;
- $save_code = 1;
- }
- elsif ($line =~ /^\#code\s*$/) {
- $save_code
- and do { warn "$src:$code_line:Unclosed #code block\n"; ++$failed; };
-
- $cond = '';
- $cond_line = 0;
- $code_line = $. + 1;
- $save_code = 1;
- }
- elsif ($line =~ /^\#\/code$/) {
- $save_code
- or do { warn "$src:$.:#/code without #code\n"; ++$failed; next; };
-
- if ($cond) {
- push @out, "#line $cond_line \"$src\"\n";
- push @out, " if ($cond) {\n";
- }
- push @out, "#undef IM_EIGHT_BIT\n",
- "#define IM_EIGHT_BIT 1\n";
- push @out, "#line $code_line \"$src\"\n";
- push @out, byte_samples(@code);
- push @out, " }\n", " else {\n"
- if $cond;
- push @out, "#undef IM_EIGHT_BIT\n";
- push @out, "#line $code_line \"$src\"\n";
- push @out, double_samples(@code);
- push @out, " }\n"
- if $cond;
- push @out, "#line $. \"$src\"\n";
- @code = ();
- $save_code = 0;
- }
- elsif ($save_code) {
- push @code, $line;
- }
- else {
- push @out, $line;
- }
-}
-
-if ($save_code) {
- warn "$src:$code_line:#code block not closed by EOF\n";
- ++$failed;
-}
-
-close SRC;
-
-$failed
- and die "Errors during parsing, aborting\n";
-
-open DEST, "> $dest"
- or die "Cannot open $dest: $!\n";
-print DEST @out;
-close DEST;
-
-sub byte_samples {
- # important we make a copy
- my @lines = @_;
-
- for (@lines) {
- s/\bIM_GPIX\b/i_gpix/g;
- s/\bIM_GLIN\b/i_glin/g;
- s/\bIM_PPIX\b/i_ppix/g;
- s/\bIM_PLIN\b/i_plin/g;
- s/\bIM_GSAMP\b/i_gsamp/g;
- s/\bIM_SAMPLE_MAX\b/255/g;
- s/\bIM_SAMPLE_T/i_sample_t/g;
- s/\bIM_COLOR\b/i_color/g;
- s/\bIM_WORK_T\b/int/g;
- s/\bIM_Sf\b/"%d"/g;
- s/\bIM_Wf\b/"%d"/g;
- s/\bIM_SUFFIX\((\w+)\)/$1_8/g;
- s/\bIM_ROUND\(/IM_ROUND_8(/g;
- s/\bIM_LIMIT\(/IM_LIMIT_8(/g;
- }
-
- @lines;
-}
-
-sub double_samples {
- # important we make a copy
- my @lines = @_;
-
- for (@lines) {
- s/\bIM_GPIX\b/i_gpixf/g;
- s/\bIM_GLIN\b/i_glinf/g;
- s/\bIM_PPIX\b/i_ppixf/g;
- s/\bIM_PLIN\b/i_plinf/g;
- s/\bIM_GSAMP\b/i_gsampf/g;
- s/\bIM_SAMPLE_MAX\b/1.0/g;
- s/\bIM_SAMPLE_T/i_fsample_t/g;
- s/\bIM_COLOR\b/i_fcolor/g;
- s/\bIM_WORK_T\b/double/g;
- s/\bIM_Sf\b/"%f"/g;
- s/\bIM_Wf\b/"%f"/g;
- s/\bIM_SUFFIX\((\w+)\)/$1_double/g;
- s/\bIM_ROUND\(/IM_ROUND_double(/g;
- s/\bIM_LIMIT\(/IM_LIMIT_double(/g;
- }
-
- @lines;
-}
-
-=head1 NAME
-
-imtoc.perl - simple preprocessor for handling multiple sample sizes
-
-=head1 SYNOPSIS
-
- /* in the source: */
- #code condition true to work with 8-bit samples
- ... code using preprocessor types/values ...
- #/code
-
- perl imtoc.perl foo.im foo.c
-
-=head1 DESCRIPTION
-
-This is a simple preprocessor that aims to reduce duplication of
-source code when implementing an algorithm both for 8-bit samples and
-double samples in Imager.
-
-Imager's Makefile.PL currently scans the MANIFEST for .im files and
-adds Makefile files to convert these to .c files.
-
-The beginning of a sample-independent section of code is preceded by:
-
- #code expression
-
-where I<expression> should return true if processing should be done at
-8-bits/sample.
-
-You can also use a #code block around a function definition to produce
-8-bit and double sample versions of a function. In this case #code
-has no expression and you will need to use IM_SUFFIX() to produce
-different function names.
-
-The end of a sample-independent section of code is terminated by:
-
- #/code
-
-#code sections cannot be nested.
-
-#/code without a starting #code is an error.
-
-The following types and values are defined in a #code section:
-
-=over
-
-=item *
-
-IM_GPIX(im, x, y, &col)
-
-=item *
-
-IM_GLIN(im, l, r, y, colors)
-
-=item *
-
-IM_PPIX(im, x, y, &col)
-
-=item *
-
-IM_PLIN(im, x, y, colors)
-
-=item *
-
-IM_GSAMP(im, l, r, y, samples, chans, chan_count)
-
-These correspond to the appropriate image function, eg. IM_GPIX()
-becomes i_gpix() or i_gpixf() as appropriate.
-
-=item *
-
-IM_SAMPLE_MAX - maximum value for a sample
-
-=item *
-
-IM_SAMPLE_T - type of a sample (i_sample_t or i_fsample_t)
-
-=item *
-
-IM_COLOR - color type, either i_color or i_fcolor.
-
-=item *
-
-IM_WORK_T - working sample type, either int or double.
-
-=item *
-
-IM_Sf - format string for the sample type, C<"%d"> or C<"%f">.
-
-=item *
-
-IM_Wf - format string for the work type, C<"%d"> or C<"%f">.
-
-=item *
-
-IM_SUFFIX(identifier) - adds _8 or _double onto the end of identifier.
-
-=item *
-
-IM_EIGHT_BIT - this is a macro defined only in 8-bit/sample code.
-
-=back
-
-Other types, functions and values may be added in the future.
-
-=head1 AUTHOR
-
-Tony Cook <tony@imager.perl.org>
-
-=cut
=for comment
-From: File image.c
+From: File paste.im
=item i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, trans)
my $packed_index_data = pack("C*", $black_index, $red_index);
$im->setscanline(y => $y, pixels => $packed_index_data, type => 'index');
+=head1 Combine Types
+
+Some methods accept a C<combine> parameter, this can be any of the
+following:
+
+=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.
+
+=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
+
+=over
+
+=item combines
+
+Returns a list of possible combine types.
+
+=back
+
=head1 BUGS
box, arc, do not support antialiasing yet. Arc, is only filled as of
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};
-
*_color = \&Imager::_color;
sub new {
my ($class, %hsh) = @_;
my $self = bless { }, $class;
- $hsh{combine} ||= 0;
- if (exists $combine_types{$hsh{combine}}) {
- $hsh{combine} = $combine_types{$hsh{combine}};
- }
+ $hsh{combine} = Imager->_combine($hsh{combine}, 0);
if ($hsh{solid}) {
my $solid = _color($hsh{solid});
if (UNIVERSAL::isa($solid, 'Imager::Color')) {
}
sub combines {
- return @combine_types;
+ return Imager->combines;
}
1;
=item combine
-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.
-
-=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
+The way in which the fill data is combined with the underlying image.
+See L<Imager::Draw/"Combine Types">.
=back
Each channel is simple scaled between c0 and c1.
-=item hsvup
+=item hueup
The color is converted to a HSV value and the scaling is done such
that the hue increases as the fill parameter increases.
-=item hsvdown
+=item huedown
The color is converted to a HSV value and the scaling is done such
that the hue decreases as the fill parameter increases.
--- /dev/null
+package Imager::Preprocess;
+use strict;
+require Exporter;
+use vars qw(@ISA @EXPORT);
+
+@EXPORT = qw(preprocess);
+@ISA = qw(Exporter);
+
+
+sub preprocess {
+ my $src = shift @ARGV;
+ my $dest = shift @ARGV
+ or usage();
+
+ open SRC, "< $src"
+ or die "Cannot open $src: $!\n";
+
+ my $cond;
+ my $cond_line;
+ my $save_code;
+ my @code;
+ my $code_line;
+ my @out;
+ my $failed;
+
+ push @out,
+ "#define IM_ROUND_8(x) ((int)((x)+0.5))\n",
+ "#define IM_ROUND_double(x) (x)\n",
+ "#define IM_LIMIT_8(x) ((x) < 0 ? 0 : (x) > 255 ? 255 : (x))\n",
+ "#define IM_LIMIT_double(x) ((x) < 0.0 ? 0.0 : (x) > 1.0 ? 1.0 : (x))\n",
+ "#line 1 \"$src\"\n";
+ while (defined(my $line = <SRC>)) {
+ if ($line =~ /^\#code\s+(\S.+)$/) {
+ $save_code
+ and do { warn "$src:$code_line:Unclosed #code block\n"; ++$failed; };
+
+ $cond = $1;
+ $cond_line = $.;
+ $code_line = $. + 1;
+ $save_code = 1;
+ }
+ elsif ($line =~ /^\#code\s*$/) {
+ $save_code
+ and do { warn "$src:$code_line:Unclosed #code block\n"; ++$failed; };
+
+ $cond = '';
+ $cond_line = 0;
+ $code_line = $. + 1;
+ $save_code = 1;
+ }
+ elsif ($line =~ /^\#\/code$/) {
+ $save_code
+ or do { warn "$src:$.:#/code without #code\n"; ++$failed; next; };
+
+ if ($cond) {
+ push @out, "#line $cond_line \"$src\"\n";
+ push @out, " if ($cond) {\n";
+ }
+ push @out,
+ "#undef IM_EIGHT_BIT\n",
+ "#define IM_EIGHT_BIT 1\n",
+ "#undef IM_FILL_COMBINE\n",
+ "#define IM_FILL_COMBINE(fill) ((fill)->combine)\n",
+ "#undef IM_FILL_FILLER\n",
+ "#define IM_FILL_FILLER(fill) ((fill)->f_fill_with_color)\n";
+ push @out, "#line $code_line \"$src\"\n";
+ push @out, byte_samples(@code);
+ push @out, " }\n", " else {\n"
+ if $cond;
+ push @out,
+ "#undef IM_EIGHT_BIT\n",
+ "#undef IM_FILL_COMBINE\n",
+ "#define IM_FILL_COMBINE(fill) ((fill)->combinef)\n",
+ "#undef IM_FILL_FILLER\n",
+ "#define IM_FILL_FILLER(fill) ((fill)->f_fill_with_fcolor)\n";
+ push @out, "#line $code_line \"$src\"\n";
+ push @out, double_samples(@code);
+ push @out, " }\n"
+ if $cond;
+ push @out, "#line ",$.+1," \"$src\"\n";
+ @code = ();
+ $save_code = 0;
+ }
+ elsif ($save_code) {
+ push @code, $line;
+ }
+ else {
+ push @out, $line;
+ }
+ }
+
+ if ($save_code) {
+ warn "$src:$code_line:#code block not closed by EOF\n";
+ ++$failed;
+ }
+
+ close SRC;
+
+ $failed
+ and die "Errors during parsing, aborting\n";
+
+ open DEST, "> $dest"
+ or die "Cannot open $dest: $!\n";
+ print DEST @out;
+ close DEST;
+}
+
+sub byte_samples {
+ # important we make a copy
+ my @lines = @_;
+
+ for (@lines) {
+ s/\bIM_GPIX\b/i_gpix/g;
+ s/\bIM_GLIN\b/i_glin/g;
+ s/\bIM_PPIX\b/i_ppix/g;
+ s/\bIM_PLIN\b/i_plin/g;
+ s/\bIM_GSAMP\b/i_gsamp/g;
+ s/\bIM_SAMPLE_MAX\b/255/g;
+ s/\bIM_SAMPLE_MAX2\b/65025/g;
+ s/\bIM_SAMPLE_T/i_sample_t/g;
+ s/\bIM_COLOR\b/i_color/g;
+ s/\bIM_WORK_T\b/int/g;
+ s/\bIM_Sf\b/"%d"/g;
+ s/\bIM_Wf\b/"%d"/g;
+ s/\bIM_SUFFIX\((\w+)\)/$1_8/g;
+ s/\bIM_ROUND\(/IM_ROUND_8(/g;
+ s/\bIM_ADAPT_COLORS\(/i_adapt_colors(/g;
+ s/\bIM_LIMIT\(/IM_LIMIT_8(/g;
+ s/\bIM_RENDER_LINE\(/i_render_line(/g;
+ s/\bIM_FILL_COMBINE_F\b/i_fill_combine_f/g;
+ }
+
+ @lines;
+}
+
+sub double_samples {
+ # important we make a copy
+ my @lines = @_;
+
+ for (@lines) {
+ s/\bIM_GPIX\b/i_gpixf/g;
+ s/\bIM_GLIN\b/i_glinf/g;
+ s/\bIM_PPIX\b/i_ppixf/g;
+ s/\bIM_PLIN\b/i_plinf/g;
+ s/\bIM_GSAMP\b/i_gsampf/g;
+ s/\bIM_SAMPLE_MAX\b/1.0/g;
+ s/\bIM_SAMPLE_MAX2\b/1.0/g;
+ s/\bIM_SAMPLE_T/i_fsample_t/g;
+ s/\bIM_COLOR\b/i_fcolor/g;
+ s/\bIM_WORK_T\b/double/g;
+ s/\bIM_Sf\b/"%f"/g;
+ s/\bIM_Wf\b/"%f"/g;
+ s/\bIM_SUFFIX\((\w+)\)/$1_double/g;
+ s/\bIM_ROUND\(/IM_ROUND_double(/g;
+ s/\bIM_ADAPT_COLORS\(/i_adapt_fcolors(/g;
+ s/\bIM_LIMIT\(/IM_LIMIT_double(/g;
+ s/\bIM_RENDER_LINE\(/i_render_linef(/g;
+ s/\bIM_FILL_COMBINE_F\b/i_fill_combinef_f/g;
+ }
+
+ @lines;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Imager::Preprocess - simple preprocessor for handling multiple sample sizes
+
+=head1 SYNOPSIS
+
+ /* in the source: */
+ #code condition true to work with 8-bit samples
+ ... code using preprocessor types/values ...
+ #/code
+
+ perl -MImager -epreprocess foo.im foo.c
+
+=head1 DESCRIPTION
+
+This is a simple preprocessor that aims to reduce duplication of
+source code when implementing an algorithm both for 8-bit samples and
+double samples in Imager.
+
+Imager's Makefile.PL currently scans the MANIFEST for .im files and
+adds Makefile files to convert these to .c files.
+
+The beginning of a sample-independent section of code is preceded by:
+
+ #code expression
+
+where I<expression> should return true if processing should be done at
+8-bits/sample.
+
+You can also use a #code block around a function definition to produce
+8-bit and double sample versions of a function. In this case #code
+has no expression and you will need to use IM_SUFFIX() to produce
+different function names.
+
+The end of a sample-independent section of code is terminated by:
+
+ #/code
+
+#code sections cannot be nested.
+
+#/code without a starting #code is an error.
+
+The following types and values are defined in a #code section:
+
+=over
+
+=item *
+
+IM_GPIX(im, x, y, &col)
+
+=item *
+
+IM_GLIN(im, l, r, y, colors)
+
+=item *
+
+IM_PPIX(im, x, y, &col)
+
+=item *
+
+IM_PLIN(im, x, y, colors)
+
+=item *
+
+IM_GSAMP(im, l, r, y, samples, chans, chan_count)
+
+These correspond to the appropriate image function, eg. IM_GPIX()
+becomes i_gpix() or i_gpixf() as appropriate.
+
+=item *
+
+IM_ADAPT_COLORS(dest_channes, src_channels, colors, count)
+
+Call i_adapt_colors() or i_adapt_fcolors().
+
+=item *
+
+IM_FILL_COMBINE(fill) - retrieve the combine function from a fill
+object.
+
+=item *
+
+IM_FILL_FILLER(fill) - retrieve the fill_with_* function from a fill
+object.
+
+=item *
+
+IM_SAMPLE_MAX - maximum value for a sample
+
+=item *
+
+IM_SAMPLE_MAX2 - maximum value for a sample, squared
+
+=item *
+
+IM_SAMPLE_T - type of a sample (i_sample_t or i_fsample_t)
+
+=item *
+
+IM_COLOR - color type, either i_color or i_fcolor.
+
+=item *
+
+IM_WORK_T - working sample type, either int or double.
+
+=item *
+
+IM_Sf - format string for the sample type, C<"%d"> or C<"%f">.
+
+=item *
+
+IM_Wf - format string for the work type, C<"%d"> or C<"%f">.
+
+=item *
+
+IM_SUFFIX(identifier) - adds _8 or _double onto the end of identifier.
+
+=item *
+
+IM_EIGHT_BIT - this is a macro defined only in 8-bit/sample code.
+
+=back
+
+Other types, functions and values may be added in the future.
+
+=head1 AUTHOR
+
+Tony Cook <tony@imager.perl.org>
+
+=cut
src_minx=>20, src_miny=>30,
src_maxx=>20, src_maxy=>30);
+ $img->compose(src => $src, tx => 30, ty => 20, combine => 'color');
+ $img->compose(src => $src, tx => 30, ty => 20, combine => 'color');
+ mask => $mask, opacity => 0.5);
$img->flip(dir=>"h"); # horizontal flip
$img->flip(dir=>"vh"); # vertical and horizontal flip
Either they take an existing image and modify it in place, or they
return a modified copy.
-Functions that modify inplace are C<flip()>, C<paste()> and
-C<rubthrough()>. If the original is to be left intact it's possible
-to make a copy and alter the copy:
+Functions that modify inplace are C<flip()>, C<paste()>,
+C<rubthrough()> and C<compose()>. If the original is to be left
+intact it's possible to make a copy and alter the copy:
$flipped = $img->copy()->flip(dir=>'h');
left => 10, top => 10,
src_minx => 20, src_miny => 20,
src_maxx => 40, src_maxx => 40);
-
+
+If the source image has an alpha channel and the target doesn't, then
+the source is treated as if composed onto a black background.
+
+If the source image is color and the target is grayscale, the the
+source is treated as if run through C< convert(preset=>'gray') >.
+
=item rubthrough
A more complicated way of blending images is where one image is
rubthrough() returns true on success. On failure check
$target->errstr for the reason for failure.
+=item compose
+
+Draws the source image over the target image, with the src alpha
+channel modified by the optional mask and the opacity.
+
+ $img->compose(src=>$overlay,
+ tx=>30, ty=>50,
+ src_minx=>20, src_miny=>30,
+ src_maxx=>20, src_maxy=>30,
+ mask => $mask, opacity => 0.5);
+
+That will take the sub image defined by I<$overlay> and
+I<[src_minx,src_maxx)[src_miny,src_maxy)> and overlay it on top of
+I<$img> with the upper left corner at (30,50). You can rub 2 or 4
+channel images onto a 3 channel image, or a 2 channel image onto a 1
+channel image.
+
+Parameters:
+
+=over
+
+=item *
+
+src - the source image to draw onto the target. Required.
+
+=item *
+
+tx, ty - location in the the target image ($self) to render the top
+left corner of the source. These can also be supplied as C<left> and
+C<right>. Default: (0, 0).
+
+=item *
+
+src_minx, src_miny - the top left corner in the source to transfer to
+the target image. Default: (0, 0).
+
+=item *
+
+src_maxx, src_maxy - the bottom right in the source image of the sub
+image to overlay. This position is B<non> inclusive. Default: bottom
+right corner of the source image.
+
+=item *
+
+mask - a mask image. The first channel of this image is used to
+modify the alpha channel of the source image. This can me used to
+mask out portions of the source image. Where the first channel is
+zero none of the source image will be used, where the first channel is
+max the full alpha of the source image will be used, as further
+modified by the opacity.
+
+=item *
+
+opacity - further modifies the alpha channel of the source image, in
+the range 0.0 to 1.0. Default: 1.0.
+
+=item *
+
+combine - the method to combine the source pixels with the target.
+See the combine option documentation in Imager::Fill. Default:
+normal.
+
+=back
+
+Calling compose() with no mask, combine set to C<normal>, opacity set
+to C<1.0> is equivalent to calling rubthrough().
+
+compose() is intended to be produce similar effects to layers in
+interactive paint software.
+
+ # overlay all of $source onto $targ
+ $targ->compose(tx => 20, ty => 25, src => $source);
+
+ # overlay the top left corner of $source onto $targ
+ $targ->compose(tx => 20, ty => 25, src => $source,
+ src_maxx => 20, src_maxy => 20);
+
+ # overlay the bottom right corner of $source onto $targ
+ $targ->compose(tx => 20, ty => 30, src => $src,
+ src_minx => $src->getwidth() - 20,
+ src_miny => $src->getheight() - 20);
+
+compose() returns true on success. On failure check $target->errstr
+for the reason for failure.
+
=item flip
An inplace horizontal or vertical flip is possible by calling the
--- /dev/null
+#include "imager.h"
+
+/*
+=item i_copyto(dest, src, x1, y1, x2, y2, tx, ty)
+
+=category Image
+
+Copies image data from the area (x1,y1)-[x2,y2] in the source image to
+a rectangle the same size with it's top-left corner at (tx,ty) in the
+destination image.
+
+If x1 > x2 or y1 > y2 then the corresponding co-ordinates are swapped.
+
+=cut
+*/
+
+void
+i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty) {
+ int x, y, t, ttx, tty;
+
+ if (x2<x1) { t=x1; x1=x2; x2=t; }
+ if (y2<y1) { t=y1; y1=y2; y2=t; }
+ if (tx < 0) {
+ /* adjust everything equally */
+ x1 += -tx;
+ x2 += -tx;
+ tx = 0;
+ }
+ if (ty < 0) {
+ y1 += -ty;
+ y2 += -ty;
+ ty = 0;
+ }
+ if (x1 >= src->xsize || y1 >= src->ysize)
+ return; /* nothing to do */
+ if (x2 > src->xsize)
+ x2 = src->xsize;
+ if (y2 > src->ysize)
+ y2 = src->ysize;
+ if (x1 == x2 || y1 == y2)
+ return; /* nothing to do */
+
+ mm_log((1,"i_copyto(im* %p, src %p, x1 %d, y1 %d, x2 %d, y2 %d, tx %d, ty %d)\n",
+ im, src, x1, y1, x2, y2, tx, ty));
+
+#code im->bits == i_8_bits
+ IM_COLOR *row = mymalloc(sizeof(IM_COLOR) * (x2-x1));
+ tty = ty;
+ for(y=y1; y<y2; y++) {
+ ttx = tx;
+ IM_GLIN(src, x1, x2, y, row);
+ if (src->channels != im->channels)
+ IM_ADAPT_COLORS(im->channels, src->channels, row, x2-x1);
+ IM_PLIN(im, tx, tx+x2-x1, tty, row);
+ tty++;
+ }
+ myfree(row);
+#/code
+}
+
+#define color_to_grey(col) ((col)->rgb.r * 0.222 + (col)->rgb.g * 0.707 + (col)->rgb.b * 0.071)
+
+#code
+void
+#ifdef IM_EIGHT_BIT
+i_adapt_colors
+#else
+i_adapt_fcolors
+#endif
+(int out_channels, int in_channels, IM_COLOR *colors,
+ size_t count) {
+ int i;
+ if (out_channels == in_channels)
+ return;
+ if (count == 0)
+ return;
+
+ switch (out_channels) {
+ case 1:
+ {
+ switch (in_channels) {
+ case 2:
+ /* apply alpha against a black background */
+ while (count) {
+ colors->channel[0] = colors->channel[0] * colors->channel[1] / IM_SAMPLE_MAX;
+ ++colors;
+ --count;
+ }
+ return;
+
+ case 3:
+ /* convert to grey */
+ while (count) {
+ colors->channel[0] = IM_ROUND(color_to_grey(colors));
+ ++colors;
+ --count;
+ }
+ return;
+
+ case 4:
+ while (count) {
+ colors->channel[0] = IM_ROUND(color_to_grey(colors) * colors->channel[3] / IM_SAMPLE_MAX);
+ ++colors;
+ --count;
+ }
+ return;
+
+ default:
+ i_fatal(3, "i_adapt_colors: in_channels of %d invalid\n", in_channels);
+ return; /* avoid warnings */
+ }
+ }
+
+ case 2:
+ {
+ switch (in_channels) {
+ case 1:
+ while (count) {
+ colors->channel[1] = IM_SAMPLE_MAX;
+ ++colors;
+ --count;
+ }
+ return;
+
+ case 3:
+ while (count) {
+ colors->channel[0] = IM_ROUND(color_to_grey(colors));
+ colors->channel[1] = IM_SAMPLE_MAX;
+ ++colors;
+ --count;
+ }
+ return;
+
+ case 4:
+ while (count) {
+ colors->channel[0] = IM_ROUND(color_to_grey(colors));
+ colors->channel[1] = colors->channel[3];
+ ++colors;
+ --count;
+ }
+ return;
+
+ default:
+ i_fatal(3, "i_adapt_colors: in_channels of %d invalid\n", in_channels);
+ return; /* avoid warnings */
+ }
+ }
+
+ case 3:
+ {
+ switch (in_channels) {
+ case 1:
+ while (count) {
+ colors->channel[1] = colors->channel[2] = colors->channel[0];
+ ++colors;
+ --count;
+ }
+ return;
+
+ case 2:
+ while (count) {
+ int alpha = colors->channel[1];
+ colors->channel[0] = colors->channel[1] = colors->channel[2] =
+ IM_ROUND(colors->channel[0] * alpha / IM_SAMPLE_MAX);
+ ++colors;
+ --count;
+ }
+ return;
+
+ case 4:
+ while (count) {
+ int alpha = colors->channel[3];
+ colors->channel[0] =
+ IM_ROUND(colors->channel[0] * alpha / IM_SAMPLE_MAX);
+ colors->channel[1] =
+ IM_ROUND(colors->channel[1] * alpha / IM_SAMPLE_MAX);
+ colors->channel[2] =
+ IM_ROUND(colors->channel[2] * alpha / IM_SAMPLE_MAX);
+ ++colors;
+ --count;
+ }
+ return;
+
+ default:
+ i_fatal(3, "i_adapt_colors: in_channels of %d invalid\n", in_channels);
+ return; /* avoid warnings */
+ }
+ }
+
+ case 4:
+ {
+ switch (in_channels) {
+ case 1:
+ while (count) {
+ colors->channel[1] = colors->channel[2] = colors->channel[0];
+ colors->channel[3] = IM_SAMPLE_MAX;
+ ++colors;
+ --count;
+ }
+ return;
+
+ case 2:
+ while (count) {
+ colors->channel[3] = colors->channel[1];
+ colors->channel[1] = colors->channel[2] = colors->channel[0];
+ ++colors;
+ --count;
+ }
+ return;
+
+ case 3:
+ while (count) {
+ colors->channel[3] = IM_SAMPLE_MAX;
+ ++colors;
+ --count;
+ }
+ return;
+
+ default:
+ i_fatal(3, "i_adapt_colors: in_channels of %d invalid\n", in_channels);
+ return; /* avoid warnings */
+ }
+ }
+
+ default:
+ i_fatal(3, "i_adapt_colors: out_channels of %d invalid\n", out_channels);
+ return; /* avoid warnings */
+ }
+}
+
+#/code
+
#include "imager.h"
#include "draw.h"
#include "log.h"
-
+#include "imrender.h"
#define IMTRUNC(x) ((int)((x)*16))
i_poly_aa_low(im, l, x, y, val, scanline_flush);
}
+#if 0
struct poly_cfill_state {
i_color *fillbuf;
i_color *linebuf;
i_plinf(im, left, right, y, line);
}
}
+#endif
+
+struct poly_render_state {
+ i_render render;
+ i_fill_t *fill;
+ unsigned char *cover;
+};
+
+static void
+scanline_flush_render(i_img *im, ss_scanline *ss, int y, const void *ctx) {
+ int x, ch, tv;
+ int pos;
+ int left, right;
+ struct poly_render_state const *state = (struct poly_render_state const *)ctx;
+
+ left = 0;
+ while (left < im->xsize && ss->line[left] <= 0)
+ ++left;
+ if (left < im->xsize) {
+ right = im->xsize;
+ /* since going from the left found something, moving from the
+ right should */
+ while (/* right > left && */ ss->line[right-1] <= 0)
+ --right;
+
+ /* convert to the format the render interface wants */
+ for (x = left; x < right; ++x) {
+ state->cover[x-left] = saturate(ss->line[x]);
+ }
+ i_render_fill(&state->render, left, y, right-left, state->cover,
+ state->fill);
+ }
+}
void
-i_poly_aa_cfill(i_img *im, int l, const double *x, const double *y, i_fill_t *fill) {
+i_poly_aa_cfill(i_img *im, int l, const double *x, const double *y,
+ i_fill_t *fill) {
+ struct poly_render_state ctx;
+
+ i_render_init(&ctx.render, im, im->xsize);
+ ctx.fill = fill;
+ ctx.cover = mymalloc(im->xsize);
+ i_poly_aa_low(im, l, x, y, &ctx, scanline_flush_render);
+ myfree(ctx.cover);
+ i_render_done(&ctx.render);
+#if 0
if (im->bits == i_8_bits && fill->fill_with_color) {
struct poly_cfill_state ctx;
ctx.fillbuf = mymalloc(sizeof(i_color) * im->xsize * 2);
myfree(ctx.fillbuf);
myfree(ctx.cover);
}
+#endif
}
typedef void (*render_color_f)(i_render *, int, int, int, 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);
IM_SUFFIX(render_color_alpha),
};
+static void IM_SUFFIX(combine_line_noalpha)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
+static void IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in, int channels, int 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, int count);
+
+static void IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
+static void IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
+
#/code
void
i_render_init(i_render *r, i_img *im, int 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, int width, int eight_bit) {
+ if (width > r->line_width) {
+ int 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, int width, int eight_bit) {
+ if (width > r->fill_width) {
+ int 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;
+ }
+ }
+ }
+}
+
void
i_render_color(i_render *r, int x, int y, int width, unsigned char const *src,
i_color const *color) {
if (!width)
return;
+#if 0
+ /* 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;
+
+ if (r->im->bits <= 8) {
+ 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->width = new_width;
+ }
+#else
+ alloc_line(r, width, r->im->bits <= 8);
+#endif
+
+#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
+}
+
+void
+i_render_fill(i_render *r, int x, int y, int 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;
+
+ 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;
+
+#if 0
+ if (r->im->bits <= 8 && fill->f_fill_with_color) {
+ if (!r->line_8)
+ r->line_8 = mymalloc(sizeof(i_color) * r->width);
+ if (!r->fill_line_8)
+ r->fill_line_8 = mymalloc(sizeof(i_color) * r->width);
+ }
+ else {
+ if (!r->line_double)
+ r->line_double = mymalloc(sizeof(i_fcolor) * r->width);
+ if (!r->fill_line_double)
+ r->fill_line_double = mymalloc(sizeof(i_fcolor) * r->width);
+ }
+
/* 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;
- if (r->line_8)
+ if (r->im->bits <= 8 && fill->f_fill_with_color) {
r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
- else
+ r->fill_line_8 = myrealloc(r->fill_line_8, sizeof(i_color) * new_width);
+ if (r->line_double) {
+ myfree(r->line_double);
+ r->line_double = NULL;
+ }
+ if (r->fill_line_double) {
+ myfree(r->fill_line_double);
+ r->fill_line_double = NULL;
+ }
+ }
+ else {
r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
+ r->fill_line_double = myrealloc(r->fill_line_double, sizeof(i_fcolor) * new_width);
+ if (r->line_8) {
+ myfree(r->line_8);
+ r->line_8 = NULL;
+ }
+ if (r->fill_line_8) {
+ myfree(r->fill_line_8);
+ r->fill_line_8 = NULL;
+ }
+ }
+ r->width = new_width;
}
+#else
+ 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);
+#endif
-#code r->im->bits <= 8
- (IM_SUFFIX(render_color_tab)[im->channels])(r, x, y, width, src, color);
+#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);
+ int 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) {
+ int 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] * (IM_SAMPLE_MAX - *src)
+ + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
+ destc->channel[ch] = IM_LIMIT(work);
+ }
+ }
+
+ ++srcc;
+ ++destc;
+ ++src;
+ --work_width;
+ }
+ }
+ else { /* if (src) */
+ IM_FILL_FILLER(fill)(fill, x, y, width, r->im->channels, r->IM_SUFFIX(line));
+ }
+ }
+ IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
#/code
}
#code
+void
+IM_RENDER_LINE(i_render *r, int x, int y, int 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) {
+ int 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) {
+ int 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,
++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, int 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, int 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, int 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, int 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, int 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, int);
+static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, int);
+
+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, int 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, int count) {
+ int ch;
+ IM_COLOR *inp = in;
+ IM_COLOR *outp = out;
+ int 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, int 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, int count) {
+ int ch;
+ int color_channels = i_color_channels(channels);
+ int 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, int count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ int 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, int count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ int 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,
+ int count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ int 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, int count) {
+ int ch;
+ IM_COLOR const *inp = in;
+ IM_COLOR *outp = out;
+ int 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, int count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ int 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, int count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ int 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, int count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ int 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, int count) {
+ if (channels > 2) {
+ IM_COLOR *inp = in;
+ IM_COLOR const *outp = out;
+ int 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
typedef struct {
int magic;
i_img *im;
+
+ int line_width;
i_color *line_8;
i_fcolor *line_double;
- int width;
+
+ int fill_width;
+ i_color *fill_line_8;
+ i_fcolor *fill_line_double;
} i_render;
#endif
int src_minx, int src_miny,
int src_maxx, int src_maxy) {
int x, y, ttx, tty;
- int chancount;
- int chans[3];
int alphachan;
int ch;
+ int width = src_maxx - src_minx;
+ int want_channels;
i_clear_error();
- if (im->channels == 3 && src->channels == 4) {
- chancount = 3;
- chans[0] = 0; chans[1] = 1; chans[2] = 2;
+ if (im->channels == 3 && (src->channels == 4 || src->channels == 2)) {
+ want_channels = 4;
alphachan = 3;
}
- else if (im->channels == 3 && src->channels == 2) {
- chancount = 3;
- chans[0] = chans[1] = chans[2] = 0;
- alphachan = 1;
- }
- else if (im->channels == 1 && src->channels == 2) {
- chancount = 1;
- chans[0] = 0;
+ else if (im->channels == 1 && (src->channels == 4 || src->channels == 2)) {
+ want_channels = 2;
alphachan = 1;
}
else {
- i_push_error(0, "rubthru can only work where (dest, src) channels are (3,4), (4,4), (3,2), (4,2), (1,2) or (2,2)");
- return 0;
+ i_copyto(im, src, src_minx, src_miny, src_maxx, src_maxy, tx, ty);
+ return 1;
}
#code im->bits <= 8 && src->bits <= 8
IM_WORK_T alpha;
- IM_COLOR pv, orig, dest;
+ IM_COLOR *src_line, *dest_line;
+
+ src_line = mymalloc(sizeof(IM_COLOR) * width);
+ dest_line = mymalloc(sizeof(IM_COLOR) * width);
tty = ty;
for(y = src_miny; y < src_maxy; y++) {
+ IM_COLOR *srcp = src_line;
+ IM_COLOR *destp = dest_line;
ttx = tx;
+ IM_GLIN(src, src_minx, src_maxx, y, src_line);
+ IM_GLIN(im, tx, tx + width, tty, dest_line);
+ if (src->channels != want_channels)
+ IM_ADAPT_COLORS(want_channels, src->channels, src_line, width);
+
for(x = src_minx; x < src_maxx; x++) {
- IM_GPIX(src, x, y, &pv);
- IM_GPIX(im, ttx, tty, &orig);
- alpha = pv.channel[alphachan];
- for (ch = 0; ch < chancount; ++ch) {
- dest.channel[ch] = (alpha * pv.channel[chans[ch]]
- + (IM_SAMPLE_MAX - alpha) * orig.channel[ch])/IM_SAMPLE_MAX;
+ alpha = srcp->channel[alphachan];
+ for (ch = 0; ch < im->channels; ++ch) {
+ IM_WORK_T samp = (alpha * srcp->channel[ch]
+ + (IM_SAMPLE_MAX - alpha) * destp->channel[ch])/IM_SAMPLE_MAX;
+ destp->channel[ch] = IM_LIMIT(samp);
}
- IM_PPIX(im, ttx, tty, &dest);
- ttx++;
+ ++srcp;
+ ++destp;
+ ttx;
}
+ IM_PLIN(im, tx, tx + width, tty, dest_line);
tty++;
}
+ myfree(src_line);
+ myfree(dest_line);
#/code
+
return 1;
}
int src_minx, int src_miny,
int src_maxx, int src_maxy) {
int x, y, ttx, tty;
- int chancount;
- int chans[3];
+ int want_channels;
int alphachan;
int ch;
int targ_alpha_chan;
+ int width = src_maxx - src_minx;
- if (im->channels == 4 && src->channels == 4) {
- chancount = 3;
- chans[0] = 0; chans[1] = 1; chans[2] = 2;
+ if (im->channels == 4 && (src->channels == 4 || src->channels == 2)) {
alphachan = 3;
+ want_channels = 4;
}
- else if (im->channels == 4 && src->channels == 2) {
- chancount = 3;
- chans[0] = chans[1] = chans[2] = 0;
- alphachan = 1;
- }
- else if (im->channels == 2 && src->channels == 2) {
- chancount = 1;
- chans[0] = 0;
+ else if (im->channels == 2 && (src->channels == 4 || src->channels == 2)) {
alphachan = 1;
+ want_channels = 2;
}
else {
- i_push_error(0, "rubthru can only work where (dest, src) channels are (3,4), (4,4), (3,2), (4,2), (1,2) or (2,2)");
- return 0;
+ i_copyto(im, src, src_minx, src_miny, src_maxx, src_maxy, tx, ty);
+ return 1;
}
targ_alpha_chan = im->channels - 1;
#code im->bits <= 8 && src->bits <= 8
IM_WORK_T src_alpha, orig_alpha, dest_alpha, remains;
- IM_COLOR pv, orig, dest;
+ IM_COLOR *src_line, *dest_line;
+
+ src_line = mymalloc(sizeof(IM_COLOR) * width);
+ dest_line = mymalloc(sizeof(IM_COLOR) * width);
tty = ty;
for(y = src_miny; y < src_maxy; y++) {
- ttx = tx;
- for(x = src_minx; x < src_maxx; x++) {
- IM_GPIX(src, x, y, &pv);
- src_alpha = pv.channel[alphachan];
- if (src_alpha) {
- remains = IM_SAMPLE_MAX - src_alpha;
- IM_GPIX(im, ttx, tty, &orig);
- orig_alpha = orig.channel[targ_alpha_chan];
- dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
-
- for (ch = 0; ch < chancount; ++ch) {
- dest.channel[ch] =
- ( src_alpha * pv.channel[chans[ch]]
- + remains * orig.channel[ch] * orig_alpha / IM_SAMPLE_MAX
- ) / dest_alpha;
- }
- /* dest's alpha */
- dest.channel[targ_alpha_chan] = dest_alpha;
- IM_PPIX(im, ttx, tty, &dest);
+ int min_x, max_x;
+ IM_COLOR *srcp = src_line;
+ IM_COLOR *destp = dest_line;
+ IM_GLIN(src, src_minx, src_maxx, y, src_line);
+ if (src->channels != want_channels)
+ IM_ADAPT_COLORS(want_channels, src->channels, src_line, width);
+ min_x = src_minx;
+ max_x = src_maxx;
+
+ while (min_x < max_x && srcp->channel[alphachan] == 0) {
+ ++min_x;
+ ++srcp;
+ }
+ while (max_x > min_x && src_line[max_x-1].channel[alphachan] == 0) {
+ --max_x;
+ }
+
+ if (max_x > min_x) {
+ int work_left = tx + min_x - src_minx;
+ int work_width = max_x - min_x;
+ ttx = work_left;
+ IM_GLIN(im, work_left, work_left + work_width, tty, dest_line);
+
+ for(x = src_minx; x < src_maxx; x++) {
+ src_alpha = srcp->channel[alphachan];
+ if (src_alpha) {
+ remains = IM_SAMPLE_MAX - src_alpha;
+ orig_alpha = destp->channel[targ_alpha_chan];
+ dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
+
+ for (ch = 0; ch < im->channels-1; ++ch) {
+ IM_WORK_T samp =
+ ( src_alpha * srcp->channel[ch]
+ + remains * destp->channel[ch] * orig_alpha / IM_SAMPLE_MAX
+ ) / dest_alpha;
+ destp->channel[ch] = IM_LIMIT(samp);
+ }
+ /* dest's alpha */
+ destp->channel[targ_alpha_chan] = dest_alpha;
+ }
+ ++srcp;
+ ++destp;
+ ttx++;
}
- ttx++;
+ IM_PLIN(im, work_left, work_left + work_width, tty, dest_line);
}
tty++;
}
+ myfree(dest_line);
+ myfree(src_line);
#/code
return 1;
}
int
i_rubthru(i_img *im, i_img *src, int tx, int ty, int src_minx, int src_miny,
int src_maxx, int src_maxy) {
+ if (src_minx < 0) {
+ tx -= src_minx;
+ src_minx = 0;
+ }
+ if (src_miny < 0) {
+ ty -= src_miny;
+ src_miny = 0;
+ }
+ if (tx < 0) {
+ src_minx -= tx;
+ tx = 0;
+ }
+ if (ty < 0) {
+ src_miny -= ty;
+ ty = 0;
+ }
+ if (src_maxx > src->xsize) {
+ src_maxx = src->xsize;
+ }
+ if (src_maxy > src->ysize) {
+ src_maxy = src->ysize;
+ }
+ if (tx >= im->xsize || ty >= im->ysize
+ || src_minx >= src_maxx || src_miny >= src_maxy) {
+ i_clear_error();
+ i_push_error(0, "rubthrough: nothing to do");
+ return 0;
+ }
+
if (im->channels == 1 || im->channels == 3)
return rubthru_targ_noalpha(im, src, tx, ty, src_minx, src_miny,
src_maxx, src_maxy);
#!perl -w
use strict;
-use Test::More tests => 56;
+use Test::More tests => 121;
use Imager ':handy';
use Imager::Fill;
# test combining modes
my $fill = NC(192, 128, 128, 128);
my $target = NC(64, 32, 64);
+my $trans_target = NC(64, 32, 64, 128);
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 },
+ none=>
+ {
+ opaque => $fill,
+ trans => $fill,
+ },
+ normal=>
+ {
+ opaque => NC(128, 80, 96),
+ trans => NC(150, 96, 107, 191),
+ },
+ multiply =>
+ {
+ opaque => NC(56, 24, 48),
+ trans => NC(101, 58, 74, 192),
+ },
+ dissolve =>
+ {
+ opaque => [ $target, NC(192, 128, 128, 255) ],
+ trans => [ $trans_target, NC(192, 128, 128, 255) ],
+ },
+ add =>
+ {
+ opaque => NC(159, 96, 128),
+ trans => NC(128, 80, 96, 255),
+ },
+ subtract =>
+ {
+ opaque => NC(0, 0, 0),
+ trans => NC(0, 0, 0, 255),
+ },
+ diff =>
+ {
+ opaque => NC(96, 64, 64),
+ trans => NC(127, 85, 85, 192),
+ },
+ lighten =>
+ {
+ opaque => NC(128, 80, 96),
+ trans => NC(149, 95, 106, 192),
+ },
+ darken =>
+ {
+ opaque => $target,
+ trans => NC(106, 63, 85, 192),
+ },
# 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, 47) },
- saturation => { result=>NC(63, 37, 64) },
- value => { result=>NC(127, 64, 128) },
- color => { result=>NC(64, 37, 52) },
+ hue =>
+ {
+ opaque => NC(64, 32, 47),
+ trans => NC(64, 32, 42, 128),
+ },
+ saturation =>
+ {
+ opaque => NC(63, 37, 64),
+ trans => NC(64, 39, 64, 128),
+ },
+ value =>
+ {
+ opaque => NC(127, 64, 128),
+ trans => NC(149, 75, 150, 128),
+ },
+ color =>
+ {
+ opaque => NC(64, 37, 52),
+ trans => NC(64, 39, 50, 128),
+ },
);
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(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(color_close($c, $test->{result}), "combine '$comb'")
- or print "# got: ",join(",", $c->rgba),
- " allowed: ",join(",", $test->{result}->rgba),"\n";
+
+ for my $bits (qw(8 double)) {
+ {
+ my $targim = Imager->new(xsize=>4, ysize=>4, bits => $bits);
+ $targim->box(filled=>1, color=>$target);
+ $targim->box(fill=>$fillobj);
+ my $c = Imager::i_get_pixel($targim->{IMG}, 1, 1);
+ my $allowed = $test->{opaque};
+ $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
+ ok(scalar grep(color_close($_, $c), @$allowed),
+ "opaque '$comb' $bits bits")
+ or print "# got:",join(",", $c->rgba)," allowed: ",
+ join("|", map { join(",", $_->rgba) } @$allowed),"\n";
+ }
+
+ {
+ # make sure the alpha path in the combine function produces the same
+ # or at least as sane a result as the non-alpha path
+ my $targim = Imager->new(xsize=>4, ysize=>4, channels => 4, bits => $bits);
+ $targim->box(filled=>1, color=>$target);
+ $targim->box(fill=>$fillobj);
+ my $c = Imager::i_get_pixel($targim->{IMG}, 1, 1);
+ my $allowed = $test->{opaque};
+ $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
+ ok(scalar grep(color_close4($_, $c), @$allowed),
+ "opaque '$comb' 4-channel $bits bits")
+ or print "# got:",join(",", $c->rgba)," allowed: ",
+ join("|", map { join(",", $_->rgba) } @$allowed),"\n";
+ }
+
+ {
+ my $transim = Imager->new(xsize => 4, ysize => 4, channels => 4, bits => $bits);
+ $transim->box(filled=>1, color=>$trans_target);
+ $transim->box(fill => $fillobj);
+ my $c = $transim->getpixel(x => 1, 'y' => 1);
+ my $allowed = $test->{trans};
+ $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
+ ok(scalar grep(color_close4($_, $c), @$allowed),
+ "translucent '$comb' $bits bits")
+ or print "# got:",join(",", $c->rgba)," allowed: ",
+ join("|", map { join(",", $_->rgba) } @$allowed),"\n";
+ }
}
}
return 1;
}
+sub color_close4 {
+ my ($c1, $c2) = @_;
+
+ my @c1 = $c1->rgba;
+ my @c2 = $c2->rgba;
+
+ for my $i (0..3) {
+ if (abs($c1[$i]-$c2[$i]) > 2) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
# for use during testing
sub save {
my ($im, $name) = @_;
#!perl -w
use strict;
-use Test::More tests => 23;
+use Test::More tests => 54;
-BEGIN { use_ok("Imager") }
+use Imager;
+use Imager::Test qw(is_image);
#$Imager::DEBUG=1;
"check pasted correctly");
}
}
+
+{ # https://rt.cpan.org/Ticket/Display.html?id=30908
+ # we now adapt the source channels to the target
+ # check each combination works as expected
+
+ # various source images
+ my $src1 = Imager->new(xsize => 50, ysize => 50, channels => 1);
+ my $g_grey_full = Imager::Color->new(128, 255, 0, 0);
+ my $g_white_50 = Imager::Color->new(255, 128, 0, 0);
+ $src1->box(filled => 1, xmax => 24, color => $g_grey_full);
+
+ my $src2 = Imager->new(xsize => 50, ysize => 50, channels => 2);
+ $src2->box(filled => 1, xmax => 24, color => $g_grey_full);
+ $src2->box(filled => 1, xmin => 25, color => $g_white_50);
+
+ my $c_red_full = Imager::Color->new(255, 0, 0);
+ my $c_blue_full = Imager::Color->new(0, 0, 255);
+ my $src3 = Imager->new(xsize => 50, ysize => 50, channels => 3);
+ $src3->box(filled => 1, xmax => 24, color => $c_red_full);
+ $src3->box(filled => 1, xmin => 25, color => $c_blue_full);
+
+ my $c_green_50 = Imager::Color->new(0, 255, 0, 127);
+ my $src4 = Imager->new(xsize => 50, ysize => 50, channels => 4);
+ $src4->box(filled => 1, xmax => 24, color => $c_blue_full);
+ $src4->box(filled => 1, xmin => 25, color => $c_green_50);
+
+ my @left_box = ( box => [ 25, 25, 49, 74 ] );
+ my @right_box = ( box => [ 50, 25, 74, 74 ] );
+
+ { # 1 channel output
+ my $base = Imager->new(xsize => 100, ysize => 100, channels => 1);
+ $base->box(filled => 1, color => Imager::Color->new(64, 255, 0, 0));
+
+ my $work = $base->copy;
+ ok($work->paste(left => 25, top => 25, src => $src1), "paste 1 to 1");
+ my $comp = $base->copy;
+ $comp->box(filled => 1, color => $g_grey_full, @left_box);
+ $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(left => 25, top => 25, src => $src2), "paste 2 to 1");
+ $comp = $base->copy;
+ $comp->box(filled => 1, @left_box, color => $g_grey_full);
+ $comp->box(filled => 1, @right_box, color => [ 128, 0, 0, 0 ]);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(left => 25, top => 25, src => $src3), "paste 3 to 1");
+ $comp = $base->copy;
+ $comp->box(filled => 1, @left_box, color => [ 57, 255, 0, 0 ]);
+ $comp->box(filled => 1, @right_box, color => [ 18, 255, 0, 0 ]);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(left => 25, top => 25, src => $src4), "paste 4 to 1");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
+ $comp->box(filled => 1, color => [ 90, 255, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+ }
+
+ { # 2 channel output
+ my $base = Imager->new(xsize => 100, ysize => 100, channels => 2);
+ $base->box(filled => 1, color => [ 128, 128, 0, 0 ]);
+
+ my $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src1), "paste 1 to 2");
+ my $comp = $base->copy;
+ $comp->box(filled => 1, color => $g_grey_full, @left_box);
+ $comp->box(filled => 1, color => [ 0, 255, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src2), "paste 2 to 2");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => $g_grey_full, @left_box);
+ $comp->box(filled => 1, color => $g_white_50, @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src3), "paste 3 to 2");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 57, 255, 0, 0 ], @left_box);
+ $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src4), "paste 4 to 2");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
+ $comp->box(filled => 1, color => [ 180, 127, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+ }
+
+ { # 3 channel output
+ my $base = Imager->new(xsize => 100, ysize => 100, channels => 3);
+ $base->box(filled => 1, color => [ 128, 255, 0, 0 ]);
+
+ my $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src1), "paste 1 to 3");
+ my $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+ $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src2), "paste 2 to 3");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+ $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src3), "paste 3 to 3");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
+ $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src4), "paste 4 to 3");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 0, 0, 255 ], @left_box);
+ $comp->box(filled => 1, color => [ 0, 127, 0 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+ }
+
+ { # 4 channel output
+ my $base = Imager->new(xsize => 100, ysize => 100, channels => 4);
+ $base->box(filled => 1, color => [ 128, 255, 64, 128 ]);
+
+ my $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src1), "paste 1 to 4");
+ my $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+ $comp->box(filled => 1, color => [ 0, 0, 0, 255 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src2), "paste 2 to 4");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+ $comp->box(filled => 1, color => [ 255, 255, 255, 128 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src3), "paste 3 to 4");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
+ $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+
+ $work = $base->copy;
+ ok($work->paste(top => 25, left => 25, src => $src4), "paste 4 to 4");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => $c_blue_full, @left_box);
+ $comp->box(filled => 1, color => $c_green_50, @right_box);
+ is_image($work, $comp, "compare paste target to expected");
+ }
+}
#!perl -w
use strict;
-use Test::More tests => 38;
-BEGIN { use_ok(Imager => qw(:all :handy)); }
+use Test::More tests => 76;
+use Imager qw(:all :handy);
+use Imager::Test qw(is_image);
init_log("testout/t69rubthru.log", 1);
is((Imager::i_get_pixel($gtarg, 30, 30)->rgba)[0], 128,
"check grey level at 30, 30");
-# an attempt rub a 4 channel image over 1 channel should fail
-ok(!i_rubthru($gtarg, $src, 10, 10, 0, 0, $src_width, $src_height),
- "check failure of 4 channel over 1 channel image");
-
# simple test for 16-bit/sample images
my $targ16 = Imager::i_img_16_new(100, 100, 3);
ok(i_rubthru($targ16, $src, 10, 10, 0, 0, $src_width, $src_height),
ok(color_cmp(Imager::i_get_pixel($ootarg->{IMG}, 30, 30), NC(128, 0, 0)) == 0,
"check pixel at 30, 30");
-# make sure we fail as expected
my $oogtarg = Imager->new(xsize=>100, ysize=>100, channels=>1);
-ok(!$oogtarg->rubthrough(src=>$oosrc), "check oo fails correctly");
-
-is($oogtarg->errstr,
- 'rubthru can only work where (dest, src) channels are (3,4), (4,4), (3,2), (4,2), (1,2) or (2,2)',
- "check error message");
{ # check empty image errors
my $empty = Imager->new;
{
# alpha source and target
- my $src = Imager->new(xsize => 10, ysize => 1, channels => 4);
- my $targ = Imager->new(xsize => 10, ysize => 2, channels => 4);
-
- # simple initialization
- $targ->setscanline('y' => 1, x => 1,
- pixels =>
- [
- NC(255, 128, 0, 255),
- NC(255, 128, 0, 128),
- NC(255, 128, 0, 0),
- NC(255, 128, 0, 255),
- NC(255, 128, 0, 128),
- NC(255, 128, 0, 0),
- NC(255, 128, 0, 255),
- NC(255, 128, 0, 128),
- NC(255, 128, 0, 0),
- ]);
- $src->setscanline('y' => 0,
- pixels =>
- [
- NC(0, 128, 255, 0),
- NC(0, 128, 255, 0),
- NC(0, 128, 255, 0),
- NC(0, 128, 255, 128),
- NC(0, 128, 255, 128),
- NC(0, 128, 255, 128),
- NC(0, 128, 255, 255),
- NC(0, 128, 255, 255),
- NC(0, 128, 255, 255),
- ]);
- ok($targ->rubthrough(src => $src,
- tx => 1, ty => 1), "do 4 on 4 rubthrough");
- iscolora($targ->getpixel(x => 1, y => 1), NC(255, 128, 0, 255),
- "check at zero source coverage on full targ coverage");
- iscolora($targ->getpixel(x => 2, y => 1), NC(255, 128, 0, 128),
- "check at zero source coverage on half targ coverage");
- iscolora($targ->getpixel(x => 3, y => 1), NC(255, 128, 0, 0),
- "check at zero source coverage on zero targ coverage");
- iscolora($targ->getpixel(x => 4, y => 1), NC(127, 128, 128, 255),
- "check at half source_coverage on full targ coverage");
- iscolora($targ->getpixel(x => 5, y => 1), NC(85, 128, 170, 191),
- "check at half source coverage on half targ coverage");
- iscolora($targ->getpixel(x => 6, y => 1), NC(0, 128, 255, 128),
- "check at half source coverage on zero targ coverage");
- iscolora($targ->getpixel(x => 7, y => 1), NC(0, 128, 255, 255),
- "check at full source_coverage on full targ coverage");
- iscolora($targ->getpixel(x => 8, y => 1), NC(0, 128, 255, 255),
- "check at full source coverage on half targ coverage");
- iscolora($targ->getpixel(x => 9, y => 1), NC(0, 128, 255, 255),
- "check at full source coverage on zero targ coverage");
+ for my $method (qw/rubthrough compose/) {
+
+ my $src = Imager->new(xsize => 10, ysize => 1, channels => 4);
+ my $targ = Imager->new(xsize => 10, ysize => 2, channels => 4);
+
+ # simple initialization
+ $targ->setscanline('y' => 1, x => 1,
+ pixels =>
+ [
+ NC(255, 128, 0, 255),
+ NC(255, 128, 0, 128),
+ NC(255, 128, 0, 0),
+ NC(255, 128, 0, 255),
+ NC(255, 128, 0, 128),
+ NC(255, 128, 0, 0),
+ NC(255, 128, 0, 255),
+ NC(255, 128, 0, 128),
+ NC(255, 128, 0, 0),
+ ]);
+ $src->setscanline('y' => 0,
+ pixels =>
+ [
+ NC(0, 128, 255, 0),
+ NC(0, 128, 255, 0),
+ NC(0, 128, 255, 0),
+ NC(0, 128, 255, 128),
+ NC(0, 128, 255, 128),
+ NC(0, 128, 255, 128),
+ NC(0, 128, 255, 255),
+ NC(0, 128, 255, 255),
+ NC(0, 128, 255, 255),
+ ]);
+ ok($targ->$method(src => $src, combine => 'normal',
+ tx => 1, ty => 1), "do 4 on 4 $method");
+ iscolora($targ->getpixel(x => 1, 'y' => 1), NC(255, 128, 0, 255),
+ "check at zero source coverage on full targ coverage");
+ iscolora($targ->getpixel(x => 2, 'y' => 1), NC(255, 128, 0, 128),
+ "check at zero source coverage on half targ coverage");
+ iscolora($targ->getpixel(x => 3, 'y' => 1), NC(255, 128, 0, 0),
+ "check at zero source coverage on zero targ coverage");
+ iscolora($targ->getpixel(x => 4, 'y' => 1), NC(127, 128, 128, 255),
+ "check at half source_coverage on full targ coverage");
+ iscolora($targ->getpixel(x => 5, 'y' => 1), NC(85, 128, 170, 191),
+ "check at half source coverage on half targ coverage");
+ iscolora($targ->getpixel(x => 6, 'y' => 1), NC(0, 128, 255, 128),
+ "check at half source coverage on zero targ coverage");
+ iscolora($targ->getpixel(x => 7, 'y' => 1), NC(0, 128, 255, 255),
+ "check at full source_coverage on full targ coverage");
+ iscolora($targ->getpixel(x => 8, 'y' => 1), NC(0, 128, 255, 255),
+ "check at full source coverage on half targ coverage");
+ iscolora($targ->getpixel(x => 9, 'y' => 1), NC(0, 128, 255, 255),
+ "check at full source coverage on zero targ coverage");
+ }
+}
+
+{ # https://rt.cpan.org/Ticket/Display.html?id=30908
+ # we now adapt the source channels to the target
+ # check each combination works as expected
+
+ # various source images
+ my $src1 = Imager->new(xsize => 50, ysize => 50, channels => 1);
+ my $g_grey_full = Imager::Color->new(128, 255, 0, 0);
+ my $g_white_50 = Imager::Color->new(255, 128, 0, 0);
+ $src1->box(filled => 1, xmax => 24, color => $g_grey_full);
+
+ my $src2 = Imager->new(xsize => 50, ysize => 50, channels => 2);
+ $src2->box(filled => 1, xmax => 24, color => $g_grey_full);
+ $src2->box(filled => 1, xmin => 25, color => $g_white_50);
+
+ my $c_red_full = Imager::Color->new(255, 0, 0);
+ my $c_blue_full = Imager::Color->new(0, 0, 255);
+ my $src3 = Imager->new(xsize => 50, ysize => 50, channels => 3);
+ $src3->box(filled => 1, xmax => 24, color => $c_red_full);
+ $src3->box(filled => 1, xmin => 25, color => $c_blue_full);
+
+ my $c_green_50 = Imager::Color->new(0, 255, 0, 127);
+ my $src4 = Imager->new(xsize => 50, ysize => 50, channels => 4);
+ $src4->box(filled => 1, xmax => 24, color => $c_blue_full);
+ $src4->box(filled => 1, xmin => 25, color => $c_green_50);
+
+ my @left_box = ( box => [ 25, 25, 49, 74 ] );
+ my @right_box = ( box => [ 50, 25, 74, 74 ] );
+
+ { # 1 channel output
+ my $base = Imager->new(xsize => 100, ysize => 100, channels => 1);
+ $base->box(filled => 1, color => Imager::Color->new(64, 255, 0, 0));
+
+ my $work = $base->copy;
+ ok($work->rubthrough(left => 25, top => 25, src => $src1), "rubthrough 1 to 1");
+ my $comp = $base->copy;
+ $comp->box(filled => 1, color => $g_grey_full, @left_box);
+ $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(left => 25, top => 25, src => $src2), "rubthrough 2 to 1");
+ $comp = $base->copy;
+ $comp->box(filled => 1, @left_box, color => $g_grey_full);
+ $comp->box(filled => 1, @right_box, color => [ 159, 0, 0, 0 ]);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(left => 25, top => 25, src => $src3), "rubthrough 3 to 1");
+ $comp = $base->copy;
+ $comp->box(filled => 1, @left_box, color => [ 57, 255, 0, 0 ]);
+ $comp->box(filled => 1, @right_box, color => [ 18, 255, 0, 0 ]);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(left => 25, top => 25, src => $src4), "rubthrough 4 to 1");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
+ $comp->box(filled => 1, color => [ 121, 255, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+ }
+
+ { # 2 channel output
+ my $base = Imager->new(xsize => 100, ysize => 100, channels => 2);
+ $base->box(filled => 1, color => [ 128, 128, 0, 0 ]);
+
+ my $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src1), "rubthrough 1 to 2");
+ my $comp = $base->copy;
+ $comp->box(filled => 1, color => $g_grey_full, @left_box);
+ $comp->box(filled => 1, color => [ 0, 255, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src2), "rubthrough 2 to 2");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => $g_grey_full, @left_box);
+ $comp->box(filled => 1, color => [ 213, 191, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src3), "rubthrough 3 to 2");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 57, 255, 0, 0 ], @left_box);
+ $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src4), "rubthrough 4 to 2");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
+ $comp->box(filled => 1, color => [ 162, 191, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+ }
+
+ { # 3 channel output
+ my $base = Imager->new(xsize => 100, ysize => 100, channels => 3);
+ $base->box(filled => 1, color => [ 128, 255, 0, 0 ]);
+
+ my $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src1), "rubthrough 1 to 3");
+ my $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+ $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src2), "rubthrough 2 to 3");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+ $comp->box(filled => 1, color => [ 191, 255, 128, 255 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src3), "rubthrough 3 to 3");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
+ $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src4), "rubthrough 4 to 3");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 0, 0, 255 ], @left_box);
+ $comp->box(filled => 1, color => [ 64, 255, 0 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+ }
+
+ { # 4 channel output
+ my $base = Imager->new(xsize => 100, ysize => 100, channels => 4);
+ $base->box(filled => 1, color => [ 128, 255, 64, 128 ]);
+
+ my $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src1), "rubthrough 1 to 4");
+ my $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+ $comp->box(filled => 1, color => [ 0, 0, 0, 255 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src2), "rubthrough 2 to 4");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+ $comp->box(filled => 1, color => [ 213, 255, 192, 191 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src3), "rubthrough 3 to 4");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
+ $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+
+ $work = $base->copy;
+ ok($work->rubthrough(top => 25, left => 25, src => $src4), "rubthrough 4 to 4");
+ $comp = $base->copy;
+ $comp->box(filled => 1, color => $c_blue_full, @left_box);
+ $comp->box(filled => 1, color => [ 43, 255, 21, 191], @right_box);
+ is_image($work, $comp, "compare rubthrough target to expected");
+ }
}
sub color_cmp {
--- /dev/null
+#!perl -w
+use strict;
+use Imager;
+use Imager::Test qw(is_image);
+use Test::More tests => 2;
+
+my $dest = Imager->new(xsize => 100, ysize => 100, channels => 4);
+$dest->box(filled => 1, color => '0000FF');
+my $src = Imager->new(xsize => 100, ysize => 100, channels => 4);
+$src->circle(color => 'FF0000', x => 50, y => 60, r => 40, aa => 1);
+ok($dest->rubthrough(src => $src, src_minx => 10, src_miny => 20, src_maxx => 90,
+ tx => 10, ty => 10), "rubthrough");
+ok($dest->write(file => "testout/x11rubthru.tif"), "save it");
+