]> git.imager.perl.org - imager.git/blobdiff - Imager.pm
- bump to 0.48
[imager.git] / Imager.pm
index ed899ffc9d494ba3715074db1aae0a6b470c9d97..0f684aec57cbbb8c4922daedfa246c0d27a3851e 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -145,11 +145,17 @@ use Imager::Font;
 
 BEGIN {
   require Exporter;
 
 BEGIN {
   require Exporter;
-  require DynaLoader;
-
-  $VERSION = '0.45';
-  @ISA = qw(Exporter DynaLoader);
-  bootstrap Imager $VERSION;
+  @ISA = qw(Exporter);
+  $VERSION = '0.48';
+  eval {
+    require XSLoader;
+    XSLoader::load(Imager => $VERSION);
+    1;
+  } or do {
+    require DynaLoader;
+    push @ISA, 'DynaLoader';
+    bootstrap Imager $VERSION;
+  }
 }
 
 BEGIN {
 }
 
 BEGIN {
@@ -235,11 +241,26 @@ BEGIN {
      }
     };
 
      }
     };
 
-  $filters{nearest_color} ={
-                           callseq => ['image', 'xo', 'yo', 'colors', 'dist'],
-                           defaults => { },
-                           callsub => sub { my %hsh=@_; i_nearest_color($hsh{image}, $hsh{xo}, $hsh{yo}, $hsh{colors}, $hsh{dist}); }
-                          };
+  $filters{nearest_color} =
+    {
+     callseq => ['image', 'xo', 'yo', 'colors', 'dist'],
+     defaults => { },
+     callsub => 
+     sub { 
+       my %hsh=@_; 
+       # make sure the segments are specified with colors
+       my @colors;
+       for my $color (@{$hsh{colors}}) {
+         my $new_color = _color($color) 
+           or die $Imager::ERRSTR."\n";
+         push @colors, $new_color;
+       }
+
+       i_nearest_color($hsh{image}, $hsh{xo}, $hsh{yo}, \@colors, 
+                       $hsh{dist})
+         or die Imager->_error_as_msg() . "\n";
+     },
+    };
   $filters{gaussian} = {
                         callseq => [ 'image', 'stddev' ],
                         defaults => { },
   $filters{gaussian} = {
                         callseq => [ 'image', 'stddev' ],
                         defaults => { },
@@ -366,7 +387,8 @@ BEGIN {
 
        i_fountain($hsh{image}, $hsh{xa}, $hsh{ya}, $hsh{xb}, $hsh{yb},
                   $hsh{ftype}, $hsh{repeat}, $hsh{combine}, $hsh{super_sample},
 
        i_fountain($hsh{image}, $hsh{xa}, $hsh{ya}, $hsh{xb}, $hsh{yb},
                   $hsh{ftype}, $hsh{repeat}, $hsh{combine}, $hsh{super_sample},
-                  $hsh{ssample_param}, \@segments);
+                  $hsh{ssample_param}, \@segments)
+         or die Imager->_error_as_msg() . "\n";
      },
     };
   $filters{unsharpmask} =
      },
     };
   $filters{unsharpmask} =
@@ -567,8 +589,7 @@ sub copy {
   }
 
   my $newcopy=Imager->new();
   }
 
   my $newcopy=Imager->new();
-  $newcopy->{IMG}=i_img_new();
-  i_copy($newcopy->{IMG},$self->{IMG});
+  $newcopy->{IMG} = i_copy($self->{IMG});
   return $newcopy;
 }
 
   return $newcopy;
 }
 
