]> git.imager.perl.org - imager.git/blobdiff - Imager.pm
use the font's first character map if we don't find a unicode map (FT1)
[imager.git] / Imager.pm
index 3ba16cf6cd812e73f985bc31150c48daa92d3376..9c585710f88cf693d511d5e2774d9d108963254a 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -44,6 +44,7 @@ use Imager::Font;
 
                i_bezier_multi
                i_poly_aa
+               i_poly_aa_cfill
 
                i_copyto
                i_rubthru
@@ -84,6 +85,7 @@ use Imager::Font;
                i_writepng_wiol
 
                i_readgif
+               i_readgif_wiol
                i_readgif_callback
                i_writegif
                i_writegifmc
@@ -117,7 +119,7 @@ use Imager::Font;
                NF
 );
 
-@EXPORT=qw( 
+@EXPORT=qw(
           init_log
           i_list_formats
           i_has_format
@@ -145,7 +147,7 @@ BEGIN {
   require Exporter;
   require DynaLoader;
 
-  $VERSION = '0.39pre1';
+  $VERSION = '0.39';
   @ISA = qw(Exporter DynaLoader);
   bootstrap Imager $VERSION;
 }
@@ -321,7 +323,7 @@ BEGIN {
                               multiply  => 2, mult => 2,
                               dissolve  => 3,
                               add       => 4,
-                              subtract  => 5, sub => 5,
+                              subtract  => 5, 'sub' => 5,
                               diff      => 6,
                               lighten   => 7,
                               darken    => 8,
@@ -444,6 +446,49 @@ sub _error_as_msg {
   return join(": ", map $_->[0], i_errors());
 }
 
+# this function tries to DWIM for color parameters
+#  color objects are used as is
+#  simple scalars are simply treated as single parameters to Imager::Color->new
+#  hashrefs are treated as named argument lists to Imager::Color->new
+#  arrayrefs are treated as list arguments to Imager::Color->new iff any
+#    parameter is > 1
+#  other arrayrefs are treated as list arguments to Imager::Color::Float
+
+sub _color {
+  my $arg = shift;
+  my $result;
+
+  if (ref $arg) {
+    if (UNIVERSAL::isa($arg, "Imager::Color")
+        || UNIVERSAL::isa($arg, "Imager::Color::Float")) {
+      $result = $arg;
+    }
+    else {
+      if ($arg =~ /^HASH\(/) {
+        $result = Imager::Color->new(%$arg);
+      }
+      elsif ($arg =~ /^ARRAY\(/) {
+        if (grep $_ > 1, @$arg) {
+          $result = Imager::Color->new(@$arg);
+        }
+        else {
+          $result = Imager::Color::Float->new(@$arg);
+        }
+      }
+      else {
+        $Imager::ERRSTR = "Not a color";
+      }
+    }
+  }
+  else {
+    # assume Imager::Color::new knows how to handle it
+    $result = Imager::Color->new($arg);
+  }
+
+  return $result;
+}
+
+
 #
 # Methods to be called on objects.
 #
@@ -562,6 +607,9 @@ sub img_set {
     $self->{IMG} = i_img_pal_new($hsh{xsize}, $hsh{ysize}, $hsh{channels},
                                  $hsh{maxcolors} || 256);
   }
+  elsif ($hsh{bits} eq 'double') {
+    $self->{IMG} = i_img_double_new($hsh{xsize}, $hsh{ysize}, $hsh{channels});
+  }
   elsif ($hsh{bits} == 16) {
     $self->{IMG} = i_img_16_new($hsh{xsize}, $hsh{ysize}, $hsh{channels});
   }
@@ -683,7 +731,11 @@ sub findcolor {
 
 sub bits {
   my $self = shift;
-  $self->{IMG} and i_img_bits($self->{IMG});
+  my $bits = $self->{IMG} && i_img_bits($self->{IMG});
+  if ($bits && $bits == length(pack("d", 1)) * 8) {
+    $bits = 'double';
+  }
+  $bits;
 }
 
 sub type {
@@ -787,8 +839,8 @@ sub deltag {
 
   return 0 unless $self->{IMG};
 
-  if (defined $opts{index}) {
-    return i_tags_delete($self->{IMG}, $opts{index});
+  if (defined $opts{'index'}) {
+    return i_tags_delete($self->{IMG}, $opts{'index'});
   }
   elsif (defined $opts{name}) {
     return i_tags_delbyname($self->{IMG}, $opts{name});
@@ -802,12 +854,108 @@ sub deltag {
   }
 }
 
+my @needseekcb = qw/tiff/;
+my %needseekcb = map { $_, $_ } @needseekcb;
+
+
+sub _get_reader_io {
+  my ($self, $input, $type) = @_;
+
+  if ($input->{fd}) {
+    return io_new_fd($input->{fd});
+  }
+  elsif ($input->{fh}) {
+    my $fd = fileno($input->{fh});
+    unless ($fd) {
+      $self->_set_error("Handle in fh option not opened");
+      return;
+    }
+    return io_new_fd($fd);
+  }
+  elsif ($input->{file}) {
+    my $file = IO::File->new($input->{file}, "r");
+    unless ($file) {
+      $self->_set_error("Could not open $input->{file}: $!");
+      return;
+    }
+    binmode $file;
+    return (io_new_fd(fileno($file)), $file);
+  }
+  elsif ($input->{data}) {
+    return io_new_buffer($input->{data});
+  }
+  elsif ($input->{callback} || $input->{readcb}) {
+    if ($needseekcb{$type} && !$input->{seekcb}) {
+      $self->_set_error("Format $type needs a seekcb parameter");
+    }
+    if ($input->{maxbuffer}) {
+      return io_new_cb($input->{writecb},
+                       $input->{callback} || $input->{readcb},
+                       $input->{seekcb}, $input->{closecb},
+                       $input->{maxbuffer});
+    }
+    else {
+      return io_new_cb($input->{writecb},
+                       $input->{callback} || $input->{readcb},
+                       $input->{seekcb}, $input->{closecb});
+    }
+  }
+  else {
+    $self->_set_error("file/fd/fh/data/callback parameter missing");
+    return;
+  }
+}
+
+sub _get_writer_io {
+  my ($self, $input, $type) = @_;
+
+  if ($input->{fd}) {
+    return io_new_fd($input->{fd});
+  }
+  elsif ($input->{fh}) {
+    my $fd = fileno($input->{fh});
+    unless ($fd) {
+      $self->_set_error("Handle in fh option not opened");
+      return;
+    }
+    return io_new_fd($fd);
+  }
+  elsif ($input->{file}) {
+    my $fh = new IO::File($input->{file},"w+");
+    unless ($fh) { 
+      $self->_set_error("Could not open file $input->{file}: $!");
+      return;
+    }
+    binmode($fh) or die;
+    return (io_new_fd(fileno($fh)), $fh);
+  }
+  elsif ($input->{data}) {
+    return io_new_bufchain();
+  }
+  elsif ($input->{callback} || $input->{writecb}) {
+    if ($input->{maxbuffer}) {
+      return io_new_cb($input->{callback} || $input->{writecb},
+                       $input->{readcb},
+                       $input->{seekcb}, $input->{closecb},
+                       $input->{maxbuffer});
+    }
+    else {
+      return io_new_cb($input->{callback} || $input->{writecb},
+                       $input->{readcb},
+                       $input->{seekcb}, $input->{closecb});
+    }
+  }
+  else {
+    $self->_set_error("file/fd/fh/data/callback parameter missing");
+    return;
+  }
+}
+
 # Read an image from file
 
 sub read {
   my $self = shift;
   my %input=@_;
-  my ($fh, $fd, $IO);
 
   if (defined($self->{IMG})) {
     # let IIM_DESTROY do the destruction, since the image may be
@@ -816,41 +964,31 @@ sub read {
     undef($self->{IMG});
   }
 
-  if (!$input{fd} and !$input{file} and !$input{data}) {
-    $self->{ERRSTR}='no file, fd or data parameter'; return undef;
-  }
-  if ($input{file}) {
-    $fh = new IO::File($input{file},"r");
-    if (!defined $fh) {
-      $self->{ERRSTR}='Could not open file'; return undef;
-    }
-    binmode($fh);
-    $fd = $fh->fileno();
-  }
-  if ($input{fd}) {
-    $fd=$input{fd};
-  }
-
   # FIXME: Find the format here if not specified
   # yes the code isn't here yet - next week maybe?
   # Next week?  Are you high or something?  That comment
   # has been there for half a year dude.
   # Look, i just work here, ok?
 
-  if (!$input{type} and $input{file}) {
-    $input{type}=$FORMATGUESS->($input{file});
+  if (!$input{'type'} and $input{file}) {
+    $input{'type'}=$FORMATGUESS->($input{file});
+  }
+  unless ($input{'type'}) {
+    $self->_set_error('type parameter missing and not possible to guess from extension'); 
+    return undef;
   }
-  if (!$formats{$input{type}}) {
+  if (!$formats{$input{'type'}}) {
     $self->{ERRSTR}='format not supported'; return undef;
   }
 
-  my %iolready=(jpeg=>1, png=>1, tiff=>1, pnm=>1, raw=>1, bmp=>1, tga=>1);
+  my %iolready=(jpeg=>1, png=>1, tiff=>1, pnm=>1, raw=>1, bmp=>1, tga=>1, rgb=>1, gif=>1);
 
-  if ($iolready{$input{type}}) {
+  if ($iolready{$input{'type'}}) {
     # Setup data source
-    $IO = io_new_fd($fd);      # sort of simple for now eh?
+    my ($IO, $fh) = $self->_get_reader_io(\%input, $input{'type'})
+      or return;
 
-    if ( $input{type} eq 'jpeg' ) {
+    if ( $input{'type'} eq 'jpeg' ) {
       ($self->{IMG},$self->{IPTCRAW})=i_readjpeg_wiol( $IO );
       if ( !defined($self->{IMG}) ) {
        $self->{ERRSTR}='unable to read jpeg image'; return undef;
@@ -859,16 +997,16 @@ sub read {
       return $self;
     }
 
-    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
       if ( !defined($self->{IMG}) ) {
-       $self->{ERRSTR}='unable to read tiff image'; return undef;
+       $self->{ERRSTR}=$self->_error_as_msg(); return undef;
       }
       $self->{DEBUG} && print "loading a tiff file\n";
       return $self;
     }
 
-    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
       if ( !defined($self->{IMG}) ) {
        $self->{ERRSTR}='unable to read pnm image: '._error_as_msg(); return undef;
@@ -877,7 +1015,7 @@ sub read {
       return $self;
     }
 
-    if ( $input{type} eq 'png' ) {
+    if ( $input{'type'} eq 'png' ) {
       $self->{IMG}=i_readpng_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
       if ( !defined($self->{IMG}) ) {
        $self->{ERRSTR}='unable to read png image';
@@ -886,26 +1024,58 @@ sub read {
       $self->{DEBUG} && print "loading a png file\n";
     }
 
-    if ( $input{type} eq 'bmp' ) {
+    if ( $input{'type'} eq 'bmp' ) {
       $self->{IMG}=i_readbmp_wiol( $IO );
       if ( !defined($self->{IMG}) ) {
-       $self->{ERRSTR}='unable to read bmp image';
+       $self->{ERRSTR}=$self->_error_as_msg();
        return undef;
       }
       $self->{DEBUG} && print "loading a bmp file\n";
     }
 
-    if ( $input{type} eq 'tga' ) {
+    if ( $input{'type'} eq 'gif' ) {
+      if ($input{colors} && !ref($input{colors})) {
+       # must be a reference to a scalar that accepts the colour map
+       $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 ];
+        }
+      }
+      else {
+        $self->{IMG} =i_readgif_wiol( $IO );
+      }
+      if ( !defined($self->{IMG}) ) {
+       $self->{ERRSTR}=$self->_error_as_msg();
+       return undef;
+      }
+      $self->{DEBUG} && print "loading a gif file\n";
+    }
+
+    if ( $input{'type'} eq 'tga' ) {
       $self->{IMG}=i_readtga_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
       if ( !defined($self->{IMG}) ) {
        $self->{ERRSTR}=$self->_error_as_msg();
-#      $self->{ERRSTR}='unable to read tga image';
        return undef;
       }
       $self->{DEBUG} && print "loading a tga file\n";
     }
 
-    if ( $input{type} eq 'raw' ) {
+    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 ( !($params{xsize} && $params{ysize}) ) {
@@ -930,19 +1100,20 @@ sub read {
 
     # Old code for reference while changing the new stuff
 
-    if (!$input{type} and $input{file}) {
-      $input{type}=$FORMATGUESS->($input{file});
+    if (!$input{'type'} and $input{file}) {
+      $input{'type'}=$FORMATGUESS->($input{file});
     }
 
-    if (!$input{type}) {
+    if (!$input{'type'}) {
       $self->{ERRSTR}='type parameter missing and not possible to guess from extension'; return undef;
     }
 
-    if (!$formats{$input{type}}) {
+    if (!$formats{$input{'type'}}) {
       $self->{ERRSTR}='format not supported';
       return undef;
     }
 
+    my ($fh, $fd);
     if ($input{file}) {
       $fh = new IO::File($input{file},"r");
       if (!defined $fh) {
@@ -957,7 +1128,7 @@ sub read {
       $fd=$input{fd};
     }
 
-    if ( $input{type} eq 'gif' ) {
+    if ( $input{'type'} eq 'gif' ) {
       my $colors;
       if ($input{colors} && !ref($input{colors})) {
        # must be a reference to a scalar that accepts the colour map
@@ -994,37 +1165,38 @@ sub read {
 # Write an image to file
 sub write {
   my $self = shift;
-  my %input=(jpegquality=>75, gifquant=>'mc', lmdither=>6.0, lmfixed=>[], 
+  my %input=(jpegquality=>75, 
+            gifquant=>'mc', 
+            lmdither=>6.0, 
+            lmfixed=>[],
+            idstring=>"",
+            compress=>1,
+            wierdpack=>0,
             fax_fine=>1, @_);
-  my ($fh, $rc, $fd, $IO);
+  my $rc;
 
-  my %iolready=( tiff=>1, raw=>1, png=>1, pnm=>1, bmp=>1, jpeg=>1, tga=>1 ); # this will be SO MUCH BETTER once they are all in there
+  my %iolready=( tiff=>1, raw=>1, png=>1, pnm=>1, bmp=>1, jpeg=>1, tga=>1, 
+                 gif=>1 ); # this will be SO MUCH BETTER once they are all in there
 
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
 
-  if (!$input{file} and !$input{'fd'} and !$input{'data'}) { $self->{ERRSTR}='file/fd/data parameter missing'; return undef; }
-  if (!$input{type} and $input{file}) { $input{type}=$FORMATGUESS->($input{file}); }
-  if (!$input{type}) { $self->{ERRSTR}='type parameter missing and not possible to guess from extension'; return undef; }
+  if (!$input{'type'} and $input{file}) { 
+    $input{'type'}=$FORMATGUESS->($input{file});
+  }
+  if (!$input{'type'}) { 
+    $self->{ERRSTR}='type parameter missing and not possible to guess from extension';
+    return undef;
+  }
 
-  if (!$formats{$input{type}}) { $self->{ERRSTR}='format not supported'; return undef; }
+  if (!$formats{$input{'type'}}) { $self->{ERRSTR}='format not supported'; return undef; }
 
-  if (exists $input{'fd'}) {
-    $fd=$input{'fd'};
-  } elsif (exists $input{'data'}) {
-    $IO = Imager::io_new_bufchain();
-  } else {
-    $fh = new IO::File($input{file},"w+");
-    if (!defined $fh) { $self->{ERRSTR}='Could not open file'; return undef; }
-    binmode($fh) or die;
-    $fd = $fh->fileno();
-  }
+  my ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
+    or return undef;
 
-  if ($iolready{$input{type}}) {
-    if (defined $fd) {
-      $IO = io_new_fd($fd);
-    }
+  # this conditional is probably obsolete
+  if ($iolready{$input{'type'}}) {
 
-    if ($input{type} eq 'tiff') {
+    if ($input{'type'} eq 'tiff') {
       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';
@@ -1036,43 +1208,56 @@ sub write {
          return undef;
        }
       }
-    } elsif ( $input{type} eq 'pnm' ) {
+    } elsif ( $input{'type'} eq 'pnm' ) {
       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' ) {
+    } elsif ( $input{'type'} eq 'raw' ) {
       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' ) {
+    } elsif ( $input{'type'} eq 'png' ) {
       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' ) {
+    } elsif ( $input{'type'} eq 'jpeg' ) {
       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' ) {
+    } elsif ( $input{'type'} eq 'bmp' ) {
       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' ) {
-      if ( !i_writetga_wiol($self->{IMG}, $IO) ) {
+    } elsif ( $input{'type'} eq 'tga' ) {
+
+      if ( !i_writetga_wiol($self->{IMG}, $IO, $input{wierdpack}, $input{compress}, $input{idstring}) ) {
        $self->{ERRSTR}=$self->_error_as_msg();
-#      $self->{ERRSTR}='unable to write tga image';
        return undef;
       }
       $self->{DEBUG} && print "writing a tga file\n";
+    } elsif ( $input{'type'} eq 'gif' ) {
+      # 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';
+      }
+      $rc = i_writegif_wiol($IO, \%input, $self->{IMG});
     }
 
     if (exists $input{'data'}) {
@@ -1084,101 +1269,54 @@ sub write {
       ${$input{data}} = $data;
     }
     return $self;
-  } else {
-    if ( $input{type} eq 'gif' ) {
-      if (not $input{gifplanes}) {
-       my $gp;
-       my $count=i_count_colors($self->{IMG}, 256);
-       $gp=8 if $count == -1;
-       $gp=1 if not $gp and $count <= 2;
-       $gp=2 if not $gp and $count <= 4;
-       $gp=3 if not $gp and $count <= 8;
-       $gp=4 if not $gp and $count <= 16;
-       $gp=5 if not $gp and $count <= 32;
-       $gp=6 if not $gp and $count <= 64;
-       $gp=7 if not $gp and $count <= 128;
-       $input{gifplanes} = $gp || 8;
-      }
-
-      if ($input{gifplanes}>8) {
-       $input{gifplanes}=8;
-      }
-      if ($input{gifquant} eq 'gen' || $input{callback}) {
-
-
-       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 ($input{callback}) {
-         defined $input{maxbuffer} or $input{maxbuffer} = -1;
-         $rc = i_writegif_callback($input{callback}, $input{maxbuffer},
-                                   \%input, $self->{IMG});
-       } else {
-         $rc = i_writegif_gen($fd, \%input, $self->{IMG});
-       }
-
-      } elsif ($input{gifquant} eq 'lm') {
-       $rc=i_writegif($self->{IMG},$fd,$input{gifplanes},$input{lmdither},$input{lmfixed});
-      } else {
-       $rc=i_writegifmc($self->{IMG},$fd,$input{gifplanes});
-      }
-      if ( !defined($rc) ) {
-       $self->{ERRSTR} = "Writing GIF file: "._error_as_msg(); return undef;
-      }
-      $self->{DEBUG} && print "writing a gif file\n";
-
-    }
   }
+
   return $self;
 }
 
 sub write_multi {
   my ($class, $opts, @images) = @_;
 
-  if ($opts->{type} eq 'gif') {
+  if (!$opts->{'type'} && $opts->{'file'}) {
+    $opts->{'type'} = $FORMATGUESS->($opts->{'file'});
+  }
+  unless ($opts->{'type'}) {
+    $class->_set_error('type parameter missing and not possible to guess from extension');
+    return;
+  }
+  # translate to ImgRaw
+  if (grep !UNIVERSAL::isa($_, 'Imager') || !$_->{IMG}, @images) {
+    $class->_set_error('Usage: Imager->write_multi({ options }, @images)');
+    return 0;
+  }
+  my @work = map $_->{IMG}, @images;
+  my ($IO, $file) = $class->_get_writer_io($opts, $opts->{'type'})
+    or return undef;
+  if ($opts->{'type'} eq 'gif') {
     my $gif_delays = $opts->{gif_delays};
     local $opts->{gif_delays} = $gif_delays;
-    unless (ref $opts->{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 ];
     }
-    # translate to ImgRaw
-    if (grep !UNIVERSAL::isa($_, 'Imager') || !$_->{IMG}, @images) {
-      $ERRSTR = "Usage: Imager->write_multi({ options }, @images)";
-      return 0;
-    }
-    my @work = map $_->{IMG}, @images;
-    if ($opts->{callback}) {
-      # Note: you may need to fix giflib for this one to work
-      my $maxbuffer = $opts->{maxbuffer};
-      defined $maxbuffer or $maxbuffer = -1; # max by default
-      return i_writegif_callback($opts->{callback}, $maxbuffer,
-                                $opts, @work);
-    }
-    if ($opts->{fd}) {
-      return i_writegif_gen($opts->{fd}, $opts, @work);
+    my $res = i_writegif_wiol($IO, $opts, @work);
+    $res or $class->_set_error($class->_error_as_msg());
+    return $res;
+  }
+  elsif ($opts->{'type'} eq 'tiff') {
+    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 {
-      my $fh = IO::File->new($opts->{file}, "w+");
-      unless ($fh) {
-       $ERRSTR = "Error creating $opts->{file}: $!";
-       return 0;
-      }
-      binmode($fh);
-      return i_writegif_gen(fileno($fh), $opts, @work);
+      $res = i_writetiff_multi_wiol($IO, @work);
     }
+    $res or $class->_set_error($class->_error_as_msg());
+    return $res;
   }
   else {
-    $ERRSTR = "Sorry, write_multi doesn't support $opts->{type} yet";
+    $ERRSTR = "Sorry, write_multi doesn't support $opts->{'type'} yet";
     return 0;
   }
 }
@@ -1187,61 +1325,33 @@ sub write_multi {
 sub read_multi {
   my ($class, %opts) = @_;
 
-  if ($opts{file} && !exists $opts{type}) {
+  if ($opts{file} && !exists $opts{'type'}) {
     # guess the type 
     my $type = $FORMATGUESS->($opts{file});
-    $opts{type} = $type;
+    $opts{'type'} = $type;
   }
-  unless ($opts{type}) {
+  unless ($opts{'type'}) {
     $ERRSTR = "No type parameter supplied and it couldn't be guessed";
     return;
   }
-  my $fd;
-  my $file;
-  if ($opts{file}) {
-    $file = IO::File->new($opts{file}, "r");
-    unless ($file) {
-      $ERRSTR = "Could not open file $opts{file}: $!";
-      return;
-    }
-    binmode $file;
-    $fd = fileno($file);
-  }
-  elsif ($opts{fh}) {
-    $fd = fileno($opts{fh});
-    unless ($fd) {
-      $ERRSTR = "File handle specified with fh option not open";
-      return;
-    }
-  }
-  elsif ($opts{fd}) {
-    $fd = $opts{fd};
-  }
-  elsif ($opts{callback} || $opts{data}) {
-    # don't fail here
-  }
-  else {
-    $ERRSTR = "You need to specify one of file, fd, fh, callback or data";
-    return;
-  }
 
-  if ($opts{type} eq 'gif') {
+  my ($IO, $file) = $class->_get_reader_io(\%opts, $opts{'type'})
+    or return;
+  if ($opts{'type'} eq 'gif') {
     my @imgs;
-    if ($fd) {
-      @imgs = i_readgif_multi($fd);
+    @imgs = i_readgif_multi_wiol($IO);
+    if (@imgs) {
+      return map { 
+        bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager' 
+      } @imgs;
     }
     else {
-      if (Imager::i_giflib_version() < 4.0) {
-        $ERRSTR = "giflib3.x does not support callbacks";
-        return;
-      }
-      if ($opts{callback}) {
-        @imgs = i_readgif_multi_callback($opts{callback})
-      }
-      else {
-        @imgs = i_readgif_multi_scalar($opts{data});
-      }
+      $ERRSTR = _error_as_msg();
+      return;
     }
+  }
+  elsif ($opts{'type'} eq 'tiff') {
+    my @imgs = i_readtiff_multi_wiol($IO, -1);
     if (@imgs) {
       return map { 
         bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager' 
@@ -1253,7 +1363,7 @@ sub read_multi {
     }
   }
 
-  $ERRSTR = "Cannot read multiple images from $opts{type} files";
+  $ERRSTR = "Cannot read multiple images from $opts{'type'} files";
   return;
 }
 
@@ -1283,35 +1393,35 @@ sub filter {
   my %hsh;
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
 
-  if (!$input{type}) { $self->{ERRSTR}='type parameter missing'; return undef; }
+  if (!$input{'type'}) { $self->{ERRSTR}='type parameter missing'; return undef; }
 
-  if ( (grep { $_ eq $input{type} } keys %filters) != 1) {
+  if ( (grep { $_ eq $input{'type'} } keys %filters) != 1) {
     $self->{ERRSTR}='type parameter not matching any filter'; return undef;
   }
 
-  if ($filters{$input{type}}{names}) {
-    my $names = $filters{$input{type}}{names};
+  if ($filters{$input{'type'}}{names}) {
+    my $names = $filters{$input{'type'}}{names};
     for my $name (keys %$names) {
       if (defined $input{$name} && exists $names->{$name}{$input{$name}}) {
         $input{$name} = $names->{$name}{$input{$name}};
       }
     }
   }
-  if (defined($filters{$input{type}}{defaults})) {
-    %hsh=('image',$self->{IMG},%{$filters{$input{type}}{defaults}},%input);
+  if (defined($filters{$input{'type'}}{defaults})) {
+    %hsh=('image',$self->{IMG},%{$filters{$input{'type'}}{defaults}},%input);
   } else {
     %hsh=('image',$self->{IMG},%input);
   }
 
-  my @cs=@{$filters{$input{type}}{callseq}};
+  my @cs=@{$filters{$input{'type'}}{callseq}};
 
   for(@cs) {
     if (!defined($hsh{$_})) {
-      $self->{ERRSTR}="missing parameter '$_' for filter ".$input{type}; return undef;
+      $self->{ERRSTR}="missing parameter '$_' for filter ".$input{'type'}; return undef;
     }
   }
 
-  &{$filters{$input{type}}{callsub}}(%hsh);
+  &{$filters{$input{'type'}}{callsub}}(%hsh);
 
   my @b=keys %hsh;
 
@@ -1325,16 +1435,16 @@ sub filter {
 
 sub scale {
   my $self=shift;
-  my %opts=(scalefactor=>0.5,type=>'max',qtype=>'normal',@_);
+  my %opts=(scalefactor=>0.5,'type'=>'max',qtype=>'normal',@_);
   my $img = Imager->new();
   my $tmp = Imager->new();
 
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
 
-  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); }
+    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(); }
 
@@ -1418,9 +1528,9 @@ sub transform {
                                             {op=>'-',trans=>'Sub'},
                                             {op=>'*',trans=>'Mult'},
                                             {op=>'/',trans=>'Div'},
-                                            {op=>'-',type=>'unary',trans=>'u-'},
+                                            {op=>'-','type'=>'unary',trans=>'u-'},
                                             {op=>'**'},
-                                            {op=>'func',type=>'unary'}],
+                                            {op=>'func','type'=>'unary'}],
                                     'grouping'=>[qw( \( \) )],
                                     'func'=>[qw( sin cos )],
                                     'vars'=>[qw( x y )]
@@ -1489,65 +1599,57 @@ sub transform {
 }
 
 
-{
-  my $got_expr;
-  sub transform2 {
-    my ($opts, @imgs) = @_;
-
-    if (!$got_expr) {
-      # this is fairly big, delay loading it
-      eval "use Imager::Expr";
-      die $@ if $@;
-      ++$got_expr;
-    }
-
-    $opts->{variables} = [ qw(x y) ];
-    my ($width, $height) = @{$opts}{qw(width height)};
-    if (@imgs) {
-       $width ||= $imgs[0]->getwidth();
-       $height ||= $imgs[0]->getheight();
-       my $img_num = 1;
-       for my $img (@imgs) {
-           $opts->{constants}{"w$img_num"} = $img->getwidth();
-           $opts->{constants}{"h$img_num"} = $img->getheight();
-           $opts->{constants}{"cx$img_num"} = $img->getwidth()/2;
-           $opts->{constants}{"cy$img_num"} = $img->getheight()/2;
-           ++$img_num;
-       }
-    }
-    if ($width) {
-      $opts->{constants}{w} = $width;
-      $opts->{constants}{cx} = $width/2;
-    }
-    else {
-      $Imager::ERRSTR = "No width supplied";
-      return;
-    }
-    if ($height) {
-      $opts->{constants}{h} = $height;
-      $opts->{constants}{cy} = $height/2;
-    }
-    else {
-      $Imager::ERRSTR = "No height supplied";
-      return;
-    }
-    my $code = Imager::Expr->new($opts);
-    if (!$code) {
-      $Imager::ERRSTR = Imager::Expr::error();
-      return;
-    }
-
-    my $img = Imager->new();
-    $img->{IMG} = i_transform2($opts->{width}, $opts->{height}, $code->code(),
-                              $code->nregs(), $code->cregs(),
-                              [ map { $_->{IMG} } @imgs ]);
-    if (!defined $img->{IMG}) {
-      $Imager::ERRSTR = "transform2 failed";
-      return;
+sub transform2 {
+  my ($opts, @imgs) = @_;
+  
+  require "Imager/Expr.pm";
+
+  $opts->{variables} = [ qw(x y) ];
+  my ($width, $height) = @{$opts}{qw(width height)};
+  if (@imgs) {
+    $width ||= $imgs[0]->getwidth();
+    $height ||= $imgs[0]->getheight();
+    my $img_num = 1;
+    for my $img (@imgs) {
+      $opts->{constants}{"w$img_num"} = $img->getwidth();
+      $opts->{constants}{"h$img_num"} = $img->getheight();
+      $opts->{constants}{"cx$img_num"} = $img->getwidth()/2;
+      $opts->{constants}{"cy$img_num"} = $img->getheight()/2;
+      ++$img_num;
     }
+  }
+  if ($width) {
+    $opts->{constants}{w} = $width;
+    $opts->{constants}{cx} = $width/2;
+  }
+  else {
+    $Imager::ERRSTR = "No width supplied";
+    return;
+  }
+  if ($height) {
+    $opts->{constants}{h} = $height;
+    $opts->{constants}{cy} = $height/2;
+  }
+  else {
+    $Imager::ERRSTR = "No height supplied";
+    return;
+  }
+  my $code = Imager::Expr->new($opts);
+  if (!$code) {
+    $Imager::ERRSTR = Imager::Expr::error();
+    return;
+  }
 
-    return $img;
+  my $img = Imager->new();
+  $img->{IMG} = i_transform2($opts->{width}, $opts->{height}, $code->code(),
+                             $code->nregs(), $code->cregs(),
+                             [ map { $_->{IMG} } @imgs ]);
+  if (!defined $img->{IMG}) {
+    $Imager::ERRSTR = Imager->_error_as_msg();
+    return;
   }
+
+  return $img;
 }
 
 sub rubthrough {
@@ -1670,8 +1772,13 @@ sub box {
   }
 
   if ($opts{filled}) { 
+    my $color = _color($opts{'color'});
+    unless ($color) { 
+      $self->{ERRSTR} = $Imager::ERRSTR; 
+      return; 
+    }
     i_box_filled($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},
-                 $opts{ymax},$opts{color}); 
+                 $opts{ymax}, $color); 
   }
   elsif ($opts{fill}) {
     unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
@@ -1686,7 +1793,13 @@ sub box {
                 $opts{ymax},$opts{fill}{fill});
   }
   else { 
-    i_box($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},$opts{ymax},$opts{color});
+    my $color = _color($opts{'color'});
+    unless ($color) { 
+      $self->{ERRSTR} = $Imager::ERRSTR; 
+      return; 
+    }
+    i_box($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},$opts{ymax},
+          $color);
   }
   return $self;
 }
@@ -1706,19 +1819,35 @@ sub arc {
     unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
       # assume it's a hash ref
       require 'Imager/Fill.pm';
-      $opts{fill} = Imager::Fill->new(%{$opts{fill}});
+      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 {
+    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'}, 
-                  $opts{'color'});
+                  $color);
     }
     else {
-      i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
-            $opts{'d2'},$opts{'color'}); 
+      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); 
+      }
     }
   }
 
@@ -1736,10 +1865,18 @@ sub line {
   unless (exists $opts{x1} and exists $opts{y1}) { $self->{ERRSTR}='missing begining coord'; return undef; }
   unless (exists $opts{x2} and exists $opts{y2}) { $self->{ERRSTR}='missing ending coord'; return undef; }
 
+  my $color = _color($opts{'color'});
+  unless ($color) { 
+    $self->{ERRSTR} = $Imager::ERRSTR; 
+    return; 
+  }
+  $opts{antialias} = $opts{aa} if defined $opts{aa};
   if ($opts{antialias}) {
-    i_line_aa($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2}, $opts{color});
+    i_line_aa($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2}, 
+              $color);
   } else {
-    i_draw($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2}, $opts{color});
+    i_draw($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2}, 
+           $color);
   }
   return $self;
 }
@@ -1762,21 +1899,73 @@ sub polyline {
 
 #  print Dumper(\@points);
 
+  my $color = _color($opts{'color'});
+  unless ($color) { 
+    $self->{ERRSTR} = $Imager::ERRSTR; 
+    return; 
+  }
+  $opts{antialias} = $opts{aa} if defined $opts{aa};
   if ($opts{antialias}) {
     for $pt(@points) {
-      if (defined($ls)) { i_line_aa($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$opts{color}); }
+      if (defined($ls)) { 
+        i_line_aa($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$color);
+      }
       $ls=$pt;
     }
   } else {
     for $pt(@points) {
-      if (defined($ls)) { i_draw($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$opts{color}); }
+      if (defined($ls)) { 
+        i_draw($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$color);
+      }
       $ls=$pt;
     }
   }
   return $self;
 }
 
-# this the multipoint bezier curve 
+sub polygon {
+  my $self = shift;
+  my ($pt,$ls,@points);
+  my $dflcl = i_color_new(0,0,0,0);
+  my %opts = (color=>$dflcl, @_);
+
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  if (exists($opts{points})) {
+    $opts{'x'} = [ map { $_->[0] } @{$opts{points}} ];
+    $opts{'y'} = [ map { $_->[1] } @{$opts{points}} ];
+  }
+
+  if (!exists $opts{'x'} or !exists $opts{'y'})  {
+    $self->{ERRSTR} = 'no points array, or x and y arrays.'; 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 undef;
+      }
+    }
+    i_poly_aa_cfill($self->{IMG}, $opts{'x'}, $opts{'y'}, 
+                    $opts{'fill'}{'fill'});
+  }
+  else {
+    my $color = _color($opts{'color'});
+    unless ($color) { 
+      $self->{ERRSTR} = $Imager::ERRSTR; 
+      return; 
+    }
+    i_poly_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
+  }
+
+  return $self;
+}
+
+
+# this the multipoint bezier curve
 # this is here more for testing that actual usage since
 # this is not a good algorithm.  Usually the curve would be
 # broken into smaller segments and each done individually.
@@ -1799,7 +1988,12 @@ sub polybezier {
     return;
   }
 
-  i_bezier_multi($self->{IMG},$opts{'x'},$opts{'y'},$opts{'color'});
+  my $color = _color($opts{'color'});
+  unless ($color) { 
+    $self->{ERRSTR} = $Imager::ERRSTR; 
+    return; 
+  }
+  i_bezier_multi($self->{IMG},$opts{'x'},$opts{'y'},$color);
   return $self;
 }
 
@@ -1807,21 +2001,29 @@ sub flood_fill {
   my $self = shift;
   my %opts = ( color=>Imager::Color->new(255, 255, 255), @_ );
 
-  unless (exists $opts{x} && exists $opts{'y'}) {
+  unless (exists $opts{'x'} && exists $opts{'y'}) {
     $self->{ERRSTR} = "missing seed x and y parameters";
     return undef;
   }
-  
+
   if ($opts{fill}) {
     unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
       # assume it's a hash ref
       require 'Imager/Fill.pm';
-      $opts{fill} = Imager::Fill->new(%{$opts{fill}});
+      unless ($opts{fill} = Imager::Fill->new(%{$opts{fill}})) {
+        $self->{ERRSTR} = $Imager::ERRSTR;
+        return;
+      }
     }
-    i_flood_cfill($self->{IMG}, $opts{x}, $opts{'y'}, $opts{fill}{fill});
+    i_flood_cfill($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{fill}{fill});
   }
   else {
-    i_flood_fill($self->{IMG}, $opts{x}, $opts{'y'}, $opts{color});
+    my $color = _color($opts{'color'});
+    unless ($color) { 
+      $self->{ERRSTR} = $Imager::ERRSTR; 
+      return; 
+    }
+    i_flood_fill($self->{IMG}, $opts{'x'}, $opts{'y'}, $color);
   }
 
   $self;
@@ -2016,7 +2218,7 @@ sub setmask {
 
 sub getcolorcount {
   my $self=shift;
-  my %opts=(maxcolors=>2**30,@_);
+  my %opts=('maxcolors'=>2**30,@_);
   if (!defined($self->{IMG})) { $self->{ERRSTR}='image is empty'; return undef; }
   my $rc=i_count_colors($self->{IMG},$opts{'maxcolors'});
   return ($rc==-1? undef : $rc);
@@ -2067,6 +2269,17 @@ sub errstr {
   ref $_[0] ? $_[0]->{ERRSTR} : $ERRSTR
 }
 
+sub _set_error {
+  my ($self, $msg) = @_;
+
+  if (ref $self) {
+    $self->{ERRSTR} = $msg;
+  }
+  else {
+    $ERRSTR = $msg;
+  }
+}
+
 # Default guess for the type of an image from extension
 
 sub def_guess_type {
@@ -2079,7 +2292,9 @@ 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 'rgb'  if ($ext eq "rgb");
   return 'gif'  if ($ext eq "gif");
+  return 'raw'  if ($ext eq "raw");
   return ();
 }
 
@@ -2163,9 +2378,8 @@ Imager - Perl extension for Generating 24 bit Images
 
 =head1 SYNOPSIS
 
-  use Imager qw(init);
+  use Imager;
 
-  init();
   $img = Imager->new();
   $img->open(file=>'image.ppm',type=>'pnm')
     || print "failed: ",$img->{ERRSTR},"\n";
@@ -2237,15 +2451,22 @@ Warning: if you draw on a paletted image with colors that aren't in
 the palette, the image will be internally converted to a normal image.
 
 For improved color precision you can use the bits parameter to specify
-16 bites per channel:
+16 bit per channel:
 
   $img = Imager->new(xsize=>200, ysize=>200, channels=>3, bits=>16);
 
-Note that as of this writing all functions should work on 16-bit
-images, but at only 8-bit/channel precision.
+or for even more precision:
 
-Currently only 8 and 16/bit per channel image types are available,
-this may change later.
+  $img = Imager->new(xsize=>200, ysize=>200, channels=>3, bits=>'double');
+
+to get an image that uses a double for each channel.
+
+Note that as of this writing all functions should work on images with
+more than 8-bits/channel, but many will only work at only
+8-bit/channel precision.
+
+Currently only 8-bit, 16-bit, and double per channel image types are
+available, this may change later.
 
 Color objects are created by calling the Imager::Color->new()
 method:
@@ -2262,6 +2483,105 @@ downwards.
 
 =head2 Reading and writing images
 
+You can read and write a variety of images formats, assuming you have
+the appropriate libraries, and images can be read or written to/from
+files, file handles, file descriptors, scalars, or through callbacks.
+
+To see which image formats Imager is compiled to support the following
+code snippet is sufficient:
+
+  use Imager;
+  print join " ", keys %Imager::formats;
+
+This will include some other information identifying libraries rather
+than file formats.
+
+Reading writing to and from files is simple, use the C<read()>
+method to read an image:
+
+  my $img = Imager->new;
+  $img->read(file=>$filename, type=>$type)
+    or die "Cannot read $filename: ", $img->errstr;
+
+and the C<write()> method to write an image:
+
+  $img->write(file=>$filename, type=>$type)
+    or die "Cannot write $filename: ", $img->errstr;
+
+If the I<filename> includes an extension that Imager recognizes, then
+you don't need the I<type>, but you may want to provide one anyway.
+Imager currently does not check the files magic to determine the
+format.  It is possible to override the method for determining the 
+filetype from the filename.  If the data is given in another form than
+a file name a 
+
+When you read an image, Imager may set some tags, possibly including
+information about the spatial resolution, textual information, and
+animation information.  See L</Tags> for specifics.
+
+When reading or writing you can specify one of a variety of sources or
+targets:
+
+=over
+
+=item file
+
+The C<file> parameter is the name of the image file to be written to
+or read from.  If Imager recognizes the extension of the file you do
+not need to supply a C<type>.
+
+=item fh
+
+C<fh> is a file handle, typically either returned from
+C<<IO::File->new()>>, or a glob from an C<open> call.  You should call
+C<binmode> on the handle before passing it to Imager.
+
+=item fd
+
+C<fd> is a file descriptor.  You can get this by calling the
+C<fileno()> function on a file handle, or by using one of the standard
+file descriptor numbers.
+
+=item data
+
+When reading data, C<data> is a scalar containing the image file data,
+when writing, C<data> is a reference to the scalar to save the image
+file data too.  For GIF images you will need giflib 4 or higher, and
+you may need to patch giflib to use this option for writing.
+
+=item callback
+
+Imager will make calls back to your supplied coderefs to read, write
+and seek from/to/through the image file.
+
+When reading from a file you can use either C<callback> or C<readcb>
+to supply the read callback, and when writing C<callback> or
+C<writecb> to supply the write callback.
+
+When writing you can also supply the C<maxbuffer> option to set the
+maximum amount of data that will be buffered before your write
+callback is called.  Note: the amount of data supplied to your
+callback can be smaller or larger than this size.
+
+The read callback is called with 2 parameters, the minimum amount of
+data required, and the maximum amount that Imager will store in it's C
+level buffer.  You may want to return the minimum if you have a slow
+data source, or the maximum if you have a fast source and want to
+prevent many calls to your perl callback.  The read data should be
+returned as a scalar.
+
+Your write callback takes exactly one parameter, a scalar containing
+the data to be written.  Return true for success.
+
+The seek callback takes 2 parameters, a I<POSITION>, and a I<WHENCE>,
+defined in the same way as perl's seek function.
+
+You can also supply a C<closecb> which is called with no parameters
+when there is no more data to be written.  This could be used to flush
+buffered data.
+
+=back
+
 C<$img-E<gt>read()> generally takes two parameters, 'file' and 'type'.
 If the type of the file can be determined from the suffix of the file
 it can be omitted.  Format dependant parameters are: For images of
@@ -2333,6 +2653,14 @@ of Imager::Color objects.  Note that the local means algorithm needs
 much more cpu time but also gives considerable better results than the
 median cut algorithm.
 
+When storing targa images rle compression can be activated with the
+'compress' parameter, the 'idstring' parameter can be used to set the
+targa comment field and the 'wierdpack' option can be used to use the
+15 and 16 bit targa formats for rgb and rgba data.  The 15 bit format
+has 5 of each red, green and blue.  The 16 bit format in addition
+allows 1 bit of alpha.  The most significant bits are used for each
+channel.
+
 Currently just for gif files, you can specify various options for the
 conversion from Imager's internal RGB format to the target's indexed
 file format.  If you set the gifquant option to 'gen', you can use the
@@ -2797,7 +3125,8 @@ the function return undef.  Examples:
   }
 
 The bits() method retrieves the number of bits used to represent each
-channel in a pixel, typically 8.  The type() method returns either
+channel in a pixel, 8 for a normal image, 16 for 16-bit image and
+'double' for a double/channel image.  The type() method returns either
 'direct' for truecolor images or 'paletted' for paletted images.  The
 virtual() method returns non-zero if the image contains no actual
 pixels, for example masked images.
@@ -2850,8 +3179,8 @@ IMPLEMENTATION MORE OR LESS DONE CHECK THE TESTS
 DOCUMENTATION OF THIS SECTION OUT OF SYNC
 
 It is possible to draw with graphics primitives onto images.  Such
-primitives include boxes, arcs, circles and lines.  A reference
-oriented list follows.
+primitives include boxes, arcs, circles, polygons and lines.  A
+reference oriented list follows.
 
 Box:
   $img->box(color=>$blue,xmin=>10,ymin=>30,xmax=>200,ymax=>300,filled=>1);
@@ -2869,18 +3198,6 @@ Arc:
 This creates a filled red arc with a 'center' at (200, 100) and spans
 10 degrees and the slice has a radius of 20. SEE section on BUGS.
 
-Both the arc() and box() methods can take a C<fill> parameter which
-can either be an Imager::Fill object, or a reference to a hash
-containing the parameters used to create the fill:
-
-  $img->box(xmin=>10, ymin=>30, xmax=>150, ymax=>60,
-            fill => { hatch=>'cross2' });
-  use Imager::Fill;
-  my $fill = Imager::Fill->new(hatch=>'stipple');
-  $img->box(fill=>$fill);
-
-See L<Imager::Fill> for the type of fills you can use.
-
 Circle:
   $img->circle(color=>$green, r=50, x=>200, y=>100);
 
@@ -2888,20 +3205,35 @@ This creates a green circle with its center at (200, 100) and has a
 radius of 20.
 
 Line:
-  $img->line(color=>$green, x1=10, x2=>100, 
-                            y1=>20, y2=>50, antialias=>1 );
+  $img->line(color=>$green, x1=>10, x2=>100,
+                            y1=>20, y2=>50, aa=>1 );
 
 That draws an antialiased line from (10,100) to (20,50).
 
+The I<antialias> parameter is still available for backwards compatibility.
+
 Polyline:
   $img->polyline(points=>[[$x0,$y0],[$x1,$y1],[$x2,$y2]],color=>$red);
-  $img->polyline(x=>[$x0,$x1,$x2], y=>[$y0,$y1,$y2], antialias=>1);
+  $img->polyline(x=>[$x0,$x1,$x2], y=>[$y0,$y1,$y2], aa=>1);
 
 Polyline is used to draw multilple lines between a series of points.
 The point set can either be specified as an arrayref to an array of
 array references (where each such array represents a point).  The
 other way is to specify two array references.
 
+The I<antialias> parameter is still available for backwards compatibility.
+
+Polygon:
+  $img->polygon(points=>[[$x0,$y0],[$x1,$y1],[$x2,$y2]],color=>$red);
+  $img->polygon(x=>[$x0,$x1,$x2], y=>[$y0,$y1,$y2]);
+
+Polygon is used to draw a filled polygon.  Currently the polygon is
+always drawn antialiased, although that will change in the future.
+Like other antialiased drawing functions its coordinates can be
+specified with floating point values.
+
+Flood Fill:
+
 You can fill a region that all has the same color using the
 flood_fill() method, for example:
 
@@ -2909,12 +3241,24 @@ flood_fill() method, for example:
 
 will fill all regions the same color connected to the point (50, 50).
 
-You can also use a general fill, so you could fill the same region
-with a check pattern using:
+The arc(), box(), polygon() and flood_fill() methods can take a
+C<fill> parameter which can either be an Imager::Fill object, or a
+reference to a hash containing the parameters used to create the fill:
 
-  $img->flood_fill(x=>50, y=>50, fill=>{ hatch=>'check2x2' });
+  $img->box(xmin=>10, ymin=>30, xmax=>150, ymax=>60,
+            fill => { hatch=>'cross2' });
+  use Imager::Fill;
+  my $fill = Imager::Fill->new(hatch=>'stipple');
+  $img->box(fill=>$fill);
 
-See L<Imager::Fill> for more information on general fills.
+Currently you can create opaque or transparent plain color fills,
+hatched fills, image based fills and fountain fills.  See
+L<Imager::Fill> for more information.
+
+The C<color> parameter for any of the drawing methods can be an
+L<Imager::Color> object, a simple scalar that Imager::Color can
+understand, a hashref of parameters that Imager::Color->new
+understands, or an arrayref of red, green, blue values.
 
 =head2 Text rendering
 
@@ -2928,13 +3272,14 @@ parameter they will determine the width or height respectively.  If
 both are given the one resulting in a larger image is used.  example:
 C<$img> is 700 pixels wide and 500 pixels tall.
 
-  $img->scale(xpixels=>400); # 400x285
-  $img->scale(ypixels=>400); # 560x400
+  $newimg = $img->scale(xpixels=>400); # 400x285
+  $newimg = $img->scale(ypixels=>400); # 560x400
 
-  $img->scale(xpixels=>400,ypixels=>400); # 560x400
-  $img->scale(xpixels=>400,ypixels=>400,type=>min); # 400x285
+  $newimg = $img->scale(xpixels=>400,ypixels=>400); # 560x400
+  $newimg = $img->scale(xpixels=>400,ypixels=>400,type=>min); # 400x285
 
-  $img->scale(scalefactor=>0.25); 175x125 $img->scale(); # 350x250
+  $newimg = $img->scale(scalefactor=>0.25); 175x125 
+  $newimg = $img->scale(); # 350x250
 
 if you want to create low quality previews of images you can pass
 C<qtype=E<gt>'preview'> to scale and it will use nearest neighbor
@@ -2993,7 +3338,8 @@ parameter which can take the values C<h>, C<v>, C<vh> and C<hv>.
 
 =head2 Rotating images
 
-Use the rotate() method to rotate an image.
+Use the rotate() method to rotate an image.  This method will return a
+new, rotated image.
 
 To rotate by an exact amount in degrees or radians, use the 'degrees'
 or 'radians' parameter:
@@ -3001,6 +3347,9 @@ or 'radians' parameter:
   my $rot20 = $img->rotate(degrees=>20);
   my $rotpi4 = $img->rotate(radians=>3.14159265/4);
 
+Exact image rotation uses the same underlying transformation engine as
+the matrix_transform() method.
+
 To rotate in steps of 90 degrees, use the 'right' parameter:
 
   my $rotated = $img->rotate(right=>270);
@@ -3765,9 +4114,9 @@ Someone decides that the filter is not working as it should -
 dyntest.c modified and recompiled.
 
   load_plugin("dynfilt/dyntest.so") || die "unable to load plugin\n";
-  $img->filter(%hsh); 
+  $img->filter(%hsh);
 
-An example plugin comes with the module - Please send feedback to 
+An example plugin comes with the module - Please send feedback to
 addi@umich.edu if you test this.
 
 Note: This seems to test ok on the following systems:
@@ -3897,6 +4246,29 @@ to control output:
 The value of the ResolutionUnit tag.  This is ignored on writing if
 the i_aspect_only tag is non-zero.
 
+=item tiff_documentname
+
+=item tiff_imagedescription
+
+=item tiff_make
+
+=item tiff_model
+
+=item tiff_pagename
+
+=item tiff_software
+
+=item tiff_datetime
+
+=item tiff_artist
+
+=item tiff_hostcomputer
+
+Various strings describing the image.  tiff_datetime must be formatted
+as "YYYY:MM:DD HH:MM:SS".  These correspond directly to the mixed case
+names in the TIFF specification.  These are set in images read from a
+TIFF and save when writing a TIFF image.
+
 =back
 
 The following tags are set when a Windows BMP file is read:
@@ -3931,6 +4303,11 @@ If this is non-zero then the values in i_xres and i_yres are treated
 as a ratio only.  If the image format does not support aspect ratios
 then this is scaled so the smaller value is 72dpi.
 
+=item i_incomplete
+
+If this tag is present then the whole image could not be read.  This
+isn't implemented for all images yet.
+
 =back
 
 =head1 BUGS
@@ -3951,7 +4328,7 @@ from Tony Cook.  See the README for a complete list.
 
 =head1 SEE ALSO
 
-perl(1), Imager::Color(3), Imager::Font(3), Imager::Matrix2d(3), 
+perl(1), Imager::Color(3), Imager::Font(3), Imager::Matrix2d(3),
 Affix::Infix2Postfix(3), Parse::RecDescent(3) 
 http://www.eecs.umich.edu/~addi/perl/Imager/