]> git.imager.perl.org - imager.git/blobdiff - Imager.pm
- attempt to work around the test failure at
[imager.git] / Imager.pm
index 45847c2ccea90fc166cc25f39917feb0238e1725..36bd47dbbae8f874ada8e12126b24ed614c76679 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -143,13 +143,28 @@ use Imager::Font;
                  unload_plugin
                 )]);
 
                  unload_plugin
                 )]);
 
+# registered file readers
+my %readers;
+
+# registered file writers
+my %writers;
+
+# modules we attempted to autoload
+my %attempted_to_load;
+
 BEGIN {
   require Exporter;
 BEGIN {
   require Exporter;
-  require DynaLoader;
-
-  $VERSION = '0.45';
-  @ISA = qw(Exporter DynaLoader);
-  bootstrap Imager $VERSION;
+  @ISA = qw(Exporter);
+  $VERSION = '0.60';
+  eval {
+    require XSLoader;
+    XSLoader::load(Imager => $VERSION);
+    1;
+  } or do {
+    require DynaLoader;
+    push @ISA, 'DynaLoader';
+    bootstrap Imager $VERSION;
+  }
 }
 
 BEGIN {
 }
 
 BEGIN {
@@ -235,11 +250,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 +396,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} =
@@ -401,8 +432,8 @@ BEGIN {
 #}
 
 sub init_log {
 #}
 
 sub init_log {
-       m_init_log($_[0],$_[1]);
-       log_entry("Imager $VERSION starting\n", 1);
+  i_init_log($_[0],$_[1]);
+  i_log_entry("Imager $VERSION starting\n", 1);
 }
 
 
 }
 
 
@@ -506,12 +537,7 @@ sub _color {
         $result = Imager::Color->new(%$arg);
       }
       elsif ($copy =~ /^ARRAY\(/) {
         $result = Imager::Color->new(%$arg);
       }
       elsif ($copy =~ /^ARRAY\(/) {
-        if (grep $_ > 1, @$arg) {
-          $result = Imager::Color->new(@$arg);
-        }
-        else {
-          $result = Imager::Color::Float->new(@$arg);
-        }
+       $result = Imager::Color->new(@$arg);
       }
       else {
         $Imager::ERRSTR = "Not a color";
       }
       else {
         $Imager::ERRSTR = "Not a color";
@@ -526,6 +552,15 @@ sub _color {
   return $result;
 }
 
   return $result;
 }
 
+sub _valid_image {
+  my ($self) = @_;
+
+  $self->{IMG} and return 1;
+
+  $self->_set_error('empty input image');
+
+  return;
+}
 
 #
 # Methods to be called on objects.
 
 #
 # Methods to be called on objects.
@@ -567,8 +602,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 +610,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_maxy}) {
+      $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 > $b 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??
 }
 
@@ -671,7 +754,10 @@ sub crop {
     $self->_set_error("resulting image would have no content");
     return;
   }
     $self->_set_error("resulting image would have no content");
     return;
   }
-
+  if( $r < $l or $b < $t ) {
+    $self->_set_error("attempting to crop outside of the image");
+    return;
+  }
   my $dst = $self->_sametype(xsize=>$r-$l, ysize=>$b-$t);
 
   i_copyto($dst->{IMG},$self->{IMG},$l,$t,$r,$b,0,0);
   my $dst = $self->_sametype(xsize=>$r-$l, ysize=>$b-$t);
 
   i_copyto($dst->{IMG},$self->{IMG},$l,$t,$r,$b,0,0);
@@ -801,7 +887,7 @@ sub to_rgb8 {
 
   unless (defined wantarray) {
     my @caller = caller;
 
   unless (defined wantarray) {
     my @caller = caller;
-    warn "to_rgb8() called in void context - to_rgb8() returns the cropped image at $caller[1] line $caller[2]\n";
+    warn "to_rgb8() called in void context - to_rgb8() returns the converted image at $caller[1] line $caller[2]\n";
     return;
   }
 
     return;
   }
 
@@ -814,21 +900,70 @@ sub to_rgb8 {
   return $result;
 }
 
   return $result;
 }
 
+# convert a paletted (or any image) to an 8-bit/channel RGB images
+sub to_rgb16 {
+  my $self = shift;
+  my $result;
+
+  unless (defined wantarray) {
+    my @caller = caller;
+    warn "to_rgb16() called in void context - to_rgb8() returns the converted image at $caller[1] line $caller[2]\n";
+    return;
+  }
+
+  if ($self->{IMG}) {
+    $result = Imager->new;
+    $result->{IMG} = i_img_to_rgb16($self->{IMG})
+      or undef $result;
+  }
+
+  return $result;
+}
+
 sub addcolors {
   my $self = shift;
   my %opts = (colors=>[], @_);
 
 sub addcolors {
   my $self = shift;
   my %opts = (colors=>[], @_);
 
-  @{$opts{colors}} or return undef;
+  unless ($self->{IMG}) {
+    $self->_set_error("empty input image");
+    return;
+  }
+
+  my @colors = @{$opts{colors}}
+    or return undef;
+
+  for my $color (@colors) {
+    $color = _color($color);
+    unless ($color) {
+      $self->_set_error($Imager::ERRSTR);
+      return;
+    }  
+  }
 
 
-  $self->{IMG} and i_addcolors($self->{IMG}, @{$opts{colors}});
+  return i_addcolors($self->{IMG}, @colors);
 }
 
 sub setcolors {
   my $self = shift;
   my %opts = (start=>0, colors=>[], @_);
 }
 
 sub setcolors {
   my $self = shift;
   my %opts = (start=>0, colors=>[], @_);
-  @{$opts{colors}} or return undef;
 
 
-  $self->{IMG} and i_setcolors($self->{IMG}, $opts{start}, @{$opts{colors}});
+  unless ($self->{IMG}) {
+    $self->_set_error("empty input image");
+    return;
+  }
+
+  my @colors = @{$opts{colors}}
+    or return undef;
+
+  for my $color (@colors) {
+    $color = _color($color);
+    unless ($color) {
+      $self->_set_error($Imager::ERRSTR);
+      return;
+    }  
+  }
+
+  return i_setcolors($self->{IMG}, $opts{start}, @colors);
 }
 
 sub getcolors {
 }
 
 sub getcolors {
@@ -1011,9 +1146,9 @@ sub settag {
 sub _get_reader_io {
   my ($self, $input) = @_;
 
 sub _get_reader_io {
   my ($self, $input) = @_;
 
-       if ($input->{io}) {
-               return $input->{io}, undef;
-       }
+  if ($input->{io}) {
+    return $input->{io}, undef;
+  }
   elsif ($input->{fd}) {
     return io_new_fd($input->{fd});
   }
   elsif ($input->{fd}) {
     return io_new_fd($input->{fd});
   }
@@ -1062,7 +1197,10 @@ sub _get_reader_io {
 sub _get_writer_io {
   my ($self, $input, $type) = @_;
 
 sub _get_writer_io {
   my ($self, $input, $type) = @_;
 
-  if ($input->{fd}) {
+  if ($input->{io}) {
+    return $input->{io};
+  }
+  elsif ($input->{fd}) {
     return io_new_fd($input->{fd});
   }
   elsif ($input->{fh}) {
     return io_new_fd($input->{fd});
   }
   elsif ($input->{fh}) {
@@ -1133,6 +1271,12 @@ sub read {
     return undef;
   }
 
     return undef;
   }
 
+  _reader_autoload($input{type});
+
+  if ($readers{$input{type}} && $readers{$input{type}}{single}) {
+    return $readers{$input{type}}{single}->($self, $IO, %input);
+  }
+
   unless ($formats{$input{'type'}}) {
     $self->_set_error("format '$input{'type'}' not supported");
     return;
   unless ($formats{$input{'type'}}) {
     $self->_set_error("format '$input{'type'}' not supported");
     return;
@@ -1148,11 +1292,13 @@ sub read {
     return $self;
   }
 
     return $self;
   }
 
+  my $allow_incomplete = $input{allow_incomplete};
+  defined $allow_incomplete or $allow_incomplete = 0;
+
   if ( $input{'type'} eq 'tiff' ) {
     my $page = $input{'page'};
     defined $page or $page = 0;
   if ( $input{'type'} eq 'tiff' ) {
     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 ); 
+    $self->{IMG}=i_readtiff_wiol( $IO, $allow_incomplete, $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;
     }
@@ -1161,9 +1307,10 @@ sub read {
   }
 
   if ( $input{'type'} eq 'pnm' ) {
   }
 
   if ( $input{'type'} eq 'pnm' ) {
-    $self->{IMG}=i_readpnm_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
+    $self->{IMG}=i_readpnm_wiol( $IO, $allow_incomplete );
     if ( !defined($self->{IMG}) ) {
     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;
@@ -1179,7 +1326,7 @@ sub read {
   }
 
   if ( $input{'type'} eq 'bmp' ) {
   }
 
   if ( $input{'type'} eq 'bmp' ) {
-    $self->{IMG}=i_readbmp_wiol( $IO );
+    $self->{IMG}=i_readbmp_wiol( $IO, $allow_incomplete );
     if ( !defined($self->{IMG}) ) {
       $self->{ERRSTR}=$self->_error_as_msg();
       return undef;
     if ( !defined($self->{IMG}) ) {
       $self->{ERRSTR}=$self->_error_as_msg();
       return undef;
@@ -1209,7 +1356,7 @@ sub read {
       my $page = $input{'page'};
       defined $page or $page = 0;
       $self->{IMG} = i_readgif_single_wiol( $IO, $page );
       my $page = $input{'page'};
       defined $page or $page = 0;
       $self->{IMG} = i_readgif_single_wiol( $IO, $page );
-      if ($input{colors}) {
+      if ($self->{IMG} && $input{colors}) {
        ${ $input{colors} } =
          [ i_getcolors($self->{IMG}, 0, i_colorcount($self->{IMG})) ];
       }
        ${ $input{colors} } =
          [ i_getcolors($self->{IMG}, 0, i_colorcount($self->{IMG})) ];
       }
@@ -1231,16 +1378,6 @@ sub read {
     $self->{DEBUG} && print "loading a tga file\n";
   }
 
     $self->{DEBUG} && print "loading a tga file\n";
   }
 
-  if ( $input{'type'} eq 'rgb' ) {
-    $self->{IMG}=i_readrgb_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
-    if ( !defined($self->{IMG}) ) {
-      $self->{ERRSTR}=$self->_error_as_msg();
-      return undef;
-    }
-    $self->{DEBUG} && print "loading a tga file\n";
-  }
-
-
   if ( $input{'type'} eq 'raw' ) {
     my %params=(datachannels=>3,storechannels=>3,interleave=>1,%input);
 
   if ( $input{'type'} eq 'raw' ) {
     my %params=(datachannels=>3,storechannels=>3,interleave=>1,%input);
 
@@ -1256,7 +1393,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";
@@ -1265,6 +1402,106 @@ sub read {
   return $self;
 }
 
   return $self;
 }
 
+sub register_reader {
+  my ($class, %opts) = @_;
+
+  defined $opts{type}
+    or die "register_reader called with no type parameter\n";
+
+  my $type = $opts{type};
+
+  defined $opts{single} || defined $opts{multiple}
+    or die "register_reader called with no single or multiple parameter\n";
+
+  $readers{$type} = {  };
+  if ($opts{single}) {
+    $readers{$type}{single} = $opts{single};
+  }
+  if ($opts{multiple}) {
+    $readers{$type}{multiple} = $opts{multiple};
+  }
+
+  return 1;
+}
+
+sub register_writer {
+  my ($class, %opts) = @_;
+
+  defined $opts{type}
+    or die "register_writer called with no type parameter\n";
+
+  my $type = $opts{type};
+
+  defined $opts{single} || defined $opts{multiple}
+    or die "register_writer called with no single or multiple parameter\n";
+
+  $writers{$type} = {  };
+  if ($opts{single}) {
+    $writers{$type}{single} = $opts{single};
+  }
+  if ($opts{multiple}) {
+    $writers{$type}{multiple} = $opts{multiple};
+  }
+
+  return 1;
+}
+
+# probes for an Imager::File::whatever module
+sub _reader_autoload {
+  my $type = shift;
+
+  return if $formats{$type} || $readers{$type};
+
+  return unless $type =~ /^\w+$/;
+
+  my $file = "Imager/File/\U$type\E.pm";
+
+  unless ($attempted_to_load{$file}) {
+    eval {
+      ++$attempted_to_load{$file};
+      require $file;
+    };
+    if ($@) {
+      # try to get a reader specific module
+      my $file = "Imager/File/\U$type\EReader.pm";
+      unless ($attempted_to_load{$file}) {
+       eval {
+         ++$attempted_to_load{$file};
+         require $file;
+       };
+      }
+    }
+  }
+}
+
+# probes for an Imager::File::whatever module
+sub _writer_autoload {
+  my $type = shift;
+
+  return if $formats{$type} || $readers{$type};
+
+  return unless $type =~ /^\w+$/;
+
+  my $file = "Imager/File/\U$type\E.pm";
+
+  unless ($attempted_to_load{$file}) {
+    eval {
+      ++$attempted_to_load{$file};
+      require $file;
+    };
+    if ($@) {
+      # try to get a writer specific module
+      my $file = "Imager/File/\U$type\EWriter.pm";
+      unless ($attempted_to_load{$file}) {
+       eval {
+         ++$attempted_to_load{$file};
+         require $file;
+       };
+      }
+    }
+  }
+}
+
 sub _fix_gif_positions {
   my ($opts, $opt, $msg, @imgs) = @_;
 
 sub _fix_gif_positions {
   my ($opts, $opt, $msg, @imgs) = @_;
 
@@ -1379,96 +1616,111 @@ sub write {
     return undef;
   }
 
     return undef;
   }
 
-  if (!$formats{$input{'type'}}) { $self->{ERRSTR}='format not supported'; return undef; }
-
-  my ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
-    or return undef;
+  _writer_autoload($input{type});
 
 
-  if ($input{'type'} eq 'tiff') {
-    $self->_set_opts(\%input, "tiff_", $self)
-      or return undef;
-    $self->_set_opts(\%input, "exif_", $self)
+  my ($IO, $fh);
+  if ($writers{$input{type}} && $writers{$input{type}}{single}) {
+    ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
       or return undef;
 
       or return undef;
 
-    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';
-       return undef;
-      }
-    } else {
-      if (!i_writetiff_wiol($self->{IMG}, $IO)) {
-       $self->{ERRSTR}='Could not write to buffer';
-       return undef;
-      }
-    }
-  } elsif ( $input{'type'} eq 'pnm' ) {
-    $self->_set_opts(\%input, "pnm_", $self)
-      or return undef;
-    if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
-      $self->{ERRSTR}='unable to write pnm image';
-      return undef;
-    }
-    $self->{DEBUG} && print "writing a pnm file\n";
-  } elsif ( $input{'type'} eq 'raw' ) {
-    $self->_set_opts(\%input, "raw_", $self)
+    $writers{$input{type}}{single}->($self, $IO, %input)
       or return undef;
       or return undef;
-    if ( !i_writeraw_wiol($self->{IMG},$IO) ) {
-      $self->{ERRSTR}='unable to write raw image';
-      return undef;
-    }
-    $self->{DEBUG} && print "writing a raw file\n";
-  } elsif ( $input{'type'} eq 'png' ) {
-    $self->_set_opts(\%input, "png_", $self)
-      or return undef;
-    if ( !i_writepng_wiol($self->{IMG}, $IO) ) {
-      $self->{ERRSTR}='unable to write png image';
-      return undef;
-    }
-    $self->{DEBUG} && print "writing a png file\n";
-  } elsif ( $input{'type'} eq 'jpeg' ) {
-    $self->_set_opts(\%input, "jpeg_", $self)
-      or return undef;
-    $self->_set_opts(\%input, "exif_", $self)
-      or return undef;
-    if ( !i_writejpeg_wiol($self->{IMG}, $IO, $input{jpegquality})) {
-      $self->{ERRSTR} = $self->_error_as_msg();
-      return undef;
-    }
-    $self->{DEBUG} && print "writing a jpeg file\n";
-  } elsif ( $input{'type'} eq 'bmp' ) {
-    $self->_set_opts(\%input, "bmp_", $self)
-      or return undef;
-    if ( !i_writebmp_wiol($self->{IMG}, $IO) ) {
-      $self->{ERRSTR}='unable to write bmp image';
-      return undef;
-    }
-    $self->{DEBUG} && print "writing a bmp file\n";
-  } elsif ( $input{'type'} eq 'tga' ) {
-    $self->_set_opts(\%input, "tga_", $self)
-      or return undef;
-
-    if ( !i_writetga_wiol($self->{IMG}, $IO, $input{wierdpack}, $input{compress}, $input{idstring}) ) {
-      $self->{ERRSTR}=$self->_error_as_msg();
+  }
+  else {
+    if (!$formats{$input{'type'}}) { 
+      $self->{ERRSTR}='format not supported'; 
       return undef;
     }
       return undef;
     }
-    $self->{DEBUG} && print "writing a tga file\n";
-  } elsif ( $input{'type'} eq 'gif' ) {
-    $self->_set_opts(\%input, "gif_", $self)
+    
+    ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
       or return undef;
       or return undef;
-    # compatibility with the old interfaces
-    if ($input{gifquant} eq 'lm') {
-      $input{make_colors} = 'addi';
-      $input{translate} = 'perturb';
-      $input{perturb} = $input{lmdither};
-    } elsif ($input{gifquant} eq 'gen') {
-      # just pass options through
-    } else {
-      $input{make_colors} = 'webmap'; # ignored
-      $input{translate} = 'giflib';
-    }
-    if (!i_writegif_wiol($IO, \%input, $self->{IMG})) {
-      $self->{ERRSTR} = $self->_error_as_msg;
-      return;
+    
+    if ($input{'type'} eq 'tiff') {
+      $self->_set_opts(\%input, "tiff_", $self)
+        or return undef;
+      $self->_set_opts(\%input, "exif_", $self)
+        or return undef;
+      
+      if (defined $input{class} && $input{class} eq 'fax') {
+        if (!i_writetiff_wiol_faxable($self->{IMG}, $IO, $input{fax_fine})) {
+          $self->{ERRSTR} = $self->_error_as_msg();
+          return undef;
+        }
+      } else {
+        if (!i_writetiff_wiol($self->{IMG}, $IO)) {
+          $self->{ERRSTR} = $self->_error_as_msg();
+          return undef;
+        }
+      }
+    } elsif ( $input{'type'} eq 'pnm' ) {
+      $self->_set_opts(\%input, "pnm_", $self)
+        or return undef;
+      if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
+        $self->{ERRSTR} = $self->_error_as_msg();
+        return undef;
+      }
+      $self->{DEBUG} && print "writing a pnm file\n";
+    } elsif ( $input{'type'} eq 'raw' ) {
+      $self->_set_opts(\%input, "raw_", $self)
+        or return undef;
+      if ( !i_writeraw_wiol($self->{IMG},$IO) ) {
+        $self->{ERRSTR} = $self->_error_as_msg();
+        return undef;
+      }
+      $self->{DEBUG} && print "writing a raw file\n";
+    } elsif ( $input{'type'} eq 'png' ) {
+      $self->_set_opts(\%input, "png_", $self)
+        or return undef;
+      if ( !i_writepng_wiol($self->{IMG}, $IO) ) {
+        $self->{ERRSTR}='unable to write png image';
+        return undef;
+      }
+      $self->{DEBUG} && print "writing a png file\n";
+    } elsif ( $input{'type'} eq 'jpeg' ) {
+      $self->_set_opts(\%input, "jpeg_", $self)
+        or return undef;
+      $self->_set_opts(\%input, "exif_", $self)
+        or return undef;
+      if ( !i_writejpeg_wiol($self->{IMG}, $IO, $input{jpegquality})) {
+        $self->{ERRSTR} = $self->_error_as_msg();
+        return undef;
+      }
+      $self->{DEBUG} && print "writing a jpeg file\n";
+    } elsif ( $input{'type'} eq 'bmp' ) {
+      $self->_set_opts(\%input, "bmp_", $self)
+        or return undef;
+      if ( !i_writebmp_wiol($self->{IMG}, $IO) ) {
+       $self->{ERRSTR} = $self->_error_as_msg;
+        return undef;
+      }
+      $self->{DEBUG} && print "writing a bmp file\n";
+    } elsif ( $input{'type'} eq 'tga' ) {
+      $self->_set_opts(\%input, "tga_", $self)
+        or return undef;
+      
+      if ( !i_writetga_wiol($self->{IMG}, $IO, $input{wierdpack}, $input{compress}, $input{idstring}) ) {
+        $self->{ERRSTR}=$self->_error_as_msg();
+        return undef;
+      }
+      $self->{DEBUG} && print "writing a tga file\n";
+    } elsif ( $input{'type'} eq 'gif' ) {
+      $self->_set_opts(\%input, "gif_", $self)
+        or return undef;
+      # compatibility with the old interfaces
+      if ($input{gifquant} eq 'lm') {
+        $input{make_colors} = 'addi';
+        $input{translate} = 'perturb';
+        $input{perturb} = $input{lmdither};
+      } elsif ($input{gifquant} eq 'gen') {
+        # just pass options through
+      } else {
+        $input{make_colors} = 'webmap'; # ignored
+        $input{translate} = 'giflib';
+      }
+      if (!i_writegif_wiol($IO, \%input, $self->{IMG})) {
+        $self->{ERRSTR} = $self->_error_as_msg;
+        return;
+      }
     }
   }
 
     }
   }
 
@@ -1486,10 +1738,12 @@ sub write {
 sub write_multi {
   my ($class, $opts, @images) = @_;
 
 sub write_multi {
   my ($class, $opts, @images) = @_;
 
-  if (!$opts->{'type'} && $opts->{'file'}) {
-    $opts->{'type'} = $FORMATGUESS->($opts->{'file'});
+  my $type = $opts->{type};
+
+  if (!$type && $opts->{'file'}) {
+    $type = $FORMATGUESS->($opts->{'file'});
   }
   }
-  unless ($opts->{'type'}) {
+  unless ($type) {
     $class->_set_error('type parameter missing and not possible to guess from extension');
     return;
   }
     $class->_set_error('type parameter missing and not possible to guess from extension');
     return;
   }
@@ -1501,60 +1755,111 @@ sub write_multi {
   $class->_set_opts($opts, "i_", @images)
     or return;
   my @work = map $_->{IMG}, @images;
   $class->_set_opts($opts, "i_", @images)
     or return;
   my @work = map $_->{IMG}, @images;
-  my ($IO, $file) = $class->_get_writer_io($opts, $opts->{'type'})
-    or return undef;
-  if ($opts->{'type'} eq 'gif') {
-    $class->_set_opts($opts, "gif_", @images)
-      or return;
-    my $gif_delays = $opts->{gif_delays};
-    local $opts->{gif_delays} = $gif_delays;
-    if ($opts->{gif_delays} && !ref $opts->{gif_delays}) {
-      # assume the caller wants the same delay for each frame
-      $opts->{gif_delays} = [ ($gif_delays) x @images ];
-    }
-    my $res = i_writegif_wiol($IO, $opts, @work);
-    $res or $class->_set_error($class->_error_as_msg());
-    return $res;
-  }
-  elsif ($opts->{'type'} eq 'tiff') {
-    $class->_set_opts($opts, "tiff_", @images)
-      or return;
-    $class->_set_opts($opts, "exif_", @images)
-      or return;
-    my $res;
-    $opts->{fax_fine} = 1 unless exists $opts->{fax_fine};
-    if ($opts->{'class'} && $opts->{'class'} eq 'fax') {
-      $res = i_writetiff_multi_wiol_faxable($IO, $opts->{fax_fine}, @work);
+
+  _writer_autoload($type);
+
+  my ($IO, $file);
+  if ($writers{$type} && $writers{$type}{multiple}) {
+    ($IO, $file) = $class->_get_writer_io($opts, $type)
+      or return undef;
+
+    $writers{$type}{multiple}->($class, $IO, $opts, @images)
+      or return undef;
+  }
+  else {
+    if (!$formats{$type}) { 
+      $class->_set_error("format $type not supported"); 
+      return undef;
+    }
+    
+    ($IO, $file) = $class->_get_writer_io($opts, $type)
+      or return undef;
+    
+    if ($type eq 'gif') {
+      $class->_set_opts($opts, "gif_", @images)
+        or return;
+      my $gif_delays = $opts->{gif_delays};
+      local $opts->{gif_delays} = $gif_delays;
+      if ($opts->{gif_delays} && !ref $opts->{gif_delays}) {
+        # assume the caller wants the same delay for each frame
+        $opts->{gif_delays} = [ ($gif_delays) x @images ];
+      }
+      unless (i_writegif_wiol($IO, $opts, @work)) {
+        $class->_set_error($class->_error_as_msg());
+        return undef;
+      }
+    }
+    elsif ($type eq 'tiff') {
+      $class->_set_opts($opts, "tiff_", @images)
+        or return;
+      $class->_set_opts($opts, "exif_", @images)
+        or return;
+      my $res;
+      $opts->{fax_fine} = 1 unless exists $opts->{fax_fine};
+      if ($opts->{'class'} && $opts->{'class'} eq 'fax') {
+        $res = i_writetiff_multi_wiol_faxable($IO, $opts->{fax_fine}, @work);
+      }
+      else {
+        $res = i_writetiff_multi_wiol($IO, @work);
+      }
+      unless ($res) {
+        $class->_set_error($class->_error_as_msg());
+        return undef;
+      }
     }
     else {
     }
     else {
-      $res = i_writetiff_multi_wiol($IO, @work);
+      if (@images == 1) {
+       unless ($images[0]->write(%$opts, io => $IO, type => $type)) {
+         return 1;
+       }
+      }
+      else {
+       $ERRSTR = "Sorry, write_multi doesn't support $type yet";
+       return 0;
+      }
     }
     }
-    $res or $class->_set_error($class->_error_as_msg());
-    return $res;
   }
   }
-  else {
-    $ERRSTR = "Sorry, write_multi doesn't support $opts->{'type'} yet";
-    return 0;
+
+  if (exists $opts->{'data'}) {
+    my $data = io_slurp($IO);
+    if (!$data) {
+      Imager->_set_error('Could not slurp from buffer');
+      return undef;
+    }
+    ${$opts->{data}} = $data;
   }
   }
+  return 1;
 }
 
 # read multiple images from a file
 sub read_multi {
   my ($class, %opts) = @_;
 
 }
 
 # read multiple images from a file
 sub read_multi {
   my ($class, %opts) = @_;
 
-  if ($opts{file} && !exists $opts{'type'}) {
+  my ($IO, $file) = $class->_get_reader_io(\%opts, $opts{'type'})
+    or return;
+
+  my $type = $opts{'type'};
+  unless ($type) {
+    $type = i_test_format_probe($IO, -1);
+  }
+
+  if ($opts{file} && !$type) {
     # guess the type 
     # guess the type 
-    my $type = $FORMATGUESS->($opts{file});
-    $opts{'type'} = $type;
+    $type = $FORMATGUESS->($opts{file});
   }
   }
-  unless ($opts{'type'}) {
+
+  unless ($type) {
     $ERRSTR = "No type parameter supplied and it couldn't be guessed";
     return;
   }
 
     $ERRSTR = "No type parameter supplied and it couldn't be guessed";
     return;
   }
 
-  my ($IO, $file) = $class->_get_reader_io(\%opts, $opts{'type'})
-    or return;
-  if ($opts{'type'} eq 'gif') {
+  _reader_autoload($type);
+
+  if ($readers{$type} && $readers{$type}{multiple}) {
+    return $readers{$type}{multiple}->($IO, %opts);
+  }
+
+  if ($type eq 'gif') {
     my @imgs;
     @imgs = i_readgif_multi_wiol($IO);
     if (@imgs) {
     my @imgs;
     @imgs = i_readgif_multi_wiol($IO);
     if (@imgs) {
@@ -1567,7 +1872,7 @@ sub read_multi {
       return;
     }
   }
       return;
     }
   }
-  elsif ($opts{'type'} eq 'tiff') {
+  elsif ($type eq 'tiff') {
     my @imgs = i_readtiff_multi_wiol($IO, -1);
     if (@imgs) {
       return map { 
     my @imgs = i_readtiff_multi_wiol($IO, -1);
     if (@imgs) {
       return map { 
@@ -1579,8 +1884,14 @@ sub read_multi {
       return;
     }
   }
       return;
     }
   }
+  else {
+    my $img = Imager->new;
+    if ($img->read(%opts, io => $IO, type => $type)) {
+      return ( $img );
+    }
+  }
 
 
-  $ERRSTR = "Cannot read multiple images from $opts{'type'} files";
+  $ERRSTR = "Cannot read multiple images from $type files";
   return;
 }
 
   return;
 }
 
@@ -1625,9 +1936,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}};
@@ -1655,13 +1971,33 @@ 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 {
   my $self=shift;
 # Scale an image to requested size and return the scaled version
 
 sub scale {
   my $self=shift;
-  my %opts=(scalefactor=>0.5,'type'=>'max',qtype=>'normal',@_);
+  my %opts=('type'=>'max',qtype=>'normal',@_);
   my $img = Imager->new();
   my $tmp = Imager->new();
   my $img = Imager->new();
   my $tmp = Imager->new();
+  my ($x_scale, $y_scale);
 
   unless (defined wantarray) {
     my @caller = caller;
 
   unless (defined wantarray) {
     my @caller = caller;
@@ -1669,35 +2005,110 @@ sub scale {
     return;
   }
 
     return;
   }
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  unless ($self->{IMG}) { 
+    $self->_set_error('empty input image'); 
+    return undef;
+  }
+
+  if ($opts{'xscalefactor'} && $opts{'yscalefactor'}) {
+    $x_scale = $opts{'xscalefactor'};
+    $y_scale = $opts{'yscalefactor'};
+  }
+  elsif ($opts{'xscalefactor'}) {
+    $x_scale = $opts{'xscalefactor'};
+    $y_scale = $opts{'scalefactor'} || $x_scale;
+  }
+  elsif ($opts{'yscalefactor'}) {
+    $y_scale = $opts{'yscalefactor'};
+    $x_scale = $opts{'scalefactor'} || $y_scale;
+  }
+  else {
+    $x_scale = $y_scale = $opts{'scalefactor'} || 0.5;
+  }
 
 
+  # 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') { 
+      $x_scale = $y_scale = _min($xpix,$ypix); 
+    }
+    elsif ($opts{'type'} eq 'max') {
+      $x_scale = $y_scale = _max($xpix,$ypix);
+    }
+    elsif ($opts{'type'} eq 'nonprop' || $opts{'type'} eq 'non-proportional') {
+      $x_scale = $xpix;
+      $y_scale = $ypix;
+    }
+    else {
+      $self->_set_error('invalid value for type parameter');
+      return undef;
+    }
+  } elsif ($opts{xpixels}) { 
+    $x_scale = $y_scale = $opts{xpixels} / $self->getwidth();
+  }
+  elsif ($opts{ypixels}) { 
+    $x_scale = $y_scale = $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
+    my $scalefactor;
+    (undef, undef, $scalefactor)
+      = $opts{constrain}->constrain($self->getwidth, $self->getheight);
+    unless ($scalefactor) {
+      $self->_set_error('constrain method failed on constrain parameter');
+      return undef;
+    }
+    $x_scale = $y_scale = $scalefactor;
+  }
 
   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}, $x_scale, 0);
+    if ( !defined($tmp->{IMG}) ) { 
+      $self->{ERRSTR} = 'unable to scale image';
+      return undef;
+    }
+    $img->{IMG}=i_scaleaxis($tmp->{IMG}, $y_scale, 1);
+    if ( !defined($img->{IMG}) ) { 
+      $self->{ERRSTR}='unable to scale image'; 
+      return undef;
+    }
+
+    return $img;
+  }
+  elsif ($opts{'qtype'} eq 'preview') {
+    $img->{IMG} = i_scale_nn($self->{IMG}, $x_scale, $y_scale); 
+    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 'mixing') {
+    my $new_width = int(0.5 + $self->getwidth * $x_scale);
+    my $new_height = int(0.5 + $self->getheight * $y_scale);
+    $new_width >= 1 or $new_width = 1;
+    $new_height >= 1 or $new_height = 1;
+    $img->{IMG} = i_scale_mixing($self->{IMG}, $new_width, $new_height);
+    unless ($img->{IMG}) {
+      $self->_set_error(Imager->_error_as_meg);
+      return;
+    }
     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;
@@ -1705,24 +2116,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;
@@ -1734,16 +2160,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
@@ -1903,8 +2339,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,
@@ -1913,8 +2355,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;
@@ -1969,9 +2412,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);
@@ -2045,10 +2495,10 @@ sub box {
   my %opts=(color=>$dflcl,xmin=>0,ymin=>0,xmax=>$self->getwidth()-1,ymax=>$self->getheight()-1,@_);
 
   if (exists $opts{'box'}) { 
   my %opts=(color=>$dflcl,xmin=>0,ymin=>0,xmax=>$self->getwidth()-1,ymax=>$self->getheight()-1,@_);
 
   if (exists $opts{'box'}) { 
-    $opts{'xmin'} = min($opts{'box'}->[0],$opts{'box'}->[2]);
-    $opts{'xmax'} = max($opts{'box'}->[0],$opts{'box'}->[2]);
-    $opts{'ymin'} = min($opts{'box'}->[1],$opts{'box'}->[3]);
-    $opts{'ymax'} = max($opts{'box'}->[1],$opts{'box'}->[3]);
+    $opts{'xmin'} = _min($opts{'box'}->[0],$opts{'box'}->[2]);
+    $opts{'xmax'} = _max($opts{'box'}->[0],$opts{'box'}->[2]);
+    $opts{'ymin'} = _min($opts{'box'}->[1],$opts{'box'}->[3]);
+    $opts{'ymax'} = _max($opts{'box'}->[1],$opts{'box'}->[3]);
   }
 
   if ($opts{filled}) { 
   }
 
   if ($opts{filled}) { 
@@ -2089,7 +2539,7 @@ sub arc {
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
   my $dflcl=i_color_new(255,255,255,255);
   my %opts=(color=>$dflcl,
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
   my $dflcl=i_color_new(255,255,255,255);
   my %opts=(color=>$dflcl,
-           'r'=>min($self->getwidth(),$self->getheight())/3,
+           'r'=>_min($self->getwidth(),$self->getheight())/3,
            'x'=>$self->getwidth()/2,
            'y'=>$self->getheight()/2,
            'd1'=>0, 'd2'=>361, @_);
            'x'=>$self->getwidth()/2,
            'y'=>$self->getheight()/2,
            'd1'=>0, 'd2'=>361, @_);
@@ -2307,26 +2757,69 @@ sub flood_fill {
     return undef;
   }
 
     return undef;
   }
 
-  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{border}) {
+    my $border = _color($opts{border});
+    unless ($border) {
+      $self->_set_error($Imager::ERRSTR);
+      return;
+    }
+    if ($opts{fill}) {
+      unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
+       # assume it's a hash ref
+       require Imager::Fill;
+       unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
+         $self->{ERRSTR} = $Imager::ERRSTR;
+         return;
+       }
       }
       }
+      $rc = i_flood_cfill_border($self->{IMG}, $opts{'x'}, $opts{'y'}, 
+                                $opts{fill}{fill}, $border);
+    }
+    else {
+      my $color = _color($opts{'color'});
+      unless ($color) {
+       $self->{ERRSTR} = $Imager::ERRSTR;
+       return;
+      }
+      $rc = i_flood_fill_border($self->{IMG}, $opts{'x'}, $opts{'y'}, 
+                               $color, $border);
+    }
+    if ($rc) { 
+      return $self; 
+    } 
+    else { 
+      $self->{ERRSTR} = $self->_error_as_msg(); 
+      return;
     }
     }
-    $rc = i_flood_cfill($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{fill}{fill});
   }
   else {
   }
   else {
-    my $color = _color($opts{'color'});
-    unless ($color) {
-      $self->{ERRSTR} = $Imager::ERRSTR;
+    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;
+       }
+      }
+      $rc = i_flood_cfill($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{fill}{fill});
+    }
+    else {
+      my $color = _color($opts{'color'});
+      unless ($color) {
+       $self->{ERRSTR} = $Imager::ERRSTR;
+       return;
+      }
+      $rc = i_flood_fill($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
+    }
+    if ($rc) { 
+      return $self; 
+    } 
+    else { 
+      $self->{ERRSTR} = $self->_error_as_msg(); 
       return;
     }
       return;
     }
-    $rc = i_flood_fill($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
-  }
-  if ($rc) { $self; } else { $self->{ERRSTR} = $self->_error_as_msg(); return (); }
+  } 
 }
 
 sub setpixel {
 }
 
 sub setpixel {
@@ -2346,25 +2839,32 @@ sub setpixel {
   if (ref $x && ref $y) {
     unless (@$x == @$y) {
       $self->{ERRSTR} = 'length of x and y mismatch';
   if (ref $x && ref $y) {
     unless (@$x == @$y) {
       $self->{ERRSTR} = 'length of x and y mismatch';
-      return undef;
+      return;
     }
     }
+    my $set = 0;
     if ($color->isa('Imager::Color')) {
       for my $i (0..$#{$opts{'x'}}) {
     if ($color->isa('Imager::Color')) {
       for my $i (0..$#{$opts{'x'}}) {
-        i_ppix($self->{IMG}, $x->[$i], $y->[$i], $color);
+        i_ppix($self->{IMG}, $x->[$i], $y->[$i], $color)
+         or ++$set;
       }
     }
     else {
       for my $i (0..$#{$opts{'x'}}) {
       }
     }
     else {
       for my $i (0..$#{$opts{'x'}}) {
-        i_ppixf($self->{IMG}, $x->[$i], $y->[$i], $color);
+        i_ppixf($self->{IMG}, $x->[$i], $y->[$i], $color)
+         or ++$set;
       }
     }
       }
     }
+    $set or return;
+    return $set;
   }
   else {
     if ($color->isa('Imager::Color')) {
   }
   else {
     if ($color->isa('Imager::Color')) {
-      i_ppix($self->{IMG}, $x, $y, $color);
+      i_ppix($self->{IMG}, $x, $y, $color)
+       and return;
     }
     else {
     }
     else {
-      i_ppixf($self->{IMG}, $x, $y, $color);
+      i_ppixf($self->{IMG}, $x, $y, $color)
+       and return;
     }
   }
 
     }
   }
 
@@ -2417,6 +2917,8 @@ sub getscanline {
   my $self = shift;
   my %opts = ( type => '8bit', x=>0, @_);
 
   my $self = shift;
   my %opts = ( type => '8bit', x=>0, @_);
 
+  $self->_valid_image or return;
+
   defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
 
   unless (defined $opts{'y'}) {
   defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
 
   unless (defined $opts{'y'}) {
@@ -2426,11 +2928,19 @@ sub getscanline {
 
   if ($opts{type} eq '8bit') {
     return i_glin($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
 
   if ($opts{type} eq '8bit') {
     return i_glin($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
-                 $opts{y});
+                 $opts{'y'});
   }
   elsif ($opts{type} eq 'float') {
     return i_glinf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
   }
   elsif ($opts{type} eq 'float') {
     return i_glinf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
-                 $opts{y});
+                 $opts{'y'});
+  }
+  elsif ($opts{type} eq 'index') {
+    unless (i_img_type($self->{IMG})) {
+      $self->_set_error("type => index only valid on paletted images");
+      return;
+    }
+    return i_gpal($self->{IMG}, $opts{x}, $opts{x} + $opts{width},
+                  $opts{'y'});
   }
   else {
     $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
   }
   else {
     $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
@@ -2442,6 +2952,8 @@ sub setscanline {
   my $self = shift;
   my %opts = ( x=>0, @_);
 
   my $self = shift;
   my %opts = ( x=>0, @_);
 
+  $self->_valid_image or return;
+
   unless (defined $opts{'y'}) {
     $self->_set_error("missing y parameter");
     return;
   unless (defined $opts{'y'}) {
     $self->_set_error("missing y parameter");
     return;
@@ -2483,6 +2995,14 @@ sub setscanline {
       return i_plinf($self->{IMG}, $opts{x}, $opts{'y'}, $opts{pixels});
     }
   }
       return i_plinf($self->{IMG}, $opts{x}, $opts{'y'}, $opts{pixels});
     }
   }
+  elsif ($opts{type} eq 'index') {
+    if (ref $opts{pixels}) {
+      return i_ppal($self->{IMG}, $opts{x}, $opts{'y'}, @{$opts{pixels}});
+    }
+    else {
+      return i_ppal_p($self->{IMG}, $opts{x}, $opts{'y'}, $opts{pixels});
+    }
+  }
   else {
     $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
     return;
   else {
     $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
     return;
@@ -2625,9 +3145,9 @@ sub convert {
     $matrix = $opts{matrix};
   }
 
     $matrix = $opts{matrix};
   }
 
-  my $new = Imager->new();
-  $new->{IMG} = i_img_new();
-  unless (i_convert($new->{IMG}, $self->{IMG}, $matrix)) {
+  my $new = Imager->new;
+  $new->{IMG} = i_convert($self->{IMG}, $matrix);
+  unless ($new->{IMG}) {
     # most likely a bad matrix
     $self->{ERRSTR} = _error_as_msg();
     return undef;
     # most likely a bad matrix
     $self->{ERRSTR} = _error_as_msg();
     return undef;
@@ -2749,6 +3269,61 @@ sub getcolorcount {
   return ($rc==-1? undef : $rc);
 }
 
   return ($rc==-1? undef : $rc);
 }
 
+# Returns a reference to a hash. The keys are colour named (packed) and the
+# values are the number of pixels in this colour.
+sub getcolorusagehash {
+  my $self = shift;
+  
+  my %opts = ( maxcolors => 2**30, @_ );
+  my $max_colors = $opts{maxcolors};
+  unless (defined $max_colors && $max_colors > 0) {
+    $self->_set_error('maxcolors must be a positive integer');
+    return;
+  }
+
+  unless (defined $self->{IMG}) {
+    $self->_set_error('empty input image'); 
+    return;
+  }
+
+  my $channels= $self->getchannels;
+  # We don't want to look at the alpha channel, because some gifs using it
+  # doesn't define it for every colour (but only for some)
+  $channels -= 1 if $channels == 2 or $channels == 4;
+  my %color_use;
+  my $height = $self->getheight;
+  for my $y (0 .. $height - 1) {
+    my $colors = $self->getsamples('y' => $y, channels => [ 0 .. $channels - 1 ]);
+    while (length $colors) {
+      $color_use{ substr($colors, 0, $channels, '') }++;
+    }
+    keys %color_use > $max_colors
+      and return;
+  }
+  return \%color_use;
+}
+
+# This will return a ordered array of the colour usage. Kind of the sorted
+# version of the values of the hash returned by getcolorusagehash.
+# You might want to add safety checks and change the names, etc...
+sub getcolorusage {
+  my $self = shift;
+
+  my %opts = ( maxcolors => 2**30, @_ );
+  my $max_colors = $opts{maxcolors};
+  unless (defined $max_colors && $max_colors > 0) {
+    $self->_set_error('maxcolors must be a positive integer');
+    return;
+  }
+
+  unless (defined $self->{IMG}) {
+    $self->_set_error('empty input image'); 
+    return undef;
+  }
+
+  return i_get_anonymous_color_histo($self->{IMG}, $max_colors);
+}
+
 # draw string to an image
 
 sub string {
 # draw string to an image
 
 sub string {
@@ -2756,9 +3331,9 @@ sub string {
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
 
   my %input=('x'=>0, 'y'=>0, @_);
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
 
   my %input=('x'=>0, 'y'=>0, @_);
-  $input{string}||=$input{text};
+  defined($input{string}) or $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;
   }
@@ -2769,7 +3344,6 @@ sub string {
   }
 
   unless ($input{font}->draw(image=>$self, %input)) {
   }
 
   unless ($input{font}->draw(image=>$self, %input)) {
-    $self->{ERRSTR} = $self->_error_as_msg();
     return;
   }
 
     return;
   }
 
@@ -2778,24 +3352,34 @@ sub string {
 
 sub align_string {
   my $self = shift;
 
 sub align_string {
   my $self = shift;
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  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}) {
 
   my %input=('x'=>0, 'y'=>0, @_);
   $input{string}||=$input{text};
 
   unless(exists $input{string}) {
-    $self->{ERRSTR}="missing required parameter 'string'";
+    $self->_set_error("missing required parameter 'string'");
     return;
   }
 
   unless($input{font}) {
     return;
   }
 
   unless($input{font}) {
-    $self->{ERRSTR}="missing required parameter 'font'";
+    $self->_set_error("missing required parameter 'font'");
     return;
   }
 
   my @result;
     return;
   }
 
   my @result;
-  unless (@result = $input{font}->align(image=>$self, %input)) {
-    $self->{ERRSTR} = $self->_error_as_msg();
+  unless (@result = $input{font}->align(image=>$img, %input)) {
     return;
   }
 
     return;
   }
 
@@ -2870,15 +3454,16 @@ sub def_guess_type {
   return 'png'  if ($ext eq "png");
   return 'bmp'  if ($ext eq "bmp" || $ext eq "dib");
   return 'tga'  if ($ext eq "tga");
   return 'png'  if ($ext eq "png");
   return 'bmp'  if ($ext eq "bmp" || $ext eq "dib");
   return 'tga'  if ($ext eq "tga");
-  return 'rgb'  if ($ext eq "rgb");
+  return 'sgi'  if ($ext eq "rgb" || $ext eq "bw" || $ext eq "sgi" || $ext eq "rgba");
   return 'gif'  if ($ext eq "gif");
   return 'raw'  if ($ext eq "raw");
   return 'gif'  if ($ext eq "gif");
   return 'raw'  if ($ext eq "raw");
+  return lc $ext; # best guess
   return ();
 }
 
 # get the minimum of a list
 
   return ();
 }
 
 # get the minimum of a list
 
-sub min {
+sub _min {
   my $mx=shift;
   for(@_) { if ($_<$mx) { $mx=$_; }}
   return $mx;
   my $mx=shift;
   for(@_) { if ($_<$mx) { $mx=$_; }}
   return $mx;
@@ -2886,7 +3471,7 @@ sub min {
 
 # get the maximum of a list
 
 
 # get the maximum of a list
 
-sub max {
+sub _max {
   my $mx=shift;
   for(@_) { if ($_>$mx) { $mx=$_; }}
   return $mx;
   my $mx=shift;
   for(@_) { if ($_>$mx) { $mx=$_; }}
   return $mx;
@@ -2894,7 +3479,7 @@ sub max {
 
 # string stuff for iptc headers
 
 
 # string stuff for iptc headers
 
-sub clean {
+sub _clean {
   my($str)=$_[0];
   $str = substr($str,3);
   $str =~ s/[\n\r]//g;
   my($str)=$_[0];
   $str = substr($str,3);
   $str =~ s/[\n\r]//g;
@@ -2913,7 +3498,8 @@ sub parseiptc {
 
   my $str=$self->{IPTCRAW};
 
 
   my $str=$self->{IPTCRAW};
 
-  #print $str;
+  defined $str
+    or return;
 
   @ar=split(/8BIM/,$str);
 
 
   @ar=split(/8BIM/,$str);
 
@@ -2923,19 +3509,19 @@ sub parseiptc {
       @sar=split(/\034\002/);
       foreach $item (@sar) {
        if ($item =~ m/^x/) {
       @sar=split(/\034\002/);
       foreach $item (@sar) {
        if ($item =~ m/^x/) {
-         $caption=&clean($item);
+         $caption = _clean($item);
          $i++;
        }
        if ($item =~ m/^P/) {
          $i++;
        }
        if ($item =~ m/^P/) {
-         $photogr=&clean($item);
+         $photogr = _clean($item);
          $i++;
        }
        if ($item =~ m/^i/) {
          $i++;
        }
        if ($item =~ m/^i/) {
-         $headln=&clean($item);
+         $headln = _clean($item);
          $i++;
        }
        if ($item =~ m/^n/) {
          $i++;
        }
        if ($item =~ m/^n/) {
-         $credit=&clean($item);
+         $credit = _clean($item);
          $i++;
        }
       }
          $i++;
        }
       }
@@ -2944,7 +3530,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__
@@ -3008,7 +3602,7 @@ render text and more.
 
 =item *
 
 
 =item *
 
-Imager - This document - Synopsis Example, Table of Contents and
+Imager - This document - Synopsis, Example, Table of Contents and
 Overview.
 
 =item *
 Overview.
 
 =item *
@@ -3077,6 +3671,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
@@ -3096,14 +3706,32 @@ or if you want to create an empty image:
 This example creates a completely black image of width 400 and height
 300 and 4 channels.
 
 This example creates a completely black image of width 400 and height
 300 and 4 channels.
 
-When an operation fails which can be directly associated with an image
-the error message is stored can be retrieved with
-C<$img-E<gt>errstr()>.
+=head1 ERROR HANDLING
 
 
-In cases where no image object is associated with an operation
-C<$Imager::ERRSTR> is used to report errors not directly associated
-with an image object.  You can also call C<Imager->errstr> to get this
-value.
+In general a method will return false when it fails, if it does use the errstr() method to find out why:
+
+=over
+
+=item errstr
+
+Returns the last error message in that context.
+
+If the last error you received was from calling an object method, such
+as read, call errstr() as an object method to find out why:
+
+  my $image = Imager->new;
+  $image->read(file => 'somefile.gif')
+     or die $image->errstr;
+
+If it was a class method then call errstr() as a class method:
+
+  my @imgs = Imager->read_multi(file => 'somefile.gif')
+    or die Imager->errstr;
+
+Note that in some cases object methods are implemented in terms of
+class methods so a failing object method may set both.
+
+=back
 
 The C<Imager-E<gt>new> method is described in detail in
 L<Imager::ImageTypes>.
 
 The C<Imager-E<gt>new> method is described in detail in
 L<Imager::ImageTypes>.
@@ -3116,10 +3744,10 @@ addcolors() -  L<Imager::ImageTypes/addcolors>
 
 addtag() -  L<Imager::ImageTypes/addtag> - add image tags
 
 
 addtag() -  L<Imager::ImageTypes/addtag> - add image tags
 
-arc() - L<Imager::Draw/arc>
-
 align_string() - L<Imager::Draw/align_string>
 
 align_string() - L<Imager::Draw/align_string>
 
+arc() - L<Imager::Draw/arc>
+
 bits() - L<Imager::ImageTypes/bits> - number of bits per sample for the
 image
 
 bits() - L<Imager::ImageTypes/bits> - number of bits per sample for the
 image
 
@@ -3136,11 +3764,13 @@ 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
 
+def_guess_type() - L<Imager::Files/def_guess_type>
+
 deltag() -  L<Imager::ImageTypes/deltag> - delete image tags
 
 difference() - L<Imager::Filters/"Image Difference">
 
 deltag() -  L<Imager::ImageTypes/deltag> - delete image tags
 
 difference() - L<Imager::Filters/"Image Difference">
 
-errstr() - L<Imager/"Basic Overview">
+errstr() - L<"Basic Overview">
 
 filter() - L<Imager::Filters>
 
 
 filter() - L<Imager::Filters>
 
@@ -3158,10 +3788,16 @@ getcolorcount() -  L<Imager::ImageTypes/getcolorcount>
 getcolors() - L<Imager::ImageTypes/getcolors> - get colors from the image
 palette, if it has one
 
 getcolors() - L<Imager::ImageTypes/getcolors> - get colors from the image
 palette, if it has one
 
+getcolorusage() - L<Imager::ImageTypes/getcolorusage>
+
+getcolorusagehash() - L<Imager::ImageTypes/getcolorusagehash>
+
 get_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
 
 getheight() - L<Imager::ImageTypes/getwidth>
 
 get_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
 
 getheight() - L<Imager::ImageTypes/getwidth>
 
+getmask() - L<Imager::ImageTypes/getmask>
+
 getpixel() - L<Imager::Draw/getpixel>
 
 getsamples() - L<Imager::Draw/getsamples>
 getpixel() - L<Imager::Draw/getpixel>
 
 getsamples() - L<Imager::Draw/getsamples>
@@ -3172,21 +3808,38 @@ getwidth() - L<Imager::ImageTypes/getwidth>
 
 img_set() - L<Imager::ImageTypes/img_set>
 
 
 img_set() - L<Imager::ImageTypes/img_set>
 
+init() - L<Imager::ImageTypes/init>
+
 line() - L<Imager::Draw/line>
 
 line() - L<Imager::Draw/line>
 
+load_plugin() - L<Imager::Filters/load_plugin>
+
 map() - L<Imager::Transformations/"Color Mappings"> - remap color
 channel values
 
 masked() -  L<Imager::ImageTypes/masked> - make a masked image
 
 map() - L<Imager::Transformations/"Color Mappings"> - remap color
 channel values
 
 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/maxcolors>
 
 
 maxcolors() - L<Imager::ImageTypes/maxcolors>
 
+NC() - L<Imager::Handy/NC>
+
 new() - L<Imager::ImageTypes/new>
 
 new() - L<Imager::ImageTypes/new>
 
+newcolor() - L<Imager::Handy/newcolor>
+
+newcolour() - L<Imager::Handy/newcolour>
+
+newfont() - L<Imager::Handy/newfont>
+
+NF() - L<Imager::Handy/NF>
+
 open() - L<Imager::Files> - an alias for read()
 
 open() - L<Imager::Files> - an alias for read()
 
+parseiptc() - L<Imager::Files/parseiptc> - parse IPTC data from a JPEG
+image
+
 paste() - L<Imager::Transformations/paste> - draw an image onto an image
 
 polygon() - L<Imager::Draw/polygon>
 paste() - L<Imager::Transformations/paste> - draw an image onto an image
 
 polygon() - L<Imager::Draw/polygon>
@@ -3198,6 +3851,12 @@ read() - L<Imager::Files> - read a single image from an image file
 read_multi() - L<Imager::Files> - read multiple images from an image
 file
 
 read_multi() - L<Imager::Files> - read multiple images from an image
 file
 
+register_filter() - L<Imager::Filters/register_filter>
+
+register_reader() - L<Imager::Filters/register_reader>
+
+register_writer() - L<Imager::Filters/register_writer>
+
 rotate() - L<Imager::Transformations/rotate>
 
 rubthrough() - L<Imager::Transformations/rubthrough> - draw an image onto an
 rotate() - L<Imager::Transformations/rotate>
 
 rubthrough() - L<Imager::Transformations/rubthrough> - draw an image onto an
@@ -3205,8 +3864,6 @@ 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>
@@ -3214,9 +3871,15 @@ scaleY() - L<Imager::Transformations/scaleY>
 setcolors() - L<Imager::ImageTypes/setcolors> - set palette colors in
 a paletted image
 
 setcolors() - L<Imager::ImageTypes/setcolors> - set palette colors in
 a paletted image
 
+set_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
+
+setmask() - L<Imager::ImageTypes/setmask>
+
 setpixel() - L<Imager::Draw/setpixel>
 
 setpixel() - L<Imager::Draw/setpixel>
 
-set_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
+setscanline() - L<Imager::Draw/setscanline>
+
+settag() - L<Imager::ImageTypes/settag>
 
 string() - L<Imager::Draw/string> - draw text on an image
 
 
 string() - L<Imager::Draw/string> - draw text on an image
 
@@ -3224,6 +3887,8 @@ tags() -  L<Imager::ImageTypes/tags> - fetch image tags
 
 to_paletted() -  L<Imager::ImageTypes/to_paletted>
 
 
 to_paletted() -  L<Imager::ImageTypes/to_paletted>
 
+to_rgb16() - L<Imager::ImageTypes/to_rgb16>
+
 to_rgb8() - L<Imager::ImageTypes/to_rgb8>
 
 transform() - L<Imager::Engines/"transform">
 to_rgb8() - L<Imager::ImageTypes/to_rgb8>
 
 transform() - L<Imager::Engines/"transform">
@@ -3232,6 +3897,8 @@ transform2() - L<Imager::Engines/"transform2">
 
 type() -  L<Imager::ImageTypes/type> - type of image (direct vs paletted)
 
 
 type() -  L<Imager::ImageTypes/type> - type of image (direct vs paletted)
 
+unload_plugin() - L<Imager::Filters/unload_plugin>
+
 virtual() - L<Imager::ImageTypes/virtual> - whether the image has it's own
 data
 
 virtual() - L<Imager::ImageTypes/virtual> - whether the image has it's own
 data
 
@@ -3247,6 +3914,9 @@ 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>
@@ -3267,17 +3937,20 @@ convolution - L<Imager::Filter/conv>
 
 cropping - L<Imager::Transformations/crop>
 
 
 cropping - L<Imager::Transformations/crop>
 
+CUR files - L<Imager::Files/"ICO (Microsoft Windows Icon) and CUR (Microsoft Windows Cursor)">
+
 C<diff> images - L<Imager::Filter/"Image Difference">
 
 C<diff> images - L<Imager::Filter/"Image Difference">
 
-dpi - L<Imager::ImageTypes/i_xres>
+dpi - L<Imager::ImageTypes/i_xres>, 
+L<Imager::Cookbook/"Image spatial resolution">
 
 drawing boxes - L<Imager::Draw/box>
 
 drawing lines - L<Imager::Draw/line>
 
 
 drawing boxes - L<Imager::Draw/box>
 
 drawing lines - L<Imager::Draw/line>
 
-drawing text - L<Imager::Font/string>, L<Imager::Font/align>
+drawing text - L<Imager::Draw/string>, L<Imager::Draw/align_string>
 
 
-error message - L<Imager/"Basic Overview">
+error message - L<"Basic Overview">
 
 files, font - L<Imager::Font>
 
 
 files, font - L<Imager::Font>
 
@@ -3293,8 +3966,8 @@ flood fill - L<Imager::Draw/flood_fill>
 
 fonts - L<Imager::Font>
 
 
 fonts - L<Imager::Font>
 
-fonts, drawing with - L<Imager::Font/string>, L<Imager::Font/align>,
-L<Imager::Font::Wrap>
+fonts, drawing with - L<Imager::Draw/string>,
+L<Imager::Draw/align_string>, L<Imager::Font::Wrap>
 
 fonts, metrics - L<Imager::Font/bounding_box>, L<Imager::Font::BBox>
 
 
 fonts, metrics - L<Imager::Font/bounding_box>, L<Imager::Font::BBox>
 
@@ -3312,10 +3985,14 @@ gradient fill - L<Imager::Fill/"Fountain fills">,
 L<Imager::Filters/fountain>, L<Imager::Fountain>,
 L<Imager::Filters/gradgen>
 
 L<Imager::Filters/fountain>, L<Imager::Fountain>,
 L<Imager::Filters/gradgen>
 
+grayscale, convert image to - L<Imager::Transformations/convert>
+
 guassian blur - L<Imager::Filter/guassian>
 
 hatch fills - L<Imager::Fill/"Hatched fills">
 
 guassian blur - L<Imager::Filter/guassian>
 
 hatch fills - L<Imager::Fill/"Hatched fills">
 
+ICO files - L<Imager::Files/"ICO (Microsoft Windows Icon) and CUR (Microsoft Windows Cursor)">
+
 invert image - L<Imager::Filter/hardinvert>
 
 JPEG - L<Imager::Files/"JPEG">
 invert image - L<Imager::Filter/hardinvert>
 
 JPEG - L<Imager::Files/"JPEG">
@@ -3337,6 +4014,9 @@ 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>
 
 pseudo-color image - L<Imager::ImageTypes/to_paletted>,
 L<Imager::ImageTypes/new>
 
@@ -3351,10 +4031,14 @@ rectangles, drawing - L<Imager::Draw/box>
 resizing an image - L<Imager::Transformations/scale>, 
 L<Imager::Transformations/crop>
 
 resizing an image - L<Imager::Transformations/scale>, 
 L<Imager::Transformations/crop>
 
+RGB (SGI) files - L<Imager::Files/"SGI (RGB, BW)">
+
 saving an image - L<Imager::Files>
 
 scaling - L<Imager::Transformations/scale>
 
 saving an image - L<Imager::Files>
 
 scaling - L<Imager::Transformations/scale>
 
+SGI files - L<Imager::Files/"SGI (RGB, BW)">
+
 sharpen - L<Imager::Filters/unsharpmask>, L<Imager::Filters/conv>
 
 size, image - L<Imager::ImageTypes/getwidth>,
 sharpen - L<Imager::Filters/unsharpmask>, L<Imager::Filters/conv>
 
 size, image - L<Imager::ImageTypes/getwidth>,
@@ -3381,8 +4065,7 @@ writing an image to a file - L<Imager::Files>
 
 =head1 SUPPORT
 
 
 =head1 SUPPORT
 
-You can ask for help, report bugs or express your undying love for
-Imager on the Imager-devel mailing list.
+The best place to get help with Imager is the mailing list.
 
 To subscribe send a message with C<subscribe> in the body to:
 
 
 To subscribe send a message with C<subscribe> in the body to:
 
@@ -3390,44 +4073,90 @@ 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/>
 
 
-where you can also find the mailing list archive.
+=back
 
 
-If you're into IRC, you can typically find the developers in #Imager
-on irc.perl.org.  As with any IRC channel, the participants could be
-occupied or asleep, so please be patient.
+where you can also find the mailing list archive.
 
 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
+
+or by sending an email to:
+
+=over
+
+bug-Imager@rt.cpan.org
+
+=back
 
 Please remember to include the versions of Imager, perl, supporting
 libraries, and any relevant code.  If you have specific images that
 cause the problems, please include those too.
 
 
 Please remember to include the versions of Imager, perl, supporting
 libraries, and any relevant code.  If you have specific images that
 cause the problems, please include those too.
 
-=head1 BUGS
+If you don't want to publish your email address on a mailing list you
+can use CPAN::Forum:
 
 
-Bugs are listed individually for relevant pod pages.
+  http://www.cpanforum.com/dist/Imager
+
+You will need to register to post.
+
+=head1 CONTRIBUTING TO IMAGER
+
+=head2 Feedback
+
+I like feedback.
+
+If you like or dislike Imager, you can add a public review of Imager
+at CPAN Ratings:
+
+  http://cpanratings.perl.org/dist/Imager
+
+This requires a Bitcard Account (http://www.bitcard.org).
+
+You can also send email to the maintainer below.
+
+If you send me a bug report via email, it will be copied to RT.
+
+=head2 Patches
+
+I accept patches, preferably against the main branch in subversion.
+You should include an explanation of the reason for why the patch is
+needed or useful.
+
+Your patch should include regression tests where possible, otherwise
+it will be delayed until I get a chance to write them.
 
 =head1 AUTHOR
 
 
 =head1 AUTHOR
 
-Arnar M. Hrafnkelsson and Tony Cook (tony@imager.perl.org) among
-others. See the README for a complete list.
+Tony Cook <tony@imager.perl.org> is the current maintainer for Imager.
+
+Arnar M. Hrafnkelsson is the original author of Imager.
+
+Many others have contributed to Imager, please 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/>
 
 
-Affix::Infix2Postfix(3), Parse::RecDescent(3)
-http://imager.perl.org/
+L<Affix::Infix2Postfix>(3), L<Parse::RecDescent>(3)
 
 Other perl imaging modules include:
 
 
 Other perl imaging modules include:
 
-GD(3), Image::Magick(3), Graphics::Magick(3).
+L<GD>(3), L<Image::Magick>(3), L<Graphics::Magick>(3).
 
 =cut
 
 =cut