@@ -576,19 +597,68 @@ sub copy {
 
 sub paste {
   my $self = shift;
 
 sub paste {
   my $self = shift;
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
-  my %input=(left=>0, top=>0, @_);
-  unless($input{img}) {
-    $self->{ERRSTR}="no source image";
+
+  unless ($self->{IMG}) { 
+    $self->_set_error('empty input image');
+    return;
+  }
+  my %input=(left=>0, top=>0, src_minx => 0, src_miny => 0, @_);
+  my $src = $input{img} || $input{src};
+  unless($src) {
+    $self->_set_error("no source image");
     return;
   }
   $input{left}=0 if $input{left} <= 0;
   $input{top}=0 if $input{top} <= 0;
     return;
   }
   $input{left}=0 if $input{left} <= 0;
   $input{top}=0 if $input{top} <= 0;
-  my $src=$input{img};
+
   my($r,$b)=i_img_info($src->{IMG});
   my($r,$b)=i_img_info($src->{IMG});
+  my ($src_left, $src_top) = @input{qw/src_minx src_miny/};
+  my ($src_right, $src_bottom);
+  if ($input{src_coords}) {
+    ($src_left, $src_top, $src_right, $src_bottom) = @{$input{src_coords}}
+  }
+  else {
+    if (defined $input{src_maxx}) {
+      $src_right = $input{src_maxx};
+    }
+    elsif (defined $input{width}) {
+      if ($input{width} <= 0) {
+        $self->_set_error("paste: width must me positive");
+        return;
+      }
+      $src_right = $src_left + $input{width};
+    }
+    else {
+      $src_right = $r;
+    }
+    if (defined $input{src_maxx}) {
+      $src_bottom = $input{src_maxy};
+    }
+    elsif (defined $input{height}) {
+      if ($input{height} < 0) {
+        $self->_set_error("paste: height must be positive");
+        return;
+      }
+      $src_bottom = $src_top + $input{height};
+    }
+    else {
+      $src_bottom = $b;
+    }
+  }
+
+  $src_right > $r and $src_right = $r;
+  $src_bottom > $r and $src_bottom = $b;
+
+  if ($src_right <= $src_left
+      || $src_bottom < $src_top) {
+    $self->_set_error("nothing to paste");
+    return;
+  }
 
   i_copyto($self->{IMG}, $src->{IMG}, 
 
   i_copyto($self->{IMG}, $src->{IMG}, 
-          0,0, $r, $b, $input{left}, $input{top});
+          $src_left, $src_top, $src_right, $src_bottom, 
+           $input{left}, $input{top});
+
   return $self;  # What should go here??
 }
 
   return $self;  # What should go here??
 }
 
@@ -1149,7 +1219,10 @@ sub read {
   }
 
   if ( $input{'type'} eq 'tiff' ) {
   }
 
   if ( $input{'type'} eq 'tiff' ) {
-    $self->{IMG}=i_readtiff_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
+    my $page = $input{'page'};
+    defined $page or $page = 0;
+    # Fixme, check if that length parameter is ever needed
+    $self->{IMG}=i_readtiff_wiol( $IO, -1, $page ); 
     if ( !defined($self->{IMG}) ) {
       $self->{ERRSTR}=$self->_error_as_msg(); return undef;
     }
     if ( !defined($self->{IMG}) ) {
       $self->{ERRSTR}=$self->_error_as_msg(); return undef;
     }
@@ -1160,7 +1233,8 @@ sub read {
   if ( $input{'type'} eq 'pnm' ) {
     $self->{IMG}=i_readpnm_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
     if ( !defined($self->{IMG}) ) {
   if ( $input{'type'} eq 'pnm' ) {
     $self->{IMG}=i_readpnm_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
     if ( !defined($self->{IMG}) ) {
-      $self->{ERRSTR}='unable to read pnm image: '._error_as_msg(); return undef;
+      $self->{ERRSTR}='unable to read pnm image: '._error_as_msg(); 
+      return undef;
     }
     $self->{DEBUG} && print "loading a pnm file\n";
     return $self;
     }
     $self->{DEBUG} && print "loading a pnm file\n";
     return $self;
@@ -1190,16 +1264,28 @@ sub read {
       $self->{ERRSTR} = "option 'colors' must be a scalar reference";
       return undef;
     }
       $self->{ERRSTR} = "option 'colors' must be a scalar reference";
       return undef;
     }
-    if ($input{colors}) {
-      my $colors;
-      ($self->{IMG}, $colors) =i_readgif_wiol( $IO );
-      if ($colors) {
-       ${ $input{colors} } = [ map { NC(@$_) } @$colors ];
+    if ($input{'gif_consolidate'}) {
+      if ($input{colors}) {
+       my $colors;
+       ($self->{IMG}, $colors) =i_readgif_wiol( $IO );
+       if ($colors) {
+         ${ $input{colors} } = [ map { NC(@$_) } @$colors ];
+       }
+      }
+      else {
+       $self->{IMG} =i_readgif_wiol( $IO );
       }
     }
     else {
       }
     }
     else {
-      $self->{IMG} =i_readgif_wiol( $IO );
+      my $page = $input{'page'};
+      defined $page or $page = 0;
+      $self->{IMG} = i_readgif_single_wiol( $IO, $page );
+      if ($input{colors}) {
+       ${ $input{colors} } =
+         [ i_getcolors($self->{IMG}, 0, i_colorcount($self->{IMG})) ];
+      }
     }
     }
+
     if ( !defined($self->{IMG}) ) {
       $self->{ERRSTR}=$self->_error_as_msg();
       return undef;
     if ( !defined($self->{IMG}) ) {
       $self->{ERRSTR}=$self->_error_as_msg();
       return undef;
@@ -1241,7 +1327,7 @@ sub read {
                                   $params{storechannels},
                                   $params{interleave});
     if ( !defined($self->{IMG}) ) {
                                   $params{storechannels},
                                   $params{interleave});
     if ( !defined($self->{IMG}) ) {
-      $self->{ERRSTR}='unable to read raw image';
+      $self->{ERRSTR}=$self->_error_as_msg();
       return undef;
     }
     $self->{DEBUG} && print "loading a raw file\n";
       return undef;
     }
     $self->{DEBUG} && print "loading a raw file\n";
@@ -1377,12 +1463,12 @@ sub write {
 
     if (defined $input{class} && $input{class} eq 'fax') {
       if (!i_writetiff_wiol_faxable($self->{IMG}, $IO, $input{fax_fine})) {
 
     if (defined $input{class} && $input{class} eq 'fax') {
       if (!i_writetiff_wiol_faxable($self->{IMG}, $IO, $input{fax_fine})) {
-       $self->{ERRSTR}='Could not write to buffer';
+       $self->{ERRSTR} = $self->_error_as_msg();
        return undef;
       }
     } else {
       if (!i_writetiff_wiol($self->{IMG}, $IO)) {
        return undef;
       }
     } else {
       if (!i_writetiff_wiol($self->{IMG}, $IO)) {
-       $self->{ERRSTR}='Could not write to buffer';
+       $self->{ERRSTR} = $self->_error_as_msg();
        return undef;
       }
     }
        return undef;
       }
     }
@@ -1390,7 +1476,7 @@ sub write {
     $self->_set_opts(\%input, "pnm_", $self)
       or return undef;
     if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
     $self->_set_opts(\%input, "pnm_", $self)
       or return undef;
     if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
-      $self->{ERRSTR}='unable to write pnm image';
+      $self->{ERRSTR} = $self->_error_as_msg();
       return undef;
     }
     $self->{DEBUG} && print "writing a pnm file\n";
       return undef;
     }
     $self->{DEBUG} && print "writing a pnm file\n";
@@ -1398,7 +1484,7 @@ sub write {
     $self->_set_opts(\%input, "raw_", $self)
       or return undef;
     if ( !i_writeraw_wiol($self->{IMG},$IO) ) {
     $self->_set_opts(\%input, "raw_", $self)
       or return undef;
     if ( !i_writeraw_wiol($self->{IMG},$IO) ) {
-      $self->{ERRSTR}='unable to write raw image';
+      $self->{ERRSTR} = $self->_error_as_msg();
       return undef;
     }
     $self->{DEBUG} && print "writing a raw file\n";
       return undef;
     }
     $self->{DEBUG} && print "writing a raw file\n";
@@ -1610,9 +1696,14 @@ sub filter {
     }
   }
   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},
+           imager => $self,
+           %{$filters{$input{'type'}}{defaults}},
+           %input );
   } else {
   } else {
-    %hsh=('image',$self->{IMG},%input);
+    %hsh=( image => $self->{IMG},
+           imager => $self,
+           %input );
   }
 
   my @cs=@{$filters{$input{'type'}}{callseq}};
   }
 
   my @cs=@{$filters{$input{'type'}}{callseq}};
@@ -1640,6 +1731,25 @@ sub filter {
   return $self;
 }
 
   return $self;
 }
 
