#!/usr/bin/perl -w
# Generate PNG files that shows the input text being rotated 90
# degrees clockwise.
use strict;
use File::Temp;
use constant PI => atan2(0, -1);
use constant FRAMES => 20;
use constant PAGE_OFFSET_X => 306;
use constant PAGE_OFFSET_Y => 396;
use constant CANVAS_WIDTH => 400;
use constant FONT_SCALE_FACTOR => 1.2;
use constant CROP_X => 275;
use constant CROP_Y => 650;
use constant CROP_S => 2000;
use constant RESIZE_WIDTH => 800;
# Load input
if( $#ARGV != 1 )
{
die "$0 \n";
}
my ($input_text, $output_prefix) = @ARGV;
open my $infile, "<$input_text" or die $!;
my @lines = <$infile>;
close $infile;
my $width = 0;
foreach (@lines)
{
chomp;
$width = length($_) if $width < length($_);
}
my $height = scalar @lines;
# Compute offsets
my $cx = $width / 2;
my $cy = $height / 2;
my $long_edge = $width > $height ? $width : $height;
my $spacing = CANVAS_WIDTH / $long_edge;
# Generate frames
my $ps_file = tmpnam();
for(my $frame = 0; $frame < FRAMES; $frame++)
{
# Page header
my $scale = $spacing * FONT_SCALE_FACTOR;
my $ps = '%!PS' .
"\n/Courier-Bold findfont $scale scalefont setfont\n";
# Compute angle, using a cubic to make the rotation accelerate
# at the beginning and decelerate at the end.
#
# f(x) = -2x^3 + 3x^2
# f'(x) = -6x^2 + 6x
# -> f(0) = 0
# -> f(1) = 1
# -> f'(0) = 0
# -> f'(1) = 0
my $ax = $frame / FRAMES;
my $ay = -2 * ($ax * $ax * $ax) + 3 * ($ax * $ax);
my $angle = $ay * -PI / 2;
# Transpose and draw each character.
#
# Note that PostScript renders each character at their bottom left
# corners, but we really want to render them at the center so that
# the rotated characters will be aligned between animation segments.
# This is achieved by adjusting the character offsets with
# "$spacing/2" before the rotation is done so that we rotate from
# the center, minus another "$spacing/2" after rotation so that we
# render characters at their centers.
for(my $y = 0; $y < $height; $y++)
{
my $dy = ($cy - $y) * $spacing - $spacing / 2;
for(my $x = 0; $x < $width; $x++)
{
# No need to render spaces
next if $x >= length($lines[$y]);
my $c = substr($lines[$y], $x, 1);
next if $c eq " ";
# Escape special characters for PostScript
if( $c =~ /[()\\]/ )
{
$c = '\\' . $c;
}
my $dx = ($x - $cx) * $spacing + $spacing / 2;
my $px = cos($angle) * $dx - sin($angle) * $dy + PAGE_OFFSET_X -
$spacing / 2;
my $py = sin($angle) * $dx + cos($angle) * $dy + PAGE_OFFSET_Y +
$spacing / 2;
$ps .= "$px $py moveto ($c) show\n";
}
}
# Page footer
$ps .= "showpage\nquit\n";
# Write PostScript file
open my $outfile, ">$ps_file" or die $!;
print $outfile $ps;
close $outfile;
# Convert PostScript to PPM
my $cmd = "gs -q -dBATCH -dNOPAUSE -sDEVICE=ppmraw " .
"-r300x300 -dTextAlphaBits=4 -dGraphicsAlphaBits=4 " .
"-sOutputFile=${output_prefix}_tmp.ppm $ps_file";
system $cmd;
# Crop and resize output
$cmd = sprintf 'pamcut -left %d -top %d -width %d -height %d ' .
'%s_tmp.ppm ' .
' | pnminvert ' .
' | pamscale -xsize %d ' .
' | pnmtopng -compression 9 > %s%04d.png',
CROP_X, CROP_Y, CROP_S, CROP_S,
$output_prefix,
RESIZE_WIDTH,
$output_prefix,
$frame;
system $cmd;
}
unlink "${output_prefix}_tmp.ppm";
unlink $ps_file;