use strict; use warnings; use GD 2; use vars qw( %CharMap $DELAY $X $Y ); use Getopt::Long; GetOptions( 'file=s' => \ my( $file ), 'text=s' => \ my( $text ), 'delay=i' => \ ( $DELAY = 25 ), 'X=i' => \ ( $X = 100 ), 'Y=i' => \ ( $Y = 100 ) ); defined $file and length $text and $DELAY > 0 and $X > 0 and $Y > 0 or die "Usage: $0 --file ... --text ... --delay ... --X ... --Y ...\n"; AnimatedGIF( $file, map RenderChar( Char( $_ ) ), split( //, $text ) ); exit; sub DELAY () { $DELAY } sub X () { $X } sub Y () { $Y } sub X1 () { int( $X * 1 / 3 ) } sub X2 () { int( $X * 2 / 3 ) } sub Y1 () { int( $Y * 1 / 4 ) } sub Y2 () { int( $Y * 2 / 4 ) } sub Y3 () { int( $Y * 3 / 4 ) } BEGIN { @CharMap{"a" .. "z", " "} = ( [[1]], [[qw[1 2]]], [[qw[1 3]]], [[qw[1 2 4]]], [[qw[1 4]]], [[qw[3 1 2]]], [[qw[1 2 4 3 1]]], [[qw[1 3 4]]], [[qw[2 3]]], [[qw[2 4 3]]], [[1],[5]], [[qw[1 3 5]]], [[qw[1 2]],[5]], [[qw[1 2 4 5]]], [[qw[1 4 5]]], [[qw[5 3 1 2]]], [[qw[3 4 2 1 4 5]]], [[qw[1 3 5]],[qw[3 4]]], [[qw[5 3 2]]], [[qw[5 3 4 2]]], [[1],[qw[5 6]]], [[qw[1 3 5 6]]], [[qw[2 4 6]],[qw[3 4]]], [[qw[1 2]],[qw[5 6]]], [[qw[1 2 4 6 5]]], [[qw[1 4 6 5]]], [] ); } sub Char { map $CharMap{$_}, @_ } sub AnimatedGIF { my $file = shift; open my $img, "> $file\0" or die "Couldn't open $file: $!"; binmode $img or die "Couldn't set binmode on $file: $!"; my $first = $_[0]; print $img $first->gifanimbegin( 1, 0 ) or die "Couldn't write to $file: $!"; print $img $first->gifanimadd( 0, 0, 0, DELAY ) or die "Couldn't write to $file: $!"; for ( 1 .. $#_ ) { my ( $this, $prev ) = @_[ $_, $_ - 1 ]; print $img $this->gifanimadd( 0, 0, 0, DELAY, 1, $prev ) or die "Couldn't write to $file: $!"; } print $img $first->gifanimend or die "Couldn't write to $file: $!"; close $img or die "Couldn't close and flush $file: $!"; return; } sub Position2XY { return map( +( $_ == 1 ? [ X1, Y1 ] : $_ == 2 ? [ X2, Y1 ] : $_ == 3 ? [ X1, Y2 ] : $_ == 4 ? [ X2, Y2 ] : $_ == 5 ? [ X1, Y3 ] : [ X2, Y3 ] ), @_ ); } sub RenderChar { my $im = GD::Image->new( X, Y ); my $brush = GD::Image->new( 7, 7 ); { my $white = $brush->colorAllocate( 255, 255, 255 ); my $black = $brush->colorAllocate( 0, 0, 0 ); $brush->transparent( $white ); $brush->arc( 4, 4, 7, 7, 0, 360, $black ); } $im->setBrush( $brush ); while ( @_ ) { for my $line ( @{shift()} ) { my @points = map Position2XY( $_ ), @$line; if ( 1 == @points ) { push( @points, [ $points[0][0] + 1, $points[0][1] ], [ $points[0][0] + 1, $points[0][1] + 1 ], [ $points[0][0], $points[0][1] + 1 ] ); } my $poly = GD::Polygon->new; $poly->addPt( @$_ ) for @points; $im->unclosedPolygon( $poly, gdBrushed ); } } return $im; }