+sub register_filter {
+  my $class = shift;
+  my %hsh = ( defaults => {}, @_ );
+
+  defined $hsh{type}
+    or die "register_filter() with no type\n";
+  defined $hsh{callsub}
+    or die "register_filter() with no callsub\n";
+  defined $hsh{callseq}
+    or die "register_filter() with no callseq\n";
+
+  exists $filters{$hsh{type}}
+    and return;
+
+  $filters{$hsh{type}} = \%hsh;
+
+  return 1;
+}
+
 # Scale an image to requested size and return the scaled version
 
 sub scale {
 # Scale an image to requested size and return the scaled version
 
 sub scale {
@@ -1648,41 +1758,84 @@ sub scale {
   my $img = Imager->new();
   my $tmp = Imager->new();
 
   my $img = Imager->new();
   my $tmp = Imager->new();
 
+  my $scalefactor = $opts{scalefactor};
+
   unless (defined wantarray) {
     my @caller = caller;
     warn "scale() called in void context - scale() returns the scaled image at $caller[1] line $caller[2]\n";
     return;
   }
 
   unless (defined wantarray) {
     my @caller = caller;
     warn "scale() called in void context - scale() returns the scaled image at $caller[1] line $caller[2]\n";
     return;
   }
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  unless ($self->{IMG}) { 
+    $self->_set_error('empty input image'); 
+    return undef;
+  }
 
 
+  # work out the scaling
   if ($opts{xpixels} and $opts{ypixels} and $opts{'type'}) {
   if ($opts{xpixels} and $opts{ypixels} and $opts{'type'}) {
-    my ($xpix,$ypix)=( $opts{xpixels}/$self->getwidth() , $opts{ypixels}/$self->getheight() );
-    if ($opts{'type'} eq 'min') { $opts{scalefactor}=min($xpix,$ypix); }
-    if ($opts{'type'} eq 'max') { $opts{scalefactor}=max($xpix,$ypix); }
-  } elsif ($opts{xpixels}) { $opts{scalefactor}=$opts{xpixels}/$self->getwidth(); }
-  elsif ($opts{ypixels}) { $opts{scalefactor}=$opts{ypixels}/$self->getheight(); }
+    my ($xpix, $ypix)=( $opts{xpixels} / $self->getwidth() , 
+                       $opts{ypixels} / $self->getheight() );
+    if ($opts{'type'} eq 'min') { 
+      $scalefactor = min($xpix,$ypix); 
+    }
+    elsif ($opts{'type'} eq 'max') {
+      $scalefactor = max($xpix,$ypix);
+    }
+    else {
+      $self->_set_error('invalid value for type parameter');
+      return undef;
+    }
+  } elsif ($opts{xpixels}) { 
+    $scalefactor = $opts{xpixels} / $self->getwidth();
+  }
+  elsif ($opts{ypixels}) { 
+    $scalefactor = $opts{ypixels}/$self->getheight();
+  }
+  elsif ($opts{constrain} && ref $opts{constrain}
+        && $opts{constrain}->can('constrain')) {
+    # we've been passed an Image::Math::Constrain object or something
+    # that looks like one
+    (undef, undef, $scalefactor)
+      = $opts{constrain}->constrain($self->getwidth, $self->getheight);
+    unless ($scalefactor) {
+      $self->_set_error('constrain method failed on constrain parameter');
+      return undef;
+    }
+  }
 
   if ($opts{qtype} eq 'normal') {
 
   if ($opts{qtype} eq 'normal') {
-    $tmp->{IMG}=i_scaleaxis($self->{IMG},$opts{scalefactor},0);
-    if ( !defined($tmp->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
-    $img->{IMG}=i_scaleaxis($tmp->{IMG},$opts{scalefactor},1);
-    if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
+    $tmp->{IMG} = i_scaleaxis($self->{IMG}, $scalefactor, 0);
+    if ( !defined($tmp->{IMG}) ) { 
+      $self->{ERRSTR} = 'unable to scale image';
+      return undef;
+    }
+    $img->{IMG}=i_scaleaxis($tmp->{IMG}, $scalefactor, 1);
+    if ( !defined($img->{IMG}) ) { 
+      $self->{ERRSTR}='unable to scale image'; 
+      return undef;
+    }
+
     return $img;
   }
     return $img;
   }
-  if ($opts{'qtype'} eq 'preview') {
-    $img->{IMG}=i_scale_nn($self->{IMG},$opts{'scalefactor'},$opts{'scalefactor'}); 
-    if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
+  elsif ($opts{'qtype'} eq 'preview') {
+    $img->{IMG} = i_scale_nn($self->{IMG}, $scalefactor, $scalefactor); 
+    if ( !defined($img->{IMG}) ) { 
+      $self->{ERRSTR}='unable to scale image'; 
+      return undef;
+    }
     return $img;
   }
     return $img;
   }
-  $self->{ERRSTR}='scale: invalid value for qtype'; return undef;
+  else {
+    $self->_set_error('invalid value for qtype parameter');
+    return undef;
+  }
 }
 
 # Scales only along the X axis
 
 sub scaleX {
 }
 
 # Scales only along the X axis
 
 sub scaleX {
-  my $self=shift;
-  my %opts=(scalefactor=>0.5,@_);
+  my $self = shift;
+  my %opts = ( scalefactor=>0.5, @_ );
 
   unless (defined wantarray) {
     my @caller = caller;
 
   unless (defined wantarray) {
     my @caller = caller;
@@ -1690,24 +1843,39 @@ sub scaleX {
     return;
   }
 
     return;
   }
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  unless ($self->{IMG}) { 
+    $self->{ERRSTR} = 'empty input image';
+    return undef;
+  }
 
   my $img = Imager->new();
 
 
   my $img = Imager->new();
 
-  if ($opts{pixels}) { $opts{scalefactor}=$opts{pixels}/$self->getwidth(); }
+  my $scalefactor = $opts{scalefactor};
 
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
-  $img->{IMG}=i_scaleaxis($self->{IMG},$opts{scalefactor},0);
+  if ($opts{pixels}) { 
+    $scalefactor = $opts{pixels} / $self->getwidth();
+  }
+
+  unless ($self->{IMG}) { 
+    $self->{ERRSTR}='empty input image'; 
+    return undef;
+  }
+
+  $img->{IMG} = i_scaleaxis($self->{IMG}, $scalefactor, 0);
+
+  if ( !defined($img->{IMG}) ) { 
+    $self->{ERRSTR} = 'unable to scale image'; 
+    return undef;
+  }
 
 
-  if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
   return $img;
 }
 
 # Scales only along the Y axis
 
 sub scaleY {
   return $img;
 }
 
 # Scales only along the Y axis
 
 sub scaleY {
-  my $self=shift;
-  my %opts=(scalefactor=>0.5,@_);
+  my $self = shift;
+  my %opts = ( scalefactor => 0.5, @_ );
 
   unless (defined wantarray) {
     my @caller = caller;
 
   unless (defined wantarray) {
     my @caller = caller;
@@ -1719,16 +1887,26 @@ sub scaleY {
 
   my $img = Imager->new();
 
 
   my $img = Imager->new();
 
-  if ($opts{pixels}) { $opts{scalefactor}=$opts{pixels}/$self->getheight(); }
+  my $scalefactor = $opts{scalefactor};
 
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
-  $img->{IMG}=i_scaleaxis($self->{IMG},$opts{scalefactor},1);
+  if ($opts{pixels}) { 
+    $scalefactor = $opts{pixels} / $self->getheight();
+  }
+
+  unless ($self->{IMG}) { 
+    $self->{ERRSTR} = 'empty input image'; 
+    return undef;
+  }
+  $img->{IMG}=i_scaleaxis($self->{IMG}, $scalefactor, 1);
+
+  if ( !defined($img->{IMG}) ) {
+    $self->{ERRSTR} = 'unable to scale image';
+    return undef;
+  }
 
 
-  if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
   return $img;
 }
 
   return $img;
 }
 
-
 # Transform returns a spatial transformation of the input image
 # this moves pixels to a new location in the returned image.
 # NOTE - should make a utility function to check transforms for
 # Transform returns a spatial transformation of the input image
 # this moves pixels to a new location in the returned image.
 # NOTE - should make a utility function to check transforms for
@@ -1888,8 +2066,14 @@ sub rubthrough {
   my $self=shift;
   my %opts=(tx => 0,ty => 0, @_);
 
   my $self=shift;
   my %opts=(tx => 0,ty => 0, @_);
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
-  unless ($opts{src} && $opts{src}->{IMG}) { $self->{ERRSTR}='empty input image for source'; return undef; }
+  unless ($self->{IMG}) { 
+    $self->{ERRSTR}='empty input image'; 
+    return undef;
+  }
+  unless ($opts{src} && $opts{src}->{IMG}) {
+    $self->{ERRSTR}='empty input image for src'; 
+    return undef;
+  }
 
   %opts = (src_minx => 0,
           src_miny => 0,
 
   %opts = (src_minx => 0,
           src_miny => 0,
@@ -1898,8 +2082,9 @@ sub rubthrough {
           %opts);
 
   unless (i_rubthru($self->{IMG}, $opts{src}->{IMG}, $opts{tx}, $opts{ty},
           %opts);
 
   unless (i_rubthru($self->{IMG}, $opts{src}->{IMG}, $opts{tx}, $opts{ty},
-         $opts{src_minx}, $opts{src_miny}, $opts{src_maxx}, $opts{src_maxy})) {
-    $self->{ERRSTR} = $self->_error_as_msg();
+                    $opts{src_minx}, $opts{src_miny}, 
+                    $opts{src_maxx}, $opts{src_maxy})) {
+    $self->_set_error($self->_error_as_msg());
     return undef;
   }
   return $self;
     return undef;
   }
   return $self;
@@ -1954,9 +2139,16 @@ sub rotate {
   elsif (defined $opts{radians} || defined $opts{degrees}) {
     my $amount = $opts{radians} || $opts{degrees} * 3.1415926535 / 180;
 
   elsif (defined $opts{radians} || defined $opts{degrees}) {
     my $amount = $opts{radians} || $opts{degrees} * 3.1415926535 / 180;
 
+    my $back = $opts{back};
     my $result = Imager->new;
     my $result = Imager->new;
-    if ($opts{back}) {
-      $result->{IMG} = i_rotate_exact($self->{IMG}, $amount, $opts{back});
+    if ($back) {
+      $back = _color($back);
+      unless ($back) {
+        $self->_set_error(Imager->errstr);
+        return undef;
+      }
+
+      $result->{IMG} = i_rotate_exact($self->{IMG}, $amount, $back);
     }
     else {
       $result->{IMG} = i_rotate_exact($self->{IMG}, $amount);
     }
     else {
       $result->{IMG} = i_rotate_exact($self->{IMG}, $amount);
@@ -2069,8 +2261,6 @@ sub box {
   return $self;
 }
 
   return $self;
 }
 
-# Draws an arc - this routine SUCKS and is buggy - it sometimes doesn't work when the arc is a convex polygon
-
 sub arc {
   my $self=shift;
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
 sub arc {
   my $self=shift;
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
@@ -2080,39 +2270,56 @@ sub arc {
            'x'=>$self->getwidth()/2,
            'y'=>$self->getheight()/2,
            'd1'=>0, 'd2'=>361, @_);
            'x'=>$self->getwidth()/2,
            'y'=>$self->getheight()/2,
            'd1'=>0, 'd2'=>361, @_);
-  if ($opts{fill}) {
-    unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
-      # assume it's a hash ref
-      require 'Imager/Fill.pm';
-      unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
-        $self->{ERRSTR} = $Imager::ERRSTR;
-        return;
+  if ($opts{aa}) {
+    if ($opts{fill}) {
+      unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
+       # assume it's a hash ref
+       require 'Imager/Fill.pm';
+       unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
+         $self->{ERRSTR} = $Imager::ERRSTR;
+         return;
+       }
+      }
+      i_arc_aa_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
+                    $opts{'d2'}, $opts{fill}{fill});
+    }
+    else {
+      my $color = _color($opts{'color'});
+      unless ($color) { 
+       $self->{ERRSTR} = $Imager::ERRSTR; 
+       return; 
+      }
+      if ($opts{d1} == 0 && $opts{d2} == 361 && $opts{aa}) {
+       i_circle_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'}, 
+                   $color);
+      }
+      else {
+       i_arc_aa($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
+                $opts{'d1'}, $opts{'d2'}, $color); 
       }
     }
       }
     }
-    i_arc_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
-                $opts{'d2'}, $opts{fill}{fill});
   }
   else {
   }
   else {
-    my $color = _color($opts{'color'});
-    unless ($color) { 
-      $self->{ERRSTR} = $Imager::ERRSTR; 
-      return; 
-    }
-    if ($opts{d1} == 0 && $opts{d2} == 361 && $opts{aa}) {
-      i_circle_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'}, 
-                  $color);
+    if ($opts{fill}) {
+      unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
+       # assume it's a hash ref
+       require 'Imager/Fill.pm';
+       unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
+         $self->{ERRSTR} = $Imager::ERRSTR;
+         return;
+       }
+      }
+      i_arc_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
+                 $opts{'d2'}, $opts{fill}{fill});
     }
     else {
     }
     else {
-      if ($opts{'d1'} <= $opts{'d2'}) { 
-        i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
-              $opts{'d1'}, $opts{'d2'}, $color); 
-      }
-      else {
-        i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
-              $opts{'d1'}, 361,         $color);
-        i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
-              0,           $opts{'d2'}, $color); 
+      my $color = _color($opts{'color'});
+      unless ($color) { 
+       $self->{ERRSTR} = $Imager::ERRSTR; 
+       return; 
       }
       }
+      i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
+           $opts{'d1'}, $opts{'d2'}, $color); 
     }
   }
 
     }
   }
 
