Skip to content

Commit

Permalink
implement fountain fills similar to most paint programs
Browse files Browse the repository at this point in the history
minor bug fixes
  • Loading branch information
Tony Cook committed Aug 29, 2001
1 parent 02652ff commit 6607600
Show file tree
Hide file tree
Showing 12 changed files with 1,763 additions and 22 deletions.
11 changes: 11 additions & 0 deletions Changes
Expand Up @@ -480,6 +480,17 @@ Revision history for Perl extension Imager.
- fixed some problems in jpeg handling from the exp_represent merge - fixed some problems in jpeg handling from the exp_represent merge
- fixed buffer flushing for wiol jpeg code - fixed buffer flushing for wiol jpeg code
- added some tests that will hopefully catch it in the future - added some tests that will hopefully catch it in the future
- added the OO interfaces to the mosaic, bumpmap, postlevels and
watermark filters, and documented them
- fixed a sample size conversion problem in i_gpixf_d() etc.
- added simple color representation conversion functions (used
in i_fountain().)
- added the fountain filter:
- creates gradients similar to paint software
- 90% support for GIMP gradient files
- OO interface and documentation
- Imager::Fountain for building/loading fill definitions
- named value translation for filters


================================================================= =================================================================


Expand Down
223 changes: 223 additions & 0 deletions Imager.pm
Expand Up @@ -168,6 +168,16 @@ BEGIN {


$DEBUG=0; $DEBUG=0;


# the members of the subhashes under %filters are:
# callseq - a list of the parameters to the underlying filter in the
# order they are passed
# callsub - a code ref that takes a named parameter list and calls the
# underlying filter
# defaults - a hash of default values
# names - defines names for value of given parameters so if the names
# field is foo=> { bar=>1 }, and the user supplies "bar" as the
# foo parameter, the filter will receive 1 for the foo
# parameter
$filters{contrast}={ $filters{contrast}={
callseq => ['image','intensity'], callseq => ['image','intensity'],
callsub => sub { my %hsh=@_; i_contrast($hsh{image},$hsh{intensity}); } callsub => sub { my %hsh=@_; i_contrast($hsh{image},$hsh{intensity}); }
Expand Down Expand Up @@ -258,6 +268,47 @@ BEGIN {
$hsh{pixdiff}); $hsh{pixdiff});
}, },
}; };
$filters{fountain} =
{
callseq => [ qw(image xa ya xb yb ftype repeat combine super_sample ssample_param segments) ],
names => {
ftype => { linear => 0,
bilinear => 1,
radial => 2,
radial_square => 3,
revolution => 4,
conical => 5 },
repeat => { none => 0,
sawtooth => 1,
triangle => 2,
saw_both => 3,
tri_both => 4,
},
super_sample => {
none => 0,
grid => 1,
random => 2,
circle => 3,
},
},
defaults => { ftype => 0, repeat => 0, combine => 0,
super_sample => 0, ssample_param => 4,
segments=>[
[ 0, 0.5, 1,
Imager::Color->new(0,0,0),
Imager::Color->new(255, 255, 255),
0, 0,
],
],
},
callsub =>
sub {
my %hsh = @_;
i_fountain($hsh{image}, $hsh{xa}, $hsh{ya}, $hsh{xb}, $hsh{yb},
$hsh{ftype}, $hsh{repeat}, $hsh{combine}, $hsh{super_sample},
$hsh{ssample_param}, $hsh{segments});
},
};


$FORMATGUESS=\&def_guess_type; $FORMATGUESS=\&def_guess_type;
} }
Expand Down Expand Up @@ -1171,6 +1222,14 @@ sub filter {
$self->{ERRSTR}='type parameter not matching any filter'; return undef; $self->{ERRSTR}='type parameter not matching any filter'; return undef;
} }


