Game-FaceGenerator
view release on metacpan or search on metacpan
lib/Game/FaceGenerator/Core.pm view on Meta::CPAN
Thus, a mustache (as part of the C<chin>) covers a mouth; C<hair> covers the
face; C<hat> cover C<hair>, and so on.
=cut
sub all_elements {
# face is the background, if any (mostly to support photos)
# chin after mouth (mustache hides mouth)
# nose after chin (mustache!)
# hair after ears
# ears after chin (if you're fat)
# chin after ears (for your beard) â damn!
return qw(face eyes brows mouth chin ears nose extra horns bangs hair hat);
}
=head2 random_components
The random components of C<$type> for C<$artist>. If C<$debug> is true,
F<empty.png> is added to the list of components.
=cut
sub random_components {
my ($type, $artist, $debug) = @_;
%artists = %{all_artists()} unless keys %artists;
$type = one(@{$artists{$artist}->{types}}) if $type eq 'random';
my @elements = all_elements();
@elements = grep(!/^extra/, @elements) if rand(1) >= 0.1; # 10% chance
@elements = grep(!/^hat/, @elements) if rand(1) >= 0.1; # 10% chance
my @files = grep { /\.png$/ } read_dir("$dir/$artist");
my @components;
for my $element (@elements) {
my @candidates1 = grep(/^${element}_/, @files);
my @candidates2 = grep(/_$type/, @candidates1);
@candidates2 = grep(/_all/, @candidates1) unless @candidates2;
my $candidate = one(@candidates2) || '';
unless (any { $type eq $_ } no_flip($artist)) {
$candidate .= '_' if $candidate and rand >= 0.5; # invert it!
}
push(@components, $candidate) if $candidate;
}
unshift(@components, 'empty.png') if $debug;
return @components;
}
=head2 render_components
Renders the components for C<$artist>. The C<@components> are probably the
result of a call to C<random_components>.
=cut
sub render_components {
my ($artist, @components) = @_;
my $image;
for my $component (@components) {
next unless $component;
my $layer;
if (-f "$dir/$component") {
$layer = GD::Image->newFromPng("$dir/$component", 1);
} elsif (substr($component, -1) eq '_') {
$component = substr($component, 0, -1);
$layer = GD::Image->newFromPng("$dir/$artist/$component", 1);
$layer->flipHorizontal();
} else {
$layer = GD::Image->newFromPng("$dir/$artist/$component", 1);
}
# scanned images with a white background: make white transparent unless this
# is the first image
if ($layer->isTrueColor == 0 and $layer->transparent == -1 and $image) {
my $white = $layer->colorClosest(255,255,255);
$layer->transparent($white);
}
# if we already have an image, combine them
if ($image) {
$image->copy($layer, 0, 0, 0, 0, $layer->getBounds());
} else {
$image = $layer;
$image->alphaBlending(1);
$image->saveAlpha(1);
}
}
return $image->png();
}
=head2 move
This is the subroutine called to edit the images.
=cut
sub move {
my ($artist, $element, $direction, $step) = @_;
my $file = "$dir/$artist/$element";
my $original = GD::Image->new($file);
my $image = GD::Image->new(450, 600);
my $white = $image->colorAllocate(255,255,255); # find white
$image->rectangle(0, 0, $image->getBounds(), $white);
if ($direction eq 'up') {
$image->copy($original, 0, 0, 0, $step, $image->width, $image->height - $step);
} elsif ($direction eq 'down') {
$image->copy($original, 0, $step, 0, 0, $image->width, $image->height - $step);
} elsif ($direction eq 'left') {
$image->copy($original, 0, 0, $step, 0, $image->width - $step, $image->height);
} elsif ($direction eq 'right') {
$image->copy($original, $step, 0, 0, 0, $image->width - $step, $image->height);
} elsif ($direction eq 'appart') {
$image->copy($original, $image->width/2 + $step/2, 0, $image->width/2, 0, $image->width/2 - $step/2, $image->height);
$image->copy($original, 0, 0, $step/2, 0, $image->width/2 - $step/2, $image->height);
} elsif ($direction eq 'closer') {
$image->copy($original, $step/2, 0, 0, 0, $image->width/2 - $step/2, $image->height);
$image->copy($original, $image->width/2, 0, $image->width/2 + $step/2, 0, $image->width/2 - $step/2, $image->height);
} else {
die "Unknown direction: $direction\n";
}
write_binary($file, $image->png);
}
1;
( run in 1.985 second using v1.01-cache-2.11-cpan-39bf76dae61 )