@@ -2696,8 +2903,17 @@ sub getmask {
 sub setmask {
   my $self = shift;
   my %opts = @_;
 sub setmask {
   my $self = shift;
   my %opts = @_;
-  if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
+  if (!defined($self->{IMG})) { 
+    $self->{ERRSTR} = 'image is empty';
+    return undef;
+  }
+  unless (defined $opts{mask}) {
+    $self->_set_error("mask parameter required");
+    return;
+  }
   i_img_setmask( $self->{IMG} , $opts{mask} );
   i_img_setmask( $self->{IMG} , $opts{mask} );
+
+  1;
 }
 
 # Get number of colors in an image
 }
 
 # Get number of colors in an image
@@ -2719,7 +2935,7 @@ sub string {
   my %input=('x'=>0, 'y'=>0, @_);
   $input{string}||=$input{text};
 
   my %input=('x'=>0, 'y'=>0, @_);
   $input{string}||=$input{text};
 
-  unless(exists $input{string}) {
+  unless(defined $input{string}) {
     $self->{ERRSTR}="missing required parameter 'string'";
     return;
   }
     $self->{ERRSTR}="missing required parameter 'string'";
     return;
   }
@@ -2730,13 +2946,48 @@ sub string {
   }
 
   unless ($input{font}->draw(image=>$self, %input)) {
   }
 
   unless ($input{font}->draw(image=>$self, %input)) {
-    $self->{ERRSTR} = $self->_error_as_msg();
     return;
   }
 
   return $self;
 }
 
     return;
   }
 
   return $self;
 }
 
+sub align_string {
+  my $self = shift;
+
+  my $img;
+  if (ref $self) {
+    unless ($self->{IMG}) { 
+      $self->{ERRSTR}='empty input image'; 
+      return;
+    }
+    $img = $self;
+  }
+  else {
+    $img = undef;
+  }
+
+  my %input=('x'=>0, 'y'=>0, @_);
+  $input{string}||=$input{text};
+
+  unless(exists $input{string}) {
+    $self->_set_error("missing required parameter 'string'");
+    return;
+  }
+
+  unless($input{font}) {
+    $self->_set_error("missing required parameter 'font'");
+    return;
+  }
+
+  my @result;
+  unless (@result = $input{font}->align(image=>$img, %input)) {
+    return;
+  }
+
+  return wantarray ? @result : $result[0];
+}
+
 my @file_limit_names = qw/width height bytes/;
 
 sub set_file_limits {
 my @file_limit_names = qw/width height bytes/;
 
 sub set_file_limits {
@@ -2879,7 +3130,15 @@ sub parseiptc {
   return (caption=>$caption,photogr=>$photogr,headln=>$headln,credit=>$credit);
 }
 
   return (caption=>$caption,photogr=>$photogr,headln=>$headln,credit=>$credit);
 }
 
-# Autoload methods go after =cut, and are processed by the autosplit program.
+sub Inline {
+  my ($lang) = @_;
+
+  $lang eq 'C'
+    or die "Only C language supported";
+
+  require Imager::ExtUtils;
+  return Imager::ExtUtils->inline_config;
+}
 
 1;
 __END__
 
 1;
 __END__
@@ -2948,6 +3207,10 @@ Overview.
 
 =item *
 
 
 =item *
 
+L<Imager::Tutorial> - a brief introduction to Imager.
+
+=item *
+
 L<Imager::Cookbook> - how to do various things with Imager.
 
 =item *
 L<Imager::Cookbook> - how to do various things with Imager.
 
 =item *
@@ -3008,6 +3271,22 @@ L<Imager::Matrix2d> - Helper class for affine transformations.
 
 L<Imager::Fountain> - Helper for making gradient profiles.
 
 
 L<Imager::Fountain> - Helper for making gradient profiles.
 
+=item *
+
+L<Imager::API> - using Imager's C API
+
+=item *
+
+L<Imager::APIRef> - API function reference
+
+=item *
+
+L<Imager::Inline> - using Imager's C API from Inline::C
+
+=item *
+
+L<Imager::ExtUtils> - tools to get access to Imager's C API.
+
 =back
 
 =head2 Basic Overview
 =back
 
 =head2 Basic Overview
@@ -3043,13 +3322,15 @@ L<Imager::ImageTypes>.
 
 Where to find information on methods for Imager class objects.
 
 
 Where to find information on methods for Imager class objects.
 
-addcolors() -  L<Imager::ImageTypes>
+addcolors() -  L<Imager::ImageTypes/addcolors>
 
 
-addtag() -  L<Imager::ImageTypes> - add image tags
+addtag() -  L<Imager::ImageTypes/addtag> - add image tags
 
 arc() - L<Imager::Draw/arc>
 
 
 arc() - L<Imager::Draw/arc>
 
-bits() - L<Imager::ImageTypes> - number of bits per sample for the
+align_string() - L<Imager::Draw/align_string>
+
+bits() - L<Imager::ImageTypes/bits> - number of bits per sample for the
 image
 
 box() - L<Imager::Draw/box>
 image
 
 box() - L<Imager::Draw/box>
@@ -3065,54 +3346,54 @@ copy() - L<Imager::Transformations/copy>
 
 crop() - L<Imager::Transformations/crop> - extract part of an image
 
 
 crop() - L<Imager::Transformations/crop> - extract part of an image
 
-deltag() -  L<Imager::ImageTypes> - delete image tags
+deltag() -  L<Imager::ImageTypes/deltag> - delete image tags
 
 difference() - L<Imager::Filters/"Image Difference">
 
 
 difference() - L<Imager::Filters/"Image Difference">
 
-errstr() - L<Imager/"Basic Overview">
+errstr() - L<"Basic Overview">
 
 filter() - L<Imager::Filters>
 
 
 filter() - L<Imager::Filters>
 
-findcolor() - L<Imager::ImageTypes> - search the image palette, if it
+findcolor() - L<Imager::ImageTypes/findcolor> - search the image palette, if it
 has one
 
 flip() - L<Imager::Transformations/flip>
 
 flood_fill() - L<Imager::Draw/flood_fill>
 
 has one
 
 flip() - L<Imager::Transformations/flip>
 
 flood_fill() - L<Imager::Draw/flood_fill>
 
-getchannels() -  L<Imager::ImageTypes>
+getchannels() -  L<Imager::ImageTypes/getchannels>
 
 
-getcolorcount() -  L<Imager::ImageTypes>
+getcolorcount() -  L<Imager::ImageTypes/getcolorcount>
 
 
-getcolors() - L<Imager::ImageTypes> - get colors from the image
+getcolors() - L<Imager::ImageTypes/getcolors> - get colors from the image
 palette, if it has one
 
 get_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
 
 palette, if it has one
 
 get_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
 
-getheight() - L<Imager::ImageTypes>
+getheight() - L<Imager::ImageTypes/getwidth>
 
 
-getpixel() - L<Imager::Draw/setpixel and getpixel>
+getpixel() - L<Imager::Draw/getpixel>
 
 getsamples() - L<Imager::Draw/getsamples>
 
 getscanline() - L<Imager::Draw/getscanline>
 
 
 getsamples() - L<Imager::Draw/getsamples>
 
 getscanline() - L<Imager::Draw/getscanline>
 
-getwidth() - L<Imager::ImageTypes>
+getwidth() - L<Imager::ImageTypes/getwidth>
 
 
-img_set() - L<Imager::ImageTypes>
+img_set() - L<Imager::ImageTypes/img_set>
 
 line() - L<Imager::Draw/line>
 
 map() - L<Imager::Transformations/"Color Mappings"> - remap color
 channel values
 
 
 line() - L<Imager::Draw/line>
 
 map() - L<Imager::Transformations/"Color Mappings"> - remap color
 channel values
 
-masked() -  L<Imager::ImageTypes> - make a masked image
+masked() -  L<Imager::ImageTypes/masked> - make a masked image
 
 
-matrix_transform() - L<Imager::Engines/"Matrix Transformations">
+matrix_transform() - L<Imager::Engines/matrix_transform>
 
 
-maxcolors() - L<Imager::ImageTypes/maxcolor>
+maxcolors() - L<Imager::ImageTypes/maxcolors>
 
 
-new() - L<Imager::ImageTypes>
+new() - L<Imager::ImageTypes/new>
 
 open() - L<Imager::Files> - an alias for read()
 
 
 open() - L<Imager::Files> - an alias for read()
 
@@ -3134,33 +3415,36 @@ image and use the alpha channel
 
 scale() - L<Imager::Transformations/scale>
 
 
 scale() - L<Imager::Transformations/scale>
 
-setscanline() - L<Imager::Draw/setscanline>
-
 scaleX() - L<Imager::Transformations/scaleX>
 
 scaleY() - L<Imager::Transformations/scaleY>
 
 scaleX() - L<Imager::Transformations/scaleX>
 
 scaleY() - L<Imager::Transformations/scaleY>
 
-setcolors() - L<Imager::ImageTypes> - set palette colors in a paletted image
+setcolors() - L<Imager::ImageTypes/setcolors> - set palette colors in
+a paletted image
+
+setpixel() - L<Imager::Draw/setpixel>
 
 
-setpixel() - L<Imager::Draw/setpixel and getpixel>
+setscanline() - L<Imager::Draw/setscanline>
+
+settag() - L<Imager::ImageTypes/settag>
 
 set_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
 
 
 set_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
 
-string() - L<Imager::Font/string> - draw text on an image
+string() - L<Imager::Draw/string> - draw text on an image
 
 
-tags() -  L<Imager::ImageTypes> - fetch image tags
+tags() -  L<Imager::ImageTypes/tags> - fetch image tags
 
 
-to_paletted() -  L<Imager::ImageTypes>
+to_paletted() -  L<Imager::ImageTypes/to_paletted>
 
 
-to_rgb8() - L<Imager::ImageTypes>
+to_rgb8() - L<Imager::ImageTypes/to_rgb8>
 
 transform() - L<Imager::Engines/"transform">
 
 transform2() - L<Imager::Engines/"transform2">
 
 
 transform() - L<Imager::Engines/"transform">
 
 transform2() - L<Imager::Engines/"transform2">
 
-type() -  L<Imager::ImageTypes> - type of image (direct vs paletted)
+type() -  L<Imager::ImageTypes/type> - type of image (direct vs paletted)
 
 
-virtual() - L<Imager::ImageTypes> - whether the image has it's own
+virtual() - L<Imager::ImageTypes/virtual> - whether the image has it's own
 data
 
 write() - L<Imager::Files> - write an image to a file
 data
 
 write() - L<Imager::Files> - write an image to a file
@@ -3175,31 +3459,40 @@ animated GIF - L<Imager::File/"Writing an animated GIF">
 aspect ratio - L<Imager::ImageTypes/i_xres>,
 L<Imager::ImageTypes/i_yres>, L<Imager::ImageTypes/i_aspect_only>
 
 aspect ratio - L<Imager::ImageTypes/i_xres>,
 L<Imager::ImageTypes/i_yres>, L<Imager::ImageTypes/i_aspect_only>
 
+blend - alpha blending one image onto another
+L<Imager::Transformations/rubthrough>
+
 blur - L<Imager::Filters/guassian>, L<Imager::Filters/conv>
 
 boxes, drawing - L<Imager::Draw/box>
 
 blur - L<Imager::Filters/guassian>, L<Imager::Filters/conv>
 
 boxes, drawing - L<Imager::Draw/box>
 
+changes between image - L<Imager::Filter/"Image Difference">
+
 color - L<Imager::Color>
 
 color names - L<Imager::Color>, L<Imager::Color::Table>
 
 combine modes - L<Imager::Fill/combine>
 
 color - L<Imager::Color>
 
 color names - L<Imager::Color>, L<Imager::Color::Table>
 
 combine modes - L<Imager::Fill/combine>
 
+compare images - L<Imager::Filter/"Image Difference">
+
 contrast - L<Imager::Filter/contrast>, L<Imager::Filter/autolevels>
 
 convolution - L<Imager::Filter/conv>
 
 cropping - L<Imager::Transformations/crop>
 
 contrast - L<Imager::Filter/contrast>, L<Imager::Filter/autolevels>
 
 convolution - L<Imager::Filter/conv>
 
 cropping - L<Imager::Transformations/crop>
 
+C<diff> images - L<Imager::Filter/"Image Difference">
+
 dpi - L<Imager::ImageTypes/i_xres>
 
 drawing boxes - L<Imager::Draw/box>
 
 drawing lines - L<Imager::Draw/line>
 
 dpi - L<Imager::ImageTypes/i_xres>
 
 drawing boxes - L<Imager::Draw/box>
 
 drawing lines - L<Imager::Draw/line>
 
-drawing text - L<Imager::Font/string>
+drawing text - L<Imager::Font/string>, L<Imager::Font/align>
 
 
-error message - L<Imager/"Basic Overview">
+error message - L<"Basic Overview">
 
 files, font - L<Imager::Font>
 
 
 files, font - L<Imager::Font>
 
@@ -3259,11 +3552,17 @@ noise, filter - L<Imager::Filter/noise>
 noise, rendered - L<Imager::Filter/turbnoise>,
 L<Imager::Filter/radnoise>
 
 noise, rendered - L<Imager::Filter/turbnoise>,
 L<Imager::Filter/radnoise>
 
+paste - L<Imager::Transformations/paste>,
+L<Imager::Transformations/rubthrough>
+
+pseudo-color image - L<Imager::ImageTypes/to_paletted>,
+L<Imager::ImageTypes/new>
+
 posterize - L<Imager::Filter/postlevels>
 
 png files - L<Imager::Files>, L<Imager::Files/"PNG">
 
 posterize - L<Imager::Filter/postlevels>
 
 png files - L<Imager::Files>, L<Imager::Files/"PNG">
 
-pnm - L<Imager::Files/"PNM (Portable aNy Map">
+pnm - L<Imager::Files/"PNM (Portable aNy Map)">
 
 rectangles, drawing - L<Imager::Draw/box>
 
 
 rectangles, drawing - L<Imager::Draw/box>
 
@@ -3281,7 +3580,9 @@ L<Imager::ImageTypes/getheight>
 
 size, text - L<Imager::Font/bounding_box>
 
 
 size, text - L<Imager::Font/bounding_box>
 
-text, drawing - L<Imager::Font/string>, L<Imager::Font/align>,
+tags, image metadata - L<Imager::ImageTypes/"Tags">
+
+text, drawing - L<Imager::Draw/string>, L<Imager::Draw/align_string>,
 L<Imager::Font::Wrap>
 
 text, wrapping text in an area - L<Imager::Font::Wrap>
 L<Imager::Font::Wrap>
 
 text, wrapping text in an area - L<Imager::Font::Wrap>
@@ -3294,7 +3595,7 @@ unsharp mask - L<Imager::Filter/unsharpmask>
 
 watermark - L<Imager::Filter/watermark>
 
 
 watermark - L<Imager::Filter/watermark>
 
-writing an image - L<Imager::Files>
+writing an image to a file - L<Imager::Files>
 
 =head1 SUPPORT
 
 
 =head1 SUPPORT
 
@@ -3307,8 +3608,11 @@ To subscribe send a message with C<subscribe> in the body to:
 
 or use the form at:
 
 
 or use the form at:
 
-   http://www.molar.is/en/lists/imager-devel/
-   (annonymous is temporarily off due to spam)
+=over
+
+L<http://www.molar.is/en/lists/imager-devel/>
+
+=back
 
 where you can also find the mailing list archive.
 
 
 where you can also find the mailing list archive.
 
@@ -3318,7 +3622,11 @@ occupied or asleep, so please be patient.
 
 You can report bugs by pointing your browser at:
 
 
 You can report bugs by pointing your browser at:
 
-  https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Imager
+=over
+
+L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Imager>
+
+=back
 
 Please remember to include the versions of Imager, perl, supporting
 libraries, and any relevant code.  If you have specific images that
 
 Please remember to include the versions of Imager, perl, supporting
 libraries, and any relevant code.  If you have specific images that
@@ -3330,17 +3638,23 @@ Bugs are listed individually for relevant pod pages.
 
 =head1 AUTHOR
 
 
 =head1 AUTHOR
 
-Arnar M. Hrafnkelsson (addi@imager.perl.org) and Tony Cook
-(tony@imager.perl.org) See the README for a complete list.
+Arnar M. Hrafnkelsson and Tony Cook (tony@imager.perl.org) among
+others. See the README for a complete list.
 
 =head1 SEE ALSO
 
 
 =head1 SEE ALSO
 
-perl(1), Imager::ImageTypes(3), Imager::Files(3), Imager::Draw(3),
-Imager::Color(3), Imager::Fill(3), Imager::Font(3),
-Imager::Transformations(3), Imager::Engines(3), Imager::Filters(3),
-Imager::Expr(3), Imager::Matrix2d(3), Imager::Fountain(3)
+L<perl>(1), L<Imager::ImageTypes>(3), L<Imager::Files>(3),
+L<Imager::Draw>(3), L<Imager::Color>(3), L<Imager::Fill>(3),
+L<Imager::Font>(3), L<Imager::Transformations>(3),
+L<Imager::Engines>(3), L<Imager::Filters>(3), L<Imager::Expr>(3),
+L<Imager::Matrix2d>(3), L<Imager::Fountain>(3)
+
+L<http://imager.perl.org/>
+
+L<Affix::Infix2Postfix>(3), L<Parse::RecDescent>(3)
+
+Other perl imaging modules include:
 
 
-Affix::Infix2Postfix(3), Parse::RecDescent(3)
-http://imager.perl.org/
+L<GD>(3), L<Image::Magick>(3), L<Graphics::Magick>(3).
 
 =cut
 
 =cut