if ($filters{$input{type}}{names}) {
my $names = $filters{$input{type}}{names};
for my $name (keys %$names) {
if (defined $input{$name} && exists $names->{$name}{$input{$name}}) {
$input{$name} = $names->{$name}{$input{$name}};
}
}
}
if (defined($filters{$input{type}}{defaults})) { if (defined($filters{$input{type}}{defaults})) {
%hsh=('image',$self->{IMG},%{$filters{$input{type}}{defaults}},%input); %hsh=('image',$self->{IMG},%{$filters{$input{type}}{defaults}},%input);
} else { } else {
Expand Down Expand Up @@ -2824,9 +2883,12 @@ source.
bumpmap bump elevation(0) lightx lighty st(2) bumpmap bump elevation(0) lightx lighty st(2)
contrast intensity contrast intensity
conv coef conv coef
fountain xa ya xb yb ftype(linear) repeat(none) combine(0)
super_sample(none) ssample_param(4) segments(see below)
gaussian stddev gaussian stddev
gradgen xo yo colors dist gradgen xo yo colors dist
hardinvert hardinvert
mosaic size(20)
noise amount(3) subtype(0) noise amount(3) subtype(0)
postlevels levels(10) postlevels levels(10)
radnoise xo(100) yo(100) ascale(17.0) rscale(0.02) radnoise xo(100) yo(100) ascale(17.0) rscale(0.02)
Expand Down Expand Up @@ -2864,6 +2926,163 @@ will reduce the contrast.
performs 2 1-dimensional convolutions on the image using the values performs 2 1-dimensional convolutions on the image using the values
from I<coef>. I<coef> should be have an odd length. from I<coef>. I<coef> should be have an odd length.
=item fountain
renders a fountain fill, similar to the gradient tool in most paint
software. The default fill is a linear fill from opaque black to
opaque white. The points A(xa, ya) and B(xb, yb) control the way the
fill is performed, depending on the ftype parameter:
=over
=item linear
the fill ramps from A through to B.
=item bilinear
the fill ramps in both directions from A, where AB defines the length
of the gradient.
=item radial
A is the center of a circle, and B is a point on it's circumference.
The fill ramps from the center out to the circumference.
=item radial_square
A is the center of a square and B is the center of one of it's sides.
This can be used to rotate the square. The fill ramps out to the
edges of the square.
=item revolution
A is the centre of a circle and B is a point on it's circumference. B
marks the 0 and 360 point on the circle, with the fill ramping
clockwise.
=item conical
A is the center of a circle and B is a point on it's circumference. B
marks the 0 and point on the circle, with the fill ramping in both
directions to meet opposite.
=back
The I<repeat> option controls how the fill is repeated for some
I<ftype>s after it leaves the AB range:
=over
=item none
no repeats, points outside of each range are treated as if they were
on the extreme end of that range.
=item sawtooth
the fill simply repeats in the positive direction
=item triangle
the fill repeats in reverse and then forward and so on, in the
positive direction
=item saw_both
the fill repeats in both the positive and negative directions (only
meaningful for a linear fill).
=item tri_both
as for triangle, but in the negative direction too (only meaningful
for a linear fill).
=back
By default the fill simply overwrites the whole image (unless you have
parts of the range 0 through 1 that aren't covered by a segment), if
any segments of your fill have any transparency, you can set the
I<combine> option to 1 to have the fill combined with the existing pixels.
If your fill has sharp edges, for example between steps if you use
repeat set to 'triangle', you may see some aliased or ragged edges.
You can enable super-sampling which will take extra samples within the
pixel in an attempt anti-alias the fill.
The possible values for the super_sample option are:
=over
=item none
no super-sampling is done
=item grid
a square grid of points are sampled. The number of points sampled is
the square of ceil(0.5 + sqrt(ssample_param)).
=item random
a random set of points within the pixel are sampled. This looks
pretty bad for low ssample_param values.
=item circle
the points on the radius of a circle within the pixel are sampled.
This seems to produce the best results, but is fairly slow (for now).
=back
You can control the level of sampling by setting the ssample_param
option. This is roughly the number of points sampled, but depends on
the type of sampling.
The segments option is an arrayref of segments. You really should use
the Imager::Fountain class to build your fountain fill. Each segment
is an array ref containing:
=over
=item start
a floating point number between 0 and 1, the start of the range of fill parameters covered by this segment.
=item middle
a floating point number between start and end which can be used to
push the color range towards one end of the segment.
=item end
a floating point number between 0 and 1, the end of the range of fill
parameters covered by this segment. This should be greater than
start.
=item c0
=item c1
The colors at each end of the segment. These can be either
Imager::Color or Imager::Color::Float objects.
=item segment type
The type of segment, this controls the way the fill parameter varies
over the segment. 0 for linear, 1 for curved (unimplemented), 2 for
sine, 3 for sphere increasing, 4 for sphere decreasing.
=item color type
The way the color varies within the segment, 0 for simple RGB, 1 for
hue increasing and 2 for hue decreasing.
=back
Don't forgot to use Imager::Fountain instead of building your own.
Really. It even loads GIMP gradient files.
=item gaussian =item gaussian
performs a gaussian blur of the image, using I<stddev> as the standard performs a gaussian blur of the image, using I<stddev> as the standard
Expand All @@ -2884,6 +3103,10 @@ for Euclidean squared, and 2 for Manhattan distance.
inverts the image, black to white, white to black. All channels are inverts the image, black to white, white to black. All channels are
inverted, including the alpha channel if any. inverted, including the alpha channel if any.
=item mosaic
produces averaged tiles of the given I<size>.
=item noise =item noise
adds noise of the given I<amount> to the image. If I<subtype> is adds noise of the given I<amount> to the image. If I<subtype> is
Expand Down
96 changes: 93 additions & 3 deletions Imager.xs
Expand Up @@ -1883,9 +1883,99 @@ i_gradgen(im, ...)
} }
i_gradgen(im, num, xo, yo, ival, dmeasure); i_gradgen(im, num, xo, yo, ival, dmeasure);



void

i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, segs)

