#! /usr/bin/perl -w
# Author : Gautam Iyer <gautam@math.uchicago.edu>
# Created : Wed 24 Nov 2005 10:23:55 AM CST
# Modified : Wed 03 Jan 2007 03:22:05 AM PST
# Licence : GNU General Programers Licence. See the file Licence for the
# exact licence.
package funroll::Resize;
use strict;
use File::Copy;
use File::Temp;
use Image::Magick;
use Image::ExifTool;
use Term::ANSIColor qw(:constants);
use funroll::Page;
our @ISA=qw(funroll::Page);
no integer;
sub funroll_page
{
my $this = shift;
my ($list, $key);
my ($item, $src, $dest);
my $image = Image::Magick->new();
my $error;
my %defaults = (
image_indir => undef,
image_outdir => undef,
img_list => 'files',
img_key => 'filename',
img_width => 640,
img_height => 480,
img_filter => 'Cubic',
img_blur => .75,
img_quality => 80,
exif_field => undef,
even_height => undef
);
$this->clean_values( \%defaults);
if( !exists( $this->{options}{image_indir})
|| !exists( $this->{options}{image_outdir})
|| $this->{options}{image_outdir} eq '' ) {
print STDERR BOLD, RED, "funroll::Resize: Must specify options image_indir and image_outdir\n", RESET;
return;
};
# Delete 'undef' values from my options
# foreach $key keys( %defaults) {
# delete $this->{options}{$key}
# if( exists( $this->{options}{$key}) && $this->{options}{$key} eq 'undef');
# }
# Now merge in our options, and we're all set.
# merge_hash( $this->{options}, \%defaults, 1);
$list = $this->{options}{img_list};
$key = $this->{options}{img_key};
print STDERR "Processing image ";
foreach $item ( @{$this->{lists}{$list}})
{
my $exifinfo = Image::ExifTool->new();
next unless( exists( $item->{$key}));
$src = $this->resolve_infile( $this->{options}{image_indir} . $item->{$key});
$dest = $this->resolve_outfile( $this->{options}{image_outdir} . $item->{$key});
make_base_dir( $dest);
if( -r $src && -e $dest && ( -M $dest <= -M $src) )
{
# Destination newer than source. Skip file
$this->show_status( YELLOW, "$dest " );
next;
}
$exifinfo->ExtractInfo( $src, {Binary=>1} );
if(
defined( $this->{options}{exif_field} )
&& $this->exif_thumbnail( $dest, $exifinfo )
)
{
# Get scaled image from exif preview / thumbnail.
$this->show_status( CYAN, "$dest " );
next;
}
# Get scaled image by rescaling the original image (slow).
if( $error = $image->Read( $src))
{
$this->show_status( BOLD.RED, "$src ($error) ");
# print STDERR BOLD, RED, "$src ", RESET;
next;
}
$this->resize_rotate( $image,
$exifinfo->GetValue( 'Orientation', 'ValueConv' ) );
# XXX: Strip the entire exif profile (otherwise the Orientation
# attribute might be incorrectly set).
$image->Strip();
# Write TODO: Find better options.
$this->show_status( $image->Write( filename=>$dest, quality=>$this->{options}{img_quality}) ? RED : GREEN, "$dest ");
# Clear
@$image = ();
}
print STDERR ".\n";
}
# Grab thumbnail from exif data. Return success / failure.
sub exif_thumbnail
{
my ($this, $dest, $exifinfo) = @_;
my $tmpfile = File::Temp->new();
# Grab resized image from exif data
my ($orientation, $origWidth, $origHeight);
my ($width, $height);
my $image=Image::Magick->new();
my $retval;
# Write thumbnail to a temporary file
open( THUMB, ">", $tmpfile->filename() );
print THUMB ${$exifinfo->GetValue( $this->{options}{exif_field}, 'Raw' )};
close( THUMB );
# Crop / resize thumbnail
$orientation = $exifinfo->GetValue( 'Orientation', 'ValueConv' );
$origHeight = $exifinfo->GetValue( 'ImageHeight', 'ValueConv' );
$origWidth = $exifinfo->GetValue( 'ImageWidth' , 'ValueConv' );
if( $image->Read( $tmpfile->filename() ) ) {return 0};
($width, $height) = $image->Get( 'columns', 'rows' );
# Check aspect ratios and crop
if( int( 0.5 + $origHeight / $origWidth * $width ) < $height )
{
# Black horizontal border
my $newheight = int( 0.5 + $origHeight / $origWidth * $width );
my $y = ($height - $newheight) / 2;
$image->Crop( geometry=>"${width}x$newheight+0+$y" );
#print STDERR "${width}x$newheight+0+$y:";
}
elsif( int( 0.5 + $origWidth / $origHeight * $height ) < $width )
{
# Black vertical border
my $newwidth = int( 0.5 + $origWidth / $origHeight * $height );
my $x = ($width - $newwidth) / 2;
$image->Crop( geometry=>"${newwidth}x$height+$x+0" );
#print STDERR "${newwidth}x$height+$x+0";
}
# Resize / rotate the image.
$retval = $this->resize_rotate( $image, $orientation );
if( $retval && $image->Get( 'taint' ) )
{
$image->Write( filename=>$dest,
quality=>$this->{options}{img_quality});
}
# Clear
@$image=();
return $retval;
}
# Returns 0 if the image is smaller than required dimensions, or if there was
# an error. Returns 1 otherwise.
sub resize_rotate
{
my ($this, $image, $orientation) = @_;
my $retval = 0;
my ($width, $height) = $image->Get( 'columns', 'rows' );
# For even height images rotate it first
if( exists( $this->{options}{even_height} ))
{
#print STDERR "R";
rotate( $image, $orientation );
($width, $height) = $image->Get( 'columns', 'rows' );
}
# Resize if necessary
if(
$width > $this->{options}{img_width} ||
$height > $this->{options}{img_height}
)
{
#print STDERR "r";
$image->Resize(
geometry=> "$this->{options}{img_width}".
"x$this->{options}{img_height}",
filter=>$this->{options}{img_filter},
blur=>$this->{options}{img_blur}
);
$retval = 1;
}
elsif(
$width == $this->{options}{img_width} ||
$height == $this->{options}{img_height}
)
{
$retval = 1;
}
# For non-even height images, rotate them now.
if( !exists( $this->{options}{even_height} ))
{
#print STDERR "R";
rotate( $image, $orientation );
}
return $retval;
}
sub rotate
{
my ($image, $orientation) = @_;
return unless( defined( $orientation ) );
if( $orientation =~ m/^(right_top|6)$/)
{
$image->Rotate( degrees=>90)
}
elsif( $orientation =~ m/^(left_bot|8)$/)
{
$image->Rotate( degrees=>-90)
}
}
sub show_status
{
my ($this, $color, $msg) = @_;
$msg =~ s/^$this->{options}{output_dir}/-/;
$msg =~ s/^$this->{options}{image_outdir}/-/;
$msg =~ s/^$this->{options}{image_indir}/+/;
print STDERR "$color$msg", RESET;
}
1;