Imager::ImgRaw im
double xa
double ya
double xb
double yb
int type
int repeat
int combine
int super_sample
double ssample_param
PREINIT:
int i, j;
AV *asegs;
AV *aseg;
SV *sv;
int count;
i_fountain_seg *segs;
double work[3];
int worki[2];
CODE:
/* Each element of segs must contain:
[ start, middle, end, c0, c1, segtype, colortrans ]
start, middle, end are doubles from 0 to 1
c0, c1 are Imager::Color::Float or Imager::Color objects
segtype, colortrans are ints
*/
if (!SvROK(ST(10)) || ! SvTYPE(SvRV(ST(10))))
croak("i_fountain: argument 11 must be an array ref");

asegs = (AV *)SvRV(ST(10));

count = av_len(asegs)+1;
if (count < 1)
croak("i_fountain must have at least one segment");
segs = mymalloc(sizeof(i_fountain_seg) * count);
for(i = 0; i<count; i++) {
SV **sv1 = av_fetch(asegs, i, 0);
if (!sv1 || !*sv1 || !SvROK(*sv1)
|| SvTYPE(SvRV(*sv1)) != SVt_PVAV) {
myfree(segs);
croak("i_fountain: segs must be an arrayref of arrayrefs");
}
aseg = (AV *)SvRV(*sv1);
if (av_len(aseg) != 7-1) {
myfree(segs);
croak("i_fountain: a segment must have 7 members");
}
for (j = 0; j < 3; ++j) {
SV **sv2 = av_fetch(aseg, j, 0);
if (!sv2 || !*sv2) {
myfree(segs);
croak("i_fountain: XS error");
}
work[j] = SvNV(*sv2);
}
segs[i].start = work[0];
segs[i].middle = work[1];
segs[i].end = work[2];
for (j = 0; j < 2; ++j) {
SV **sv3 = av_fetch(aseg, 3+j, 0);
if (!sv3 || !*sv3 || !SvROK(*sv3) ||
(!sv_derived_from(*sv3, "Imager::Color")
&& !sv_derived_from(*sv3, "Imager::Color::Float"))) {
myfree(segs);
croak("i_fountain: segs must contain colors in elements 3 and 4");
}
if (sv_derived_from(*sv3, "Imager::Color::Float")) {
segs[i].c[j] = *(i_fcolor *)SvIV((SV *)SvRV(*sv3));
}
else {
i_color c = *(i_color *)SvIV((SV *)SvRV(*sv3));
int ch;
for (ch = 0; ch < MAXCHANNELS; ++ch) {
segs[i].c[j].channel[ch] = c.channel[ch] / 255.0;
}
}
}
for (j = 0; j < 2; ++j) {
SV **sv2 = av_fetch(aseg, j+5, 0);
if (!sv2 || !*sv2) {
myfree(segs);
croak("i_fountain: XS error");
}
worki[j] = SvIV(*sv2);
}
segs[i].type = worki[0];
segs[i].color = worki[1];
}
i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample,
ssample_param, count, segs);
myfree(segs);


void void
i_errors() i_errors()
Expand Down

0 comments on commit 6607600

Please sign in to comment.