]> git.imager.perl.org - imager.git/blobdiff - Imager.pm
[rt.cpan.org #65385] Patch for Imager::Color->hsv
[imager.git] / Imager.pm
index ac235ea86be57e2d4e0e2f710d012597a32e6ba5..869dabc4fc153bc3915161bccae7c76a2afa265b 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -1,7 +1,7 @@
 package Imager;
 
 use strict;
 package Imager;
 
 use strict;
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR $fontstate %OPCODES $I2P $FORMATGUESS $warn_obsolete);
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR %OPCODES $I2P $FORMATGUESS $warn_obsolete);
 use IO::File;
 
 use Imager::Color;
 use IO::File;
 
 use Imager::Color;
@@ -74,24 +74,6 @@ use Imager::Font;
                i_tt_text
                i_tt_bbox
 
                i_tt_text
                i_tt_bbox
 
-               i_readjpeg_wiol
-               i_writejpeg_wiol
-
-               i_readtiff_wiol
-               i_writetiff_wiol
-               i_writetiff_wiol_faxable
-
-               i_readpng_wiol
-               i_writepng_wiol
-
-               i_readgif
-               i_readgif_wiol
-               i_readgif_callback
-               i_writegif
-               i_writegifmc
-               i_writegif_gen
-               i_writegif_callback
-
                i_readpnm_wiol
                i_writeppm_wiol
 
                i_readpnm_wiol
                i_writeppm_wiol
 
@@ -117,6 +99,7 @@ use Imager::Font;
                newcolour
                NC
                NF
                newcolour
                NC
                NF
+                NCF
 );
 
 @EXPORT=qw(
 );
 
 @EXPORT=qw(
@@ -136,6 +119,7 @@ use Imager::Font;
                newcolor
                NF
                NC
                newcolor
                NF
                NC
+                NCF
               )],
    all => [@EXPORT_OK],
    default => [qw(
               )],
    all => [@EXPORT_OK],
    default => [qw(
@@ -152,10 +136,26 @@ my %writers;
 # modules we attempted to autoload
 my %attempted_to_load;
 
 # modules we attempted to autoload
 my %attempted_to_load;
 
+# library keys that are image file formats
+my %file_formats = map { $_ => 1 } qw/tiff pnm gif png jpeg raw bmp tga/;
+
+# image pixel combine types
+my @combine_types = 
+  qw/none normal multiply dissolve add subtract diff lighten darken
+     hue saturation value color/;
+my %combine_types;
+@combine_types{@combine_types} = 0 .. $#combine_types;
+$combine_types{mult} = $combine_types{multiply};
+$combine_types{'sub'}  = $combine_types{subtract};
+$combine_types{sat}  = $combine_types{saturation};
+
+# this will be used to store global defaults at some point
+my %defaults;
+
 BEGIN {
   require Exporter;
   @ISA = qw(Exporter);
 BEGIN {
   require Exporter;
   @ISA = qw(Exporter);
-  $VERSION = '0.55';
+  $VERSION = '0.80';
   eval {
     require XSLoader;
     XSLoader::load(Imager => $VERSION);
   eval {
     require XSLoader;
     XSLoader::load(Imager => $VERSION);
@@ -167,19 +167,21 @@ BEGIN {
   }
 }
 
   }
 }
 
-BEGIN {
-  i_init_fonts(); # Initialize font engines
-  Imager::Font::__init();
-  for(i_list_formats()) { $formats{$_}++; }
+my %formats_low;
+my %format_classes =
+  (
+   png => "Imager::File::PNG",
+   gif => "Imager::File::GIF",
+   tiff => "Imager::File::TIFF",
+   jpeg => "Imager::File::JPEG",
+   w32 => "Imager::Font::W32",
+   ft2 => "Imager::Font::FT2",
+  );
 
 
-  if ($formats{'t1'}) {
-    i_t1_set_aa(1);
-  }
+tie %formats, "Imager::FORMATS", \%formats_low, \%format_classes;
 
 
-  if (!$formats{'t1'} and !$formats{'tt'} 
-      && !$formats{'ft2'} && !$formats{'w32'}) {
-    $fontstate='no font support';
-  }
+BEGIN {
+  for(i_list_formats()) { $formats_low{$_}++; }
 
   %OPCODES=(Add=>[0],Sub=>[1],Mult=>[2],Div=>[3],Parm=>[4],'sin'=>[5],'cos'=>[6],'x'=>[4,0],'y'=>[4,1]);
 
 
   %OPCODES=(Add=>[0],Sub=>[1],Mult=>[2],Div=>[3],Parm=>[4],'sin'=>[5],'cos'=>[6],'x'=>[4,0],'y'=>[4,1]);
 
@@ -212,6 +214,13 @@ BEGIN {
                         callsub => sub { my %hsh=@_; i_hardinvert($hsh{image}); }
                        };
 
                         callsub => sub { my %hsh=@_; i_hardinvert($hsh{image}); }
                        };
 
+  $filters{hardinvertall} =
+    {
+     callseq => ['image'],
+     defaults => { },
+     callsub => sub { my %hsh=@_; i_hardinvertall($hsh{image}); }
+    };
+
   $filters{autolevels} ={
                         callseq => ['image','lsat','usat','skew'],
                         defaults => { lsat=>0.1,usat=>0.1,skew=>0.0 },
   $filters{autolevels} ={
                         callseq => ['image','lsat','usat','skew'],
                         defaults => { lsat=>0.1,usat=>0.1,skew=>0.0 },
@@ -230,11 +239,17 @@ BEGIN {
                       callsub => sub { my %hsh=@_; i_radnoise($hsh{image},$hsh{xo},$hsh{yo},$hsh{rscale},$hsh{ascale}); }
                      };
 
                       callsub => sub { my %hsh=@_; i_radnoise($hsh{image},$hsh{xo},$hsh{yo},$hsh{rscale},$hsh{ascale}); }
                      };
 
-  $filters{conv} ={
-                      callseq => ['image', 'coef'],
-                      defaults => { },
-                      callsub => sub { my %hsh=@_; i_conv($hsh{image},$hsh{coef}); }
-                     };
+  $filters{conv} =
+    {
+     callseq => ['image', 'coef'],
+     defaults => { },
+     callsub => 
+     sub { 
+       my %hsh=@_;
+       i_conv($hsh{image},$hsh{coef})
+        or die Imager->_error_as_msg() . "\n";
+     }
+    };
 
   $filters{gradgen} =
     {
 
   $filters{gradgen} =
     {
@@ -304,12 +319,18 @@ BEGIN {
                  cd => 1.0,
                  cs => 40,
                  n => 1.3,
                  cd => 1.0,
                  cs => 40,
                  n => 1.3,
-                 Ia => Imager::Color->new(rgb=>[0,0,0]),
-                 Il => Imager::Color->new(rgb=>[255,255,255]),
-                 Is => Imager::Color->new(rgb=>[255,255,255]),
+                 Ia => [0,0,0],
+                 Il => [255,255,255],
+                 Is => [255,255,255],
                 },
      callsub => sub {
        my %hsh = @_;
                 },
      callsub => sub {
        my %hsh = @_;
+       for my $cname (qw/Ia Il Is/) {
+        my $old = $hsh{$cname};
+        my $new_color = _color($old)
+          or die $Imager::ERRSTR, "\n";
+        $hsh{$cname} = $new_color;
+       }
        i_bumpmap_complex($hsh{image}, $hsh{bump}{IMG}, $hsh{channel},
                  $hsh{tx}, $hsh{ty}, $hsh{Lx}, $hsh{Ly}, $hsh{Lz},
                 $hsh{cd}, $hsh{cs}, $hsh{n}, $hsh{Ia}, $hsh{Il},
        i_bumpmap_complex($hsh{image}, $hsh{bump}{IMG}, $hsh{channel},
                  $hsh{tx}, $hsh{ty}, $hsh{Lx}, $hsh{Ly}, $hsh{Lz},
                 $hsh{cd}, $hsh{cs}, $hsh{n}, $hsh{Ia}, $hsh{Il},
@@ -375,8 +396,8 @@ BEGIN {
                    super_sample => 0, ssample_param => 4,
                    segments=>[ 
                               [ 0, 0.5, 1,
                    super_sample => 0, ssample_param => 4,
                    segments=>[ 
                               [ 0, 0.5, 1,
-                                Imager::Color->new(0,0,0),
-                                Imager::Color->new(255, 255, 255),
+                                [0,0,0],
+                                [255, 255, 255],
                                 0, 0,
                               ],
                              ],
                                 0, 0,
                               ],
                              ],
@@ -423,13 +444,19 @@ BEGIN {
 # initlize Imager
 # NOTE: this might be moved to an import override later on
 
 # initlize Imager
 # NOTE: this might be moved to an import override later on
 
-#sub import {
-#  my $pack = shift;
-#  (look through @_ for special tags, process, and remove them);   
-#  use Data::Dumper;
-#  print Dumper($pack);
-#  print Dumper(@_);
-#}
+sub import {
+  my $i = 1;
+  while ($i < @_) {
+    if ($_[$i] eq '-log-stderr') {
+      init_log(undef, 4);
+      splice(@_, $i, 1);
+    }
+    else {
+      ++$i;
+    }
+  }
+  goto &Exporter::import;
+}
 
 sub init_log {
   i_init_log($_[0],$_[1]);
 
 sub init_log {
   i_init_log($_[0],$_[1]);
@@ -552,6 +579,22 @@ sub _color {
   return $result;
 }
 
   return $result;
 }
 
+sub _combine {
+  my ($self, $combine, $default) = @_;
+
+  if (!defined $combine && ref $self) {
+    $combine = $self->{combine};
+  }
+  defined $combine or $combine = $defaults{combine};
+  defined $combine or $combine = $default;
+
+  if (exists $combine_types{$combine}) {
+    $combine = $combine_types{$combine};
+  }
+  
+  return $combine;
+}
+
 sub _valid_image {
   my ($self) = @_;
 
 sub _valid_image {
   my ($self) = @_;
 
@@ -562,6 +605,14 @@ sub _valid_image {
   return;
 }
 
   return;
 }
 
+# returns first defined parameter
+sub _first {
+  for (@_) {
+    return $_ if defined $_;
+  }
+  return undef;
+}
+
 #
 # Methods to be called on objects.
 #
 #
 # Methods to be called on objects.
 #
@@ -578,13 +629,33 @@ sub new {
   $self->{IMG}=undef;    # Just to indicate what exists
   $self->{ERRSTR}=undef; #
   $self->{DEBUG}=$DEBUG;
   $self->{IMG}=undef;    # Just to indicate what exists
   $self->{ERRSTR}=undef; #
   $self->{DEBUG}=$DEBUG;
-  $self->{DEBUG} && print "Initialized Imager\n";
-  if (defined $hsh{xsize} && defined $hsh{ysize}) { 
+  $self->{DEBUG} and print "Initialized Imager\n";
+  if (defined $hsh{xsize} || defined $hsh{ysize}) { 
     unless ($self->img_set(%hsh)) {
       $Imager::ERRSTR = $self->{ERRSTR};
       return;
     }
   }
     unless ($self->img_set(%hsh)) {
       $Imager::ERRSTR = $self->{ERRSTR};
       return;
     }
   }
+  elsif (defined $hsh{file} || 
+        defined $hsh{fh} ||
+        defined $hsh{fd} ||
+        defined $hsh{callback} ||
+        defined $hsh{readcb} ||
+        defined $hsh{data}) {
+    # allow $img = Imager->new(file => $filename)
+    my %extras;
+    
+    # type is already used as a parameter to new(), rename it for the
+    # call to read()
+    if ($hsh{filetype}) {
+      $extras{type} = $hsh{filetype};
+    }
+    unless ($self->read(%hsh, %extras)) {
+      $Imager::ERRSTR = $self->{ERRSTR};
+      return;
+    }
+  }
+
   return $self;
 }
 
   return $self;
 }
 
@@ -754,7 +825,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);
@@ -839,11 +913,16 @@ sub masked {
   $result->{IMG} = i_img_masked_new($self->{IMG}, $mask, $opts{left}, 
                                     $opts{top}, $opts{right} - $opts{left},
                                     $opts{bottom} - $opts{top});
   $result->{IMG} = i_img_masked_new($self->{IMG}, $mask, $opts{left}, 
                                     $opts{top}, $opts{right} - $opts{left},
                                     $opts{bottom} - $opts{top});
+  unless ($result->{IMG}) {
+    $self->_set_error(Imager->_error_as_msg);
+    return;
+  }
+
   # keep references to the mask and base images so they don't
   # disappear on us
   $result->{DEPENDS} = [ $self->{IMG}, $mask ];
 
   # keep references to the mask and base images so they don't
   # disappear on us
   $result->{DEPENDS} = [ $self->{IMG}, $mask ];
 
-  $result;
+  return $result;
 }
 
 # convert an RGB image into a paletted image
 }
 
 # convert an RGB image into a paletted image
@@ -897,6 +976,26 @@ 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=>[], @_);
@@ -999,6 +1098,14 @@ sub virtual {
   $self->{IMG} and i_img_virtual($self->{IMG});
 }
 
   $self->{IMG} and i_img_virtual($self->{IMG});
 }
 
+sub is_bilevel {
+  my ($self) = @_;
+
+  $self->{IMG} or return;
+
+  return i_img_is_monochrome($self->{IMG});
+}
+
 sub tags {
   my ($self, %opts) = @_;
 
 sub tags {
   my ($self, %opts) = @_;
 
@@ -1131,7 +1238,7 @@ sub _get_reader_io {
   }
   elsif ($input->{fh}) {
     my $fd = fileno($input->{fh});
   }
   elsif ($input->{fh}) {
     my $fd = fileno($input->{fh});
-    unless ($fd) {
+    unless (defined $fd) {
       $self->_set_error("Handle in fh option not opened");
       return;
     }
       $self->_set_error("Handle in fh option not opened");
       return;
     }
@@ -1182,7 +1289,7 @@ sub _get_writer_io {
   }
   elsif ($input->{fh}) {
     my $fd = fileno($input->{fh});
   }
   elsif ($input->{fh}) {
     my $fd = fileno($input->{fh});
-    unless ($fd) {
+    unless (defined $fd) {
       $self->_set_error("Handle in fh option not opened");
       return;
     }
       $self->_set_error("Handle in fh option not opened");
       return;
     }
@@ -1254,35 +1361,15 @@ sub read {
     return $readers{$input{type}}{single}->($self, $IO, %input);
   }
 
     return $readers{$input{type}}{single}->($self, $IO, %input);
   }
 
-  unless ($formats{$input{'type'}}) {
-    $self->_set_error("format '$input{'type'}' not supported");
+  unless ($formats_low{$input{'type'}}) {
+    my $read_types = join ', ', sort Imager->read_types();
+    $self->_set_error("format '$input{'type'}' not supported - formats $read_types available for reading");
     return;
   }
 
     return;
   }
 
-  # Setup data source
-  if ( $input{'type'} eq 'jpeg' ) {
-    ($self->{IMG},$self->{IPTCRAW}) = i_readjpeg_wiol( $IO );
-    if ( !defined($self->{IMG}) ) {
-      $self->{ERRSTR}=$self->_error_as_msg(); return undef;
-    }
-    $self->{DEBUG} && print "loading a jpeg file\n";
-    return $self;
-  }
-
   my $allow_incomplete = $input{allow_incomplete};
   defined $allow_incomplete or $allow_incomplete = 0;
 
   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;
-    $self->{IMG}=i_readtiff_wiol( $IO, $allow_incomplete, $page ); 
-    if ( !defined($self->{IMG}) ) {
-      $self->{ERRSTR}=$self->_error_as_msg(); return undef;
-    }
-    $self->{DEBUG} && print "loading a tiff file\n";
-    return $self;
-  }
-
   if ( $input{'type'} eq 'pnm' ) {
     $self->{IMG}=i_readpnm_wiol( $IO, $allow_incomplete );
     if ( !defined($self->{IMG}) ) {
   if ( $input{'type'} eq 'pnm' ) {
     $self->{IMG}=i_readpnm_wiol( $IO, $allow_incomplete );
     if ( !defined($self->{IMG}) ) {
@@ -1293,15 +1380,6 @@ sub read {
     return $self;
   }
 
     return $self;
   }
 
-  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} = $self->_error_as_msg();
-      return undef;
-    }
-    $self->{DEBUG} && print "loading a png file\n";
-  }
-
   if ( $input{'type'} eq 'bmp' ) {
     $self->{IMG}=i_readbmp_wiol( $IO, $allow_incomplete );
     if ( !defined($self->{IMG}) ) {
   if ( $input{'type'} eq 'bmp' ) {
     $self->{IMG}=i_readbmp_wiol( $IO, $allow_incomplete );
     if ( !defined($self->{IMG}) ) {
@@ -1333,7 +1411,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})) ];
       }
@@ -1355,30 +1433,27 @@ 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();
+  if ( $input{'type'} eq 'raw' ) {
+    unless ( $input{xsize} && $input{ysize} ) {
+      $self->_set_error('missing xsize or ysize parameter for raw');
       return undef;
     }
       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}) ) {
-      $self->{ERRSTR}='missing xsize or ysize parameter for raw';
-      return undef;
+    my $interleave = _first($input{raw_interleave}, $input{interleave});
+    unless (defined $interleave) {
+      my @caller = caller;
+      warn "read(type => 'raw') $caller[2] line $caller[1]: supply interleave or raw_interleave for future compatibility\n";
+      $interleave = 1;
     }
     }
+    my $data_ch = _first($input{raw_datachannels}, $input{datachannels}, 3);
+    my $store_ch = _first($input{raw_storechannels}, $input{storechannels}, 3);
 
     $self->{IMG} = i_readraw_wiol( $IO,
 
     $self->{IMG} = i_readraw_wiol( $IO,
-                                  $params{xsize},
-                                  $params{ysize},
-                                  $params{datachannels},
-                                  $params{storechannels},
-                                  $params{interleave});
+                                  $input{xsize},
+                                  $input{ysize},
+                                  $data_ch,
+                                  $store_ch,
+                                  $interleave);
     if ( !defined($self->{IMG}) ) {
       $self->{ERRSTR}=$self->_error_as_msg();
       return undef;
     if ( !defined($self->{IMG}) ) {
       $self->{ERRSTR}=$self->_error_as_msg();
       return undef;
@@ -1433,11 +1508,35 @@ sub register_writer {
   return 1;
 }
 
   return 1;
 }
 
+sub read_types {
+  my %types =
+    (
+     map { $_ => 1 }
+     keys %readers,
+     grep($file_formats{$_}, keys %formats),
+     qw(ico sgi), # formats not handled directly, but supplied with Imager
+    );
+
+  return keys %types;
+}
+
+sub write_types {
+  my %types =
+    (
+     map { $_ => 1 }
+     keys %writers,
+     grep($file_formats{$_}, keys %formats),
+     qw(ico sgi), # formats not handled directly, but supplied with Imager
+    );
+
+  return keys %types;
+}
+
 # probes for an Imager::File::whatever module
 sub _reader_autoload {
   my $type = shift;
 
 # probes for an Imager::File::whatever module
 sub _reader_autoload {
   my $type = shift;
 
-  return if $formats{$type} || $readers{$type};
+  return if $formats_low{$type} || $readers{$type};
 
   return unless $type =~ /^\w+$/;
 
 
   return unless $type =~ /^\w+$/;
 
@@ -1465,7 +1564,7 @@ sub _reader_autoload {
 sub _writer_autoload {
   my $type = shift;
 
 sub _writer_autoload {
   my $type = shift;
 
-  return if $formats{$type} || $readers{$type};
+  return if $formats_low{$type} || $readers{$type};
 
   return unless $type =~ /^\w+$/;
 
 
   return unless $type =~ /^\w+$/;
 
@@ -1512,6 +1611,9 @@ my %obsolete_opts =
    gif_loop_count => 'gif_loop',
   );
 
    gif_loop_count => 'gif_loop',
   );
 
+# options that should be converted to colors
+my %color_opts = map { $_ => 1 } qw/i_background/;
+
 sub _set_opts {
   my ($self, $opts, $prefix, @imgs) = @_;
 
 sub _set_opts {
   my ($self, $opts, $prefix, @imgs) = @_;
 
@@ -1532,6 +1634,13 @@ sub _set_opts {
     }
     next unless $tagname =~ /^\Q$prefix/;
     my $value = $opts->{$opt};
     }
     next unless $tagname =~ /^\Q$prefix/;
     my $value = $opts->{$opt};
+    if ($color_opts{$opt}) {
+      $value = _color($value);
+      unless ($value) {
+       $self->_set_error($Imager::ERRSTR);
+       return;
+      }
+    }
     if (ref $value) {
       if (UNIVERSAL::isa($value, "Imager::Color")) {
         my $tag = sprintf("color(%d,%d,%d,%d)", $value->rgba);
     if (ref $value) {
       if (UNIVERSAL::isa($value, "Imager::Color")) {
         my $tag = sprintf("color(%d,%d,%d,%d)", $value->rgba);
@@ -1614,32 +1723,16 @@ sub write {
       or return undef;
   }
   else {
       or return undef;
   }
   else {
-    if (!$formats{$input{'type'}}) { 
-      $self->{ERRSTR}='format not supported'; 
+    if (!$formats_low{$input{'type'}}) { 
+      my $write_types = join ', ', sort Imager->write_types();
+      $self->_set_error("format '$input{'type'}' not supported - formats $write_types available for writing");
       return undef;
     }
     
     ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
       or return undef;
     
       return undef;
     }
     
     ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
       or return undef;
     
-    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' ) {
+    if ( $input{'type'} eq 'pnm' ) {
       $self->_set_opts(\%input, "pnm_", $self)
         or return undef;
       if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
       $self->_set_opts(\%input, "pnm_", $self)
         or return undef;
       if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
@@ -1655,14 +1748,6 @@ sub write {
         return undef;
       }
       $self->{DEBUG} && print "writing a raw file\n";
         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;
     } elsif ( $input{'type'} eq 'jpeg' ) {
       $self->_set_opts(\%input, "jpeg_", $self)
         or return undef;
@@ -1755,44 +1840,15 @@ sub write_multi {
   }
   else {
     if (!$formats{$type}) { 
   }
   else {
     if (!$formats{$type}) { 
-      $class->_set_error("format $type not supported"); 
+      my $write_types = join ', ', sort Imager->write_types();
+      $class->_set_error("format '$type' not supported - formats $write_types available for writing");
       return undef;
     }
     
     ($IO, $file) = $class->_get_writer_io($opts, $type)
       or return undef;
     
       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;
-      }
+    if (0) { # eventually PNM in here, now that TIFF/GIF are elsewhere
     }
     else {
       if (@images == 1) {
     }
     else {
       if (@images == 1) {
@@ -1846,40 +1902,32 @@ sub read_multi {
     return $readers{$type}{multiple}->($IO, %opts);
   }
 
     return $readers{$type}{multiple}->($IO, %opts);
   }
 
-  if ($type eq 'gif') {
-    my @imgs;
-    @imgs = i_readgif_multi_wiol($IO);
-    if (@imgs) {
-      return map { 
-        bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager' 
-      } @imgs;
-    }
-    else {
-      $ERRSTR = _error_as_msg();
-      return;
-    }
+  unless ($formats{$type}) {
+    my $read_types = join ', ', sort Imager->read_types();
+    Imager->_set_error("format '$type' not supported - formats $read_types available for reading");
+    return;
   }
   }
-  elsif ($type eq 'tiff') {
-    my @imgs = i_readtiff_multi_wiol($IO, -1);
-    if (@imgs) {
-      return map { 
-        bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager' 
-      } @imgs;
-    }
-    else {
-      $ERRSTR = _error_as_msg();
-      return;
-    }
+
+  my @imgs;
+  if ($type eq 'pnm') {
+    @imgs = i_readpnm_multi_wiol($IO, $opts{allow_incomplete}||0);
   }
   else {
     my $img = Imager->new;
     if ($img->read(%opts, io => $IO, type => $type)) {
       return ( $img );
     }
   }
   else {
     my $img = Imager->new;
     if ($img->read(%opts, io => $IO, type => $type)) {
       return ( $img );
     }
+    Imager->_set_error($img->errstr);
+    return;
   }
 
   }
 
-  $ERRSTR = "Cannot read multiple images from $type files";
+  if (!@imgs) {
+    $ERRSTR = _error_as_msg();
   return;
   return;
+  }
+  return map { 
+        bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager' 
+      } @imgs;
 }
 
 # Destroy an Imager object
 }
 
 # Destroy an Imager object
@@ -1977,24 +2025,31 @@ sub register_filter {
   return 1;
 }
 
   return 1;
 }
 
-# Scale an image to requested size and return the scaled version
+sub scale_calculate {
+  my $self = shift;
 
 
-sub scale {
-  my $self=shift;
-  my %opts=('type'=>'max',qtype=>'normal',@_);
-  my $img = Imager->new();
-  my $tmp = Imager->new();
-  my ($x_scale, $y_scale);
+  my %opts = ('type'=>'max', @_);
 
 
-  unless (defined wantarray) {
-    my @caller = caller;
-    warn "scale() called in void context - scale() returns the scaled image at $caller[1] line $caller[2]\n";
-    return;
+  # none of these should be references
+  for my $name (qw/xpixels ypixels xscalefactor yscalefactor width height/) {
+    if (defined $opts{$name} && ref $opts{$name}) {
+      $self->_set_error("scale_calculate: $name parameter cannot be a reference");
+      return;
+    }
   }
 
   }
 
-  unless ($self->{IMG}) { 
-    $self->_set_error('empty input image'); 
-    return undef;
+  my ($x_scale, $y_scale);
+  my $width = $opts{width};
+  my $height = $opts{height};
+  if (ref $self) {
+    defined $width or $width = $self->getwidth;
+    defined $height or $height = $self->getheight;
+  }
+  else {
+    unless (defined $width && defined $height) {
+      $self->_set_error("scale_calculate: width and height parameters must be supplied when called as a class method");
+      return;
+    }
   }
 
   if ($opts{'xscalefactor'} && $opts{'yscalefactor'}) {
   }
 
   if ($opts{'xscalefactor'} && $opts{'yscalefactor'}) {
@@ -2015,8 +2070,8 @@ sub scale {
 
   # work out the scaling
   if ($opts{xpixels} and $opts{ypixels} and $opts{'type'}) {
 
   # work out the scaling
   if ($opts{xpixels} and $opts{ypixels} and $opts{'type'}) {
-    my ($xpix, $ypix)=( $opts{xpixels} / $self->getwidth() , 
-                       $opts{ypixels} / $self->getheight() );
+    my ($xpix, $ypix)=( $opts{xpixels} / $width , 
+                       $opts{ypixels} / $height );
     if ($opts{'type'} eq 'min') { 
       $x_scale = $y_scale = _min($xpix,$ypix); 
     }
     if ($opts{'type'} eq 'min') { 
       $x_scale = $y_scale = _min($xpix,$ypix); 
     }
@@ -2029,13 +2084,13 @@ sub scale {
     }
     else {
       $self->_set_error('invalid value for type parameter');
     }
     else {
       $self->_set_error('invalid value for type parameter');
-      return undef;
+      return;
     }
   } elsif ($opts{xpixels}) { 
     }
   } elsif ($opts{xpixels}) { 
-    $x_scale = $y_scale = $opts{xpixels} / $self->getwidth();
+    $x_scale = $y_scale = $opts{xpixels} / $width;
   }
   elsif ($opts{ypixels}) { 
   }
   elsif ($opts{ypixels}) { 
-    $x_scale = $y_scale = $opts{ypixels}/$self->getheight();
+    $x_scale = $y_scale = $opts{ypixels}/$height;
   }
   elsif ($opts{constrain} && ref $opts{constrain}
         && $opts{constrain}->can('constrain')) {
   }
   elsif ($opts{constrain} && ref $opts{constrain}
         && $opts{constrain}->can('constrain')) {
@@ -2046,20 +2101,52 @@ sub scale {
       = $opts{constrain}->constrain($self->getwidth, $self->getheight);
     unless ($scalefactor) {
       $self->_set_error('constrain method failed on constrain parameter');
       = $opts{constrain}->constrain($self->getwidth, $self->getheight);
     unless ($scalefactor) {
       $self->_set_error('constrain method failed on constrain parameter');
-      return undef;
+      return;
     }
     $x_scale = $y_scale = $scalefactor;
   }
 
     }
     $x_scale = $y_scale = $scalefactor;
   }
 
+  my $new_width = int($x_scale * $width + 0.5);
+  $new_width > 0 or $new_width = 1;
+  my $new_height = int($y_scale * $height + 0.5);
+  $new_height > 0 or $new_height = 1;
+
+  return ($x_scale, $y_scale, $new_width, $new_height);
+  
+}
+
+# Scale an image to requested size and return the scaled version
+
+sub scale {
+  my $self=shift;
+  my %opts = (qtype=>'normal' ,@_);
+  my $img = Imager->new();
+  my $tmp = Imager->new();
+
+  unless (defined wantarray) {
+    my @caller = caller;
+    warn "scale() called in void context - scale() returns the scaled image at $caller[1] line $caller[2]\n";
+    return;
+  }
+
+  unless ($self->{IMG}) { 
+    $self->_set_error('empty input image'); 
+    return undef;
+  }
+
+  my ($x_scale, $y_scale, $new_width, $new_height) = 
+    $self->scale_calculate(%opts)
+      or return;
+
   if ($opts{qtype} eq 'normal') {
     $tmp->{IMG} = i_scaleaxis($self->{IMG}, $x_scale, 0);
     if ( !defined($tmp->{IMG}) ) { 
   if ($opts{qtype} eq 'normal') {
     $tmp->{IMG} = i_scaleaxis($self->{IMG}, $x_scale, 0);
     if ( !defined($tmp->{IMG}) ) { 
-      $self->{ERRSTR} = 'unable to scale image';
+      $self->{ERRSTR} = 'unable to scale image: ' . $self->_error_as_msg;
       return undef;
     }
     $img->{IMG}=i_scaleaxis($tmp->{IMG}, $y_scale, 1);
     if ( !defined($img->{IMG}) ) { 
       return undef;
     }
     $img->{IMG}=i_scaleaxis($tmp->{IMG}, $y_scale, 1);
     if ( !defined($img->{IMG}) ) { 
-      $self->{ERRSTR}='unable to scale image'
+      $self->{ERRSTR}='unable to scale image: ' . $self->_error_as_msg
       return undef;
     }
 
       return undef;
     }
 
@@ -2074,13 +2161,9 @@ sub scale {
     return $img;
   }
   elsif ($opts{'qtype'} eq 'mixing') {
     return $img;
   }
   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}) {
     $img->{IMG} = i_scale_mixing($self->{IMG}, $new_width, $new_height);
     unless ($img->{IMG}) {
-      $self->_set_error(Imager->_error_as_meg);
+      $self->_set_error(Imager->_error_as_msg);
       return;
     }
     return $img;
       return;
     }
     return $img;
@@ -2324,7 +2407,7 @@ sub transform2 {
 
 sub rubthrough {
   my $self=shift;
 
 sub rubthrough {
   my $self=shift;
-  my %opts=(tx => 0,ty => 0, @_);
+  my %opts= @_;
 
   unless ($self->{IMG}) { 
     $self->{ERRSTR}='empty input image'; 
 
   unless ($self->{IMG}) { 
     $self->{ERRSTR}='empty input image'; 
@@ -2341,15 +2424,108 @@ sub rubthrough {
           src_maxy => $opts{src}->getheight(),
           %opts);
 
           src_maxy => $opts{src}->getheight(),
           %opts);
 
-  unless (i_rubthru($self->{IMG}, $opts{src}->{IMG}, $opts{tx}, $opts{ty},
+  my $tx = $opts{tx};
+  defined $tx or $tx = $opts{left};
+  defined $tx or $tx = 0;
+
+  my $ty = $opts{ty};
+  defined $ty or $ty = $opts{top};
+  defined $ty or $ty = 0;
+
+  unless (i_rubthru($self->{IMG}, $opts{src}->{IMG}, $tx, $ty,
                     $opts{src_minx}, $opts{src_miny}, 
                     $opts{src_maxx}, $opts{src_maxy})) {
     $self->_set_error($self->_error_as_msg());
     return undef;
   }
                     $opts{src_minx}, $opts{src_miny}, 
                     $opts{src_maxx}, $opts{src_maxy})) {
     $self->_set_error($self->_error_as_msg());
     return undef;
   }
+
   return $self;
 }
 
   return $self;
 }
 
+sub compose {
+  my $self = shift;
+  my %opts =
+    ( 
+     opacity => 1.0,
+     mask_left => 0,
+     mask_top => 0,
+     @_
+    );
+
+  unless ($self->{IMG}) {
+    $self->_set_error("compose: empty input image");
+    return;
+  }
+
+  unless ($opts{src}) {
+    $self->_set_error("compose: src parameter missing");
+    return;
+  }
+  
+  unless ($opts{src}{IMG}) {
+    $self->_set_error("compose: src parameter empty image");
+    return;
+  }
+  my $src = $opts{src};
+
+  my $left = $opts{left};
+  defined $left or $left = $opts{tx};
+  defined $left or $left = 0;
+
+  my $top = $opts{top};
+  defined $top or $top = $opts{ty};
+  defined $top or $top = 0;
+
+  my $src_left = $opts{src_left};
+  defined $src_left or $src_left = $opts{src_minx};
+  defined $src_left or $src_left = 0;
+
+  my $src_top = $opts{src_top};
+  defined $src_top or $src_top = $opts{src_miny};
+  defined $src_top or $src_top = 0;
+
+  my $width = $opts{width};
+  if (!defined $width && defined $opts{src_maxx}) {
+    $width = $opts{src_maxx} - $src_left;
+  }
+  defined $width or $width = $src->getwidth() - $src_left;
+
+  my $height = $opts{height};
+  if (!defined $height && defined $opts{src_maxy}) {
+    $height = $opts{src_maxy} - $src_top;
+  }
+  defined $height or $height = $src->getheight() - $src_top;
+
+  my $combine = $self->_combine($opts{combine}, 'normal');
+
+  if ($opts{mask}) {
+    unless ($opts{mask}{IMG}) {
+      $self->_set_error("compose: mask parameter empty image");
+      return;
+    }
+
+    my $mask_left = $opts{mask_left};
+    defined $mask_left or $mask_left = $opts{mask_minx};
+    defined $mask_left or $mask_left = 0;
+    
+    my $mask_top = $opts{mask_top};
+    defined $mask_top or $mask_top = $opts{mask_miny};
+    defined $mask_top or $mask_top = 0;
+
+    i_compose_mask($self->{IMG}, $src->{IMG}, $opts{mask}{IMG}, 
+                  $left, $top, $src_left, $src_top,
+                  $mask_left, $mask_top, $width, $height, 
+                  $combine, $opts{opacity})
+      or return;
+  }
+  else {
+    i_compose($self->{IMG}, $src->{IMG}, $left, $top, $src_left, $src_top,
+             $width, $height, $combine, $opts{opacity})
+      or return;
+  }
+
+  return $self;
+}
 
 sub flip {
   my $self  = shift;
 
 sub flip {
   my $self  = shift;
@@ -2477,25 +2653,46 @@ sub i_color_set {
 # Draws a box between the specified corner points.
 sub box {
   my $self=shift;
 # Draws a box between the specified corner points.
 sub box {
   my $self=shift;
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
-  my $dflcl=i_color_new(255,255,255,255);
-  my %opts=(color=>$dflcl,xmin=>0,ymin=>0,xmax=>$self->getwidth()-1,ymax=>$self->getheight()-1,@_);
+  my $raw = $self->{IMG};
 
 
+  unless ($raw) {
+    $self->{ERRSTR}='empty input image';
+    return undef;
+  }
+
+  my %opts = @_;
+
+  my ($xmin, $ymin, $xmax, $ymax);
   if (exists $opts{'box'}) { 
   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]);
+    $xmin = _min($opts{'box'}->[0],$opts{'box'}->[2]);
+    $xmax = _max($opts{'box'}->[0],$opts{'box'}->[2]);
+    $ymin = _min($opts{'box'}->[1],$opts{'box'}->[3]);
+    $ymax = _max($opts{'box'}->[1],$opts{'box'}->[3]);
+  }
+  else {
+    defined($xmin = $opts{xmin}) or $xmin = 0;
+    defined($xmax = $opts{xmax}) or $xmax = $self->getwidth()-1;
+    defined($ymin = $opts{ymin}) or $ymin = 0;
+    defined($ymax = $opts{ymax}) or $ymax = $self->getheight()-1;
   }
 
   if ($opts{filled}) { 
   }
 
   if ($opts{filled}) { 
-    my $color = _color($opts{'color'});
-    unless ($color) { 
-      $self->{ERRSTR} = $Imager::ERRSTR; 
-      return; 
+    my $color = $opts{'color'};
+
+    if (defined $color) {
+      unless (_is_color_object($color)) {
+       $color = _color($color);
+       unless ($color) { 
+         $self->{ERRSTR} = $Imager::ERRSTR; 
+         return;
+       }
+      }
+    }
+    else {
+      $color = i_color_new(255,255,255,255);
     }
     }
-    i_box_filled($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},
-                 $opts{ymax}, $color); 
+
+    i_box_filled($raw, $xmin, $ymin,$xmax, $ymax, $color);
   }
   elsif ($opts{fill}) {
     unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
   }
   elsif ($opts{fill}) {
     unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
@@ -2506,30 +2703,47 @@ sub box {
         return undef;
       }
     }
         return undef;
       }
     }
-    i_box_cfill($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},
-                $opts{ymax},$opts{fill}{fill});
+    i_box_cfill($raw, $xmin, $ymin, $xmax, $ymax, $opts{fill}{fill});
   }
   else {
   }
   else {
-    my $color = _color($opts{'color'});
+    my $color = $opts{'color'};
+    if (defined $color) {
+      unless (_is_color_object($color)) {
+       $color = _color($color);
+       unless ($color) { 
+         $self->{ERRSTR} = $Imager::ERRSTR;
+         return;
+       }
+      }
+    }
+    else {
+      $color = i_color_new(255, 255, 255, 255);
+    }
     unless ($color) { 
       $self->{ERRSTR} = $Imager::ERRSTR;
       return;
     }
     unless ($color) { 
       $self->{ERRSTR} = $Imager::ERRSTR;
       return;
     }
-    i_box($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},$opts{ymax},
-          $color);
+    i_box($raw, $xmin, $ymin, $xmax, $ymax, $color);
   }
   }
+
   return $self;
 }
 
 sub arc {
   my $self=shift;
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
   return $self;
 }
 
 sub arc {
   my $self=shift;
   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,
-           'x'=>$self->getwidth()/2,
-           'y'=>$self->getheight()/2,
-           'd1'=>0, 'd2'=>361, @_);
+  my $dflcl= [ 255, 255, 255, 255];
+  my $good = 1;
+  my %opts=
+    (
+     color=>$dflcl,
+     'r'=>_min($self->getwidth(),$self->getheight())/3,
+     'x'=>$self->getwidth()/2,
+     'y'=>$self->getheight()/2,
+     'd1'=>0, 'd2'=>361, 
+     filled => 1,
+     @_,
+    );
   if ($opts{aa}) {
     if ($opts{fill}) {
       unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
   if ($opts{aa}) {
     if ($opts{fill}) {
       unless (UNIVERSAL::isa($opts{fill}, 'Imager::Fill')) {
@@ -2543,7 +2757,7 @@ sub arc {
       i_arc_aa_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
                     $opts{'d2'}, $opts{fill}{fill});
     }
       i_arc_aa_cfill($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},
                     $opts{'d2'}, $opts{fill}{fill});
     }
-    else {
+    elsif ($opts{filled}) {
       my $color = _color($opts{'color'});
       unless ($color) { 
        $self->{ERRSTR} = $Imager::ERRSTR; 
       my $color = _color($opts{'color'});
       unless ($color) { 
        $self->{ERRSTR} = $Imager::ERRSTR; 
@@ -2558,6 +2772,15 @@ sub arc {
                 $opts{'d1'}, $opts{'d2'}, $color); 
       }
     }
                 $opts{'d1'}, $opts{'d2'}, $color); 
       }
     }
+    else {
+      my $color = _color($opts{'color'});
+      if ($opts{d2} - $opts{d1} >= 360) {
+       $good = i_circle_out_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'}, $color);
+      }
+      else {
+       $good = i_arc_out_aa($self->{IMG}, $opts{'x'}, $opts{'y'}, $opts{'r'}, $opts{'d1'}, $opts{'d2'}, $color);
+      }
+    }
   }
   else {
     if ($opts{fill}) {
   }
   else {
     if ($opts{fill}) {
@@ -2576,12 +2799,26 @@ sub arc {
       my $color = _color($opts{'color'});
       unless ($color) { 
        $self->{ERRSTR} = $Imager::ERRSTR; 
       my $color = _color($opts{'color'});
       unless ($color) { 
        $self->{ERRSTR} = $Imager::ERRSTR; 
-       return; 
+       return;
+      }
+      if ($opts{filled}) {
+       i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
+             $opts{'d1'}, $opts{'d2'}, $color); 
+      }
+      else {
+       if ($opts{d1} == 0 && $opts{d2} == 361) {
+         $good = i_circle_out($self->{IMG}, $opts{x}, $opts{y}, $opts{r}, $color);
+       }
+       else {
+         $good = i_arc_out($self->{IMG}, $opts{x}, $opts{y}, $opts{r}, $opts{d1}, $opts{d2}, $color);
+       }
       }
       }
-      i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},
-           $opts{'d1'}, $opts{'d2'}, $color); 
     }
   }
     }
   }
+  unless ($good) {
+    $self->_set_error($self->_error_as_msg);
+    return;
+  }
 
   return $self;
 }
 
   return $self;
 }
@@ -2810,9 +3047,18 @@ sub flood_fill {
 }
 
 sub setpixel {
 }
 
 sub setpixel {
-  my $self = shift;
+  my ($self, %opts) = @_;
+
+  my $color = $opts{color};
+  unless (defined $color) {
+    $color = $self->{fg};
+    defined $color or $color = NC(255, 255, 255);
+  }
 
 
-  my %opts = ( color=>$self->{fg} || NC(255, 255, 255), @_);
+  unless (ref $color && UNIVERSAL::isa($color, "Imager::Color")) {
+    $color = _color($color)
+      or return undef;
+  }
 
   unless (exists $opts{'x'} && exists $opts{'y'}) {
     $self->{ERRSTR} = 'missing x and y parameters';
 
   unless (exists $opts{'x'} && exists $opts{'y'}) {
     $self->{ERRSTR} = 'missing x and y parameters';
@@ -2821,30 +3067,35 @@ sub setpixel {
 
   my $x = $opts{'x'};
   my $y = $opts{'y'};
 
   my $x = $opts{'x'};
   my $y = $opts{'y'};
-  my $color = _color($opts{color})
-    or return undef;
   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;
     }
   }
 
     }
   }
 
@@ -2991,7 +3242,7 @@ sub setscanline {
 
 sub getsamples {
   my $self = shift;
 
 sub getsamples {
   my $self = shift;
-  my %opts = ( type => '8bit', x=>0, @_);
+  my %opts = ( type => '8bit', x=>0, offset => 0, @_);
 
   defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
 
 
   defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
 
@@ -3004,18 +3255,103 @@ sub getsamples {
     $opts{channels} = [ 0 .. $self->getchannels()-1 ];
   }
 
     $opts{channels} = [ 0 .. $self->getchannels()-1 ];
   }
 
-  if ($opts{type} eq '8bit') {
-    return i_gsamp($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
-                  $opts{y}, @{$opts{channels}});
-  }
-  elsif ($opts{type} eq 'float') {
-    return i_gsampf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
-                   $opts{y}, @{$opts{channels}});
+  if ($opts{target}) {
+    my $target = $opts{target};
+    my $offset = $opts{offset};
+    if ($opts{type} eq '8bit') {
+      my @samples = i_gsamp($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
+                           $opts{y}, @{$opts{channels}})
+       or return;
+      @{$target}{$offset .. $offset + @samples - 1} = @samples;
+      return scalar(@samples);
+    }
+    elsif ($opts{type} eq 'float') {
+      my @samples = i_gsampf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
+                            $opts{y}, @{$opts{channels}});
+      @{$target}{$offset .. $offset + @samples - 1} = @samples;
+      return scalar(@samples);
+    }
+    elsif ($opts{type} =~ /^(\d+)bit$/) {
+      my $bits = $1;
+
+      my @data;
+      my $count = i_gsamp_bits($self->{IMG}, $opts{x}, $opts{x}+$opts{width}, 
+                              $opts{y}, $bits, $target, 
+                              $offset, @{$opts{channels}});
+      unless (defined $count) {
+       $self->_set_error(Imager->_error_as_msg);
+       return;
+      }
+
+      return $count;
+    }
+    else {
+      $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
+      return;
+    }
   }
   else {
   }
   else {
-    $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
+    if ($opts{type} eq '8bit') {
+      return i_gsamp($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
+                    $opts{y}, @{$opts{channels}});
+    }
+    elsif ($opts{type} eq 'float') {
+      return i_gsampf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
+                     $opts{y}, @{$opts{channels}});
+    }
+    elsif ($opts{type} =~ /^(\d+)bit$/) {
+      my $bits = $1;
+
+      my @data;
+      i_gsamp_bits($self->{IMG}, $opts{x}, $opts{x}+$opts{width}, 
+                  $opts{y}, $bits, \@data, 0, @{$opts{channels}})
+       or return;
+      return @data;
+    }
+    else {
+      $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
+      return;
+    }
+  }
+}
+
+sub setsamples {
+  my $self = shift;
+  my %opts = ( x => 0, offset => 0, @_ );
+
+  unless ($self->{IMG}) {
+    $self->_set_error('setsamples: empty input image');
     return;
   }
     return;
   }
+
+  unless(defined $opts{data} && ref $opts{data}) {
+    $self->_set_error('setsamples: data parameter missing or invalid');
+    return;
+  }
+
+  unless ($opts{channels}) {
+    $opts{channels} = [ 0 .. $self->getchannels()-1 ];
+  }
+
+  unless ($opts{type} && $opts{type} =~ /^(\d+)bit$/) {
+    $self->_set_error('setsamples: type parameter missing or invalid');
+    return;
+  }
+  my $bits = $1;
+
+  unless (defined $opts{width}) {
+    $opts{width} = $self->getwidth() - $opts{x};
+  }
+
+  my $count = i_psamp_bits($self->{IMG}, $opts{x}, $opts{y}, $bits,
+                          $opts{channels}, $opts{data}, $opts{offset}, 
+                          $opts{width});
+  unless (defined $count) {
+    $self->_set_error(Imager->_error_as_msg);
+    return;
+  }
+
+  return $count;
 }
 
 # make an identity matrix of the given size
 }
 
 # make an identity matrix of the given size
@@ -3125,9 +3461,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;
@@ -3135,6 +3471,46 @@ sub convert {
   return $new;
 }
 
   return $new;
 }
 
+# combine channels from multiple input images, a class method
+sub combine {
+  my ($class, %opts) = @_;
+
+  my $src = delete $opts{src};
+  unless ($src) {
+    $class->_set_error("src parameter missing");
+    return;
+  }
+  my @imgs;
+  my $index = 0;
+  for my $img (@$src) {
+    unless (eval { $img->isa("Imager") }) {
+      $class->_set_error("src must contain image objects");
+      return;
+    }
+    unless ($img->{IMG}) {
+      $class->_set_error("empty input image");
+      return;
+    }
+    push @imgs, $img->{IMG};
+  }
+  my $result;
+  if (my $channels = delete $opts{channels}) {
+    $result = i_combine(\@imgs, $channels);
+  }
+  else {
+    $result = i_combine(\@imgs);
+  }
+  unless ($result) {
+    $class->_set_error($class->_error_as_msg);
+    return;
+  }
+
+  my $img = $class->new;
+  $img->{IMG} = $result;
+
+  return $img;
+}
+
 
 # general function to map an image through lookup tables
 
 
 # general function to map an image through lookup tables
 
@@ -3193,16 +3569,26 @@ sub border {
 
 sub getwidth {
   my $self = shift;
 
 sub getwidth {
   my $self = shift;
-  if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
-  return (i_img_info($self->{IMG}))[0];
+
+  if (my $raw = $self->{IMG}) {
+    return i_img_get_width($raw);
+  }
+  else {
+    $self->{ERRSTR} = 'image is empty'; return undef;
+  }
 }
 
 # Get the height of an image
 
 sub getheight {
   my $self = shift;
 }
 
 # Get the height of an image
 
 sub getheight {
   my $self = shift;
-  if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
-  return (i_img_info($self->{IMG}))[1];
+
+  if (my $raw = $self->{IMG}) {
+    return i_img_get_height($raw);
+  }
+  else {
+    $self->{ERRSTR} = 'image is empty'; return undef;
+  }
 }
 
 # Get number of channels in an image
 }
 
 # Get number of channels in an image
@@ -3249,6 +3635,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 {
@@ -3291,7 +3732,8 @@ sub align_string {
   }
 
   my %input=('x'=>0, 'y'=>0, @_);
   }
 
   my %input=('x'=>0, 'y'=>0, @_);
-  $input{string}||=$input{text};
+  defined $input{string}
+    or $input{string} = $input{text};
 
   unless(exists $input{string}) {
     $self->_set_error("missing required parameter 'string'");
 
   unless(exists $input{string}) {
     $self->_set_error("missing required parameter 'string'");
@@ -3341,6 +3783,10 @@ sub get_file_limits {
 
 sub newcolor { Imager::Color->new(@_); }
 sub newfont  { Imager::Font->new(@_); }
 
 sub newcolor { Imager::Color->new(@_); }
 sub newfont  { Imager::Font->new(@_); }
+sub NCF {
+  require Imager::Color::Float;
+  return Imager::Color::Float->new(@_);
+}
 
 *NC=*newcolour=*newcolor;
 *NF=*newfont;
 
 *NC=*newcolour=*newcolor;
 *NF=*newfont;
@@ -3379,13 +3825,17 @@ 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 lc $ext; # best guess
   return ();
 }
 
   return 'gif'  if ($ext eq "gif");
   return 'raw'  if ($ext eq "raw");
   return lc $ext; # best guess
   return ();
 }
 
+sub combines {
+  return @combine_types;
+}
+
 # get the minimum of a list
 
 sub _min {
 # get the minimum of a list
 
 sub _min {
@@ -3465,6 +3915,110 @@ sub Inline {
   return Imager::ExtUtils->inline_config;
 }
 
   return Imager::ExtUtils->inline_config;
 }
 
+# threads shouldn't try to close raw Imager objects
+sub Imager::ImgRaw::CLONE_SKIP { 1 }
+
+# backward compatibility for %formats
+package Imager::FORMATS;
+use strict;
+use constant IX_FORMATS => 0;
+use constant IX_LIST => 1;
+use constant IX_INDEX => 2;
+use constant IX_CLASSES => 3;
+
+sub TIEHASH {
+  my ($class, $formats, $classes) = @_;
+
+  return bless [ $formats, [ ], 0, $classes ], $class;
+}
+
+sub _check {
+  my ($self, $key) = @_;
+
+  (my $file = $self->[IX_CLASSES]{$key} . ".pm") =~ s(::)(/)g;
+  my $value;
+  if (eval { require $file; 1 }) {
+    $value = 1;
+  }
+  else {
+    $value = undef;
+  }
+  $self->[IX_FORMATS]{$key} = $value;
+
+  return $value;
+}
+
+sub FETCH {
+  my ($self, $key) = @_;
+
+  exists $self->[IX_FORMATS]{$key} and return $self->[IX_FORMATS]{$key};
+
+  $self->[IX_CLASSES]{$key} or return undef;
+
+  return $self->_check($key);
+}
+
+sub STORE {
+  die "%Imager::formats is not user monifiable";
+}
+
+sub DELETE {
+  die "%Imager::formats is not user monifiable";
+}
+
+sub CLEAR {
+  die "%Imager::formats is not user monifiable";
+}
+
+sub EXISTS {
+  my ($self, $key) = @_;
+
+  if (exists $self->[IX_FORMATS]{$key}) {
+    my $value = $self->[IX_FORMATS]{$key}
+      or return;
+    return 1;
+  }
+
+  $self->_check($key) or return 1==0;
+
+  return 1==1;
+}
+
+sub FIRSTKEY {
+  my ($self) = @_;
+
+  unless (@{$self->[IX_LIST]}) {
+    # full populate it
+    @{$self->[IX_LIST]} = grep $self->[IX_FORMATS]{$_},
+      keys %{$self->[IX_FORMATS]};
+
+    for my $key (keys %{$self->[IX_CLASSES]}) {
+      $self->[IX_FORMATS]{$key} and next;
+      $self->_check($key)
+       and push @{$self->[IX_LIST]}, $key;
+    }
+  }
+
+  @{$self->[IX_LIST]} or return;
+  $self->[IX_INDEX] = 1;
+  return $self->[IX_LIST][0];
+}
+
+sub NEXTKEY {
+  my ($self) = @_;
+
+  $self->[IX_INDEX] < @{$self->[IX_LIST]}
+    or return;
+
+  return $self->[IX_LIST][$self->[IX_INDEX]++];
+}
+
+sub SCALAR {
+  my ($self) = @_;
+
+  return scalar @{$self->[IX_LIST]};
+}
+
 1;
 __END__
 # Below is the stub of documentation for your module. You better edit it!
 1;
 __END__
 # Below is the stub of documentation for your module. You better edit it!
@@ -3486,9 +4040,9 @@ Imager - Perl extension for Generating 24 bit Images
 
   my $format;
 
 
   my $format;
 
-  my $img = Imager->new();
   # see Imager::Files for information on the read() method
   # see Imager::Files for information on the read() method
-  $img->read(file=>$file) or die $img->errstr();
+  my $img = Imager->new(file=>$file)
+    or die Imager->errstr();
 
   $file =~ s/\.[^.]*$//;
 
 
   $file =~ s/\.[^.]*$//;
 
@@ -3502,7 +4056,7 @@ Imager - Perl extension for Generating 24 bit Images
   # try to save in one of these formats
   SAVE:
 
   # try to save in one of these formats
   SAVE:
 
-  for $format ( qw( png gif jpg tiff ppm ) ) {
+  for $format ( qw( png gif jpeg tiff ppm ) ) {
     # Check if given format is supported
     if ($Imager::formats{$format}) {
       $file.="_low.$format";
     # Check if given format is supported
     if ($Imager::formats{$format}) {
       $file.="_low.$format";
@@ -3581,7 +4135,7 @@ C<transform()>, C<transform2()> and C<matrix_transform()>.
 =item *
 
 L<Imager::Filters> - Filters, sharpen, blur, noise, convolve etc. and
 =item *
 
 L<Imager::Filters> - Filters, sharpen, blur, noise, convolve etc. and
-filter plugins.
+filter plug-ins.
 
 =item *
 
 
 =item *
 
@@ -3633,11 +4187,12 @@ This example creates a completely black image of width 400 and height
 
 =head1 ERROR HANDLING
 
 
 =head1 ERROR HANDLING
 
-In general a method will return false when it fails, if it does use the errstr() method to find out why:
+In general a method will return false when it fails, if it does use
+the C<errstr()> method to find out why:
 
 =over
 
 
 =over
 
-=item errstr
+=item C<errstr>
 
 Returns the last error message in that context.
 
 
 Returns the last error message in that context.
 
@@ -3665,73 +4220,107 @@ L<Imager::ImageTypes>.
 
 Where to find information on methods for Imager class objects.
 
 
 Where to find information on methods for Imager class objects.
 
-addcolors() -  L<Imager::ImageTypes/addcolors>
+addcolors() - L<Imager::ImageTypes/addcolors> - add colors to a
+paletted image
 
 addtag() -  L<Imager::ImageTypes/addtag> - add image tags
 
 
 addtag() -  L<Imager::ImageTypes/addtag> - add image tags
 
-align_string() - L<Imager::Draw/align_string>
+align_string() - L<Imager::Draw/align_string> - draw text aligned on a
+point
 
 
-arc() - L<Imager::Draw/arc>
+arc() - L<Imager::Draw/arc> - draw a filled 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
 
-box() - L<Imager::Draw/box>
+box() - L<Imager::Draw/box> - draw a filled or outline box.
+
+circle() - L<Imager::Draw/circle> - draw a filled circle
+
+colorcount() - L<Imager::Draw/colorcount> - the number of colors in an
+image's palette (paletted images only)
+
+combine() - L<Imager::Transformations/combine> - combine channels from one or
+more images.
 
 
-circle() - L<Imager::Draw/circle>
+combines() - L<Imager::Draw/combines> - return a list of the different
+combine type keywords
 
 
-colorcount() - L<Imager::Draw/colorcount>
+compose() - L<Imager::Transformations/compose> - compose one image
+over another.
 
 convert() - L<Imager::Transformations/"Color transformations"> -
 transform the color space
 
 
 convert() - L<Imager::Transformations/"Color transformations"> -
 transform the color space
 
-copy() - L<Imager::Transformations/copy>
+copy() - L<Imager::Transformations/copy> - make a duplicate of an
+image
 
 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>
+def_guess_type() - L<Imager::Files/def_guess_type> - default function
+used to guess the output file format based on the output file name
 
 deltag() -  L<Imager::ImageTypes/deltag> - delete image tags
 
 
 deltag() -  L<Imager::ImageTypes/deltag> - delete image tags
 
-difference() - L<Imager::Filters/"Image Difference">
+difference() - L<Imager::Filters/"Image Difference"> - produce a
+difference images from two input images.
 
 
-errstr() - L<"Basic Overview">
+errstr() - L<"Basic Overview"> - the error from the last failed
+operation.
 
 
-filter() - L<Imager::Filters>
+filter() - L<Imager::Filters> - image filtering
 
 
-findcolor() - L<Imager::ImageTypes/findcolor> - search the image palette, if it
-has one
+findcolor() - L<Imager::ImageTypes/findcolor> - search the image
+palette, if it has one
 
 
-flip() - L<Imager::Transformations/flip>
+flip() - L<Imager::Transformations/flip> - flip an image, vertically,
+horizontally
 
 
-flood_fill() - L<Imager::Draw/flood_fill>
+flood_fill() - L<Imager::Draw/flood_fill> - fill an enclosed or same
+color area
 
 
-getchannels() -  L<Imager::ImageTypes/getchannels>
+getchannels() - L<Imager::ImageTypes/getchannels> - the number of
+samples per pixel for an image
 
 
-getcolorcount() -  L<Imager::ImageTypes/getcolorcount>
+getcolorcount() - L<Imager::ImageTypes/getcolorcount> - the number of
+different colors used by an image (works for direct color images)
 
 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">
 
 get_file_limits() - L<Imager::Files/"Limiting the sizes of images you read">
 
-getheight() - L<Imager::ImageTypes/getwidth>
+getheight() - L<Imager::ImageTypes/getwidth> - height of the image in
+pixels
 
 
-getmask() - L<Imager::ImageTypes/getmask>
+getmask() - L<Imager::ImageTypes/getmask> - write mask for the image
 
 
-getpixel() - L<Imager::Draw/getpixel>
+getpixel() - L<Imager::Draw/getpixel> - retrieve one or more pixel
+colors
 
 
-getsamples() - L<Imager::Draw/getsamples>
+getsamples() - L<Imager::Draw/getsamples> - retrieve samples from a
+row or partial row of pixels.
 
 
-getscanline() - L<Imager::Draw/getscanline>
+getscanline() - L<Imager::Draw/getscanline> - retrieve colors for a
+row or partial row of pixels.
 
 
-getwidth() - L<Imager::ImageTypes/getwidth>
+getwidth() - L<Imager::ImageTypes/getwidth> - width of the image in
+pixels.
 
 
-img_set() - L<Imager::ImageTypes/img_set>
+img_set() - L<Imager::ImageTypes/img_set> - re-use an Imager object
+for a new image.
 
 init() - L<Imager::ImageTypes/init>
 
 
 init() - L<Imager::ImageTypes/init>
 
-line() - L<Imager::Draw/line>
+is_bilevel() - L<Imager::ImageTypes/is_bilevel> - returns whether
+image write functions should write the image in their bilevel (blank
+and white, no gray levels) format
+
+line() - L<Imager::Draw/line> - draw an interval
 
 load_plugin() - L<Imager::Filters/load_plugin>
 
 
 load_plugin() - L<Imager::Filters/load_plugin>
 
@@ -3746,6 +4335,8 @@ maxcolors() - L<Imager::ImageTypes/maxcolors>
 
 NC() - L<Imager::Handy/NC>
 
 
 NC() - L<Imager::Handy/NC>
 
+NCF() - L<Imager::Handy/NCF>
+
 new() - L<Imager::ImageTypes/new>
 
 newcolor() - L<Imager::Handy/newcolor>
 new() - L<Imager::ImageTypes/new>
 
 newcolor() - L<Imager::Handy/newcolor>
@@ -3758,6 +4349,8 @@ NF() - L<Imager::Handy/NF>
 
 open() - L<Imager::Files> - an alias for read()
 
 
 open() - L<Imager::Files> - an alias for read()
 
+=for stopwords IPTC
+
 parseiptc() - L<Imager::Files/parseiptc> - parse IPTC data from a JPEG
 image
 
 parseiptc() - L<Imager::Files/parseiptc> - parse IPTC data from a JPEG
 image
 
@@ -3772,11 +4365,14 @@ 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
 
+read_types() - L<Imager::Files/read_types> - list image types Imager
+can read.
+
 register_filter() - L<Imager::Filters/register_filter>
 
 register_filter() - L<Imager::Filters/register_filter>
 
-register_reader() - L<Imager::Filters/register_reader>
+register_reader() - L<Imager::Files/register_reader>
 
 
-register_writer() - L<Imager::Filters/register_writer>
+register_writer() - L<Imager::Files/register_writer>
 
 rotate() - L<Imager::Transformations/rotate>
 
 
 rotate() - L<Imager::Transformations/rotate>
 
@@ -3785,6 +4381,8 @@ image and use the alpha channel
 
 scale() - L<Imager::Transformations/scale>
 
 
 scale() - L<Imager::Transformations/scale>
 
+scale_calculate() - L<Imager::Transformations/scale_calculate>
+
 scaleX() - L<Imager::Transformations/scaleX>
 
 scaleY() - L<Imager::Transformations/scaleY>
 scaleX() - L<Imager::Transformations/scaleX>
 
 scaleY() - L<Imager::Transformations/scaleY>
@@ -3798,6 +4396,8 @@ setmask() - L<Imager::ImageTypes/setmask>
 
 setpixel() - L<Imager::Draw/setpixel>
 
 
 setpixel() - L<Imager::Draw/setpixel>
 
+setsamples() - L<Imager::Draw/setsamples>
+
 setscanline() - L<Imager::Draw/setscanline>
 
 settag() - L<Imager::ImageTypes/settag>
 setscanline() - L<Imager::Draw/setscanline>
 
 settag() - L<Imager::ImageTypes/settag>
@@ -3808,6 +4408,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">
@@ -3826,9 +4428,12 @@ write() - L<Imager::Files> - write an image to a file
 write_multi() - L<Imager::Files> - write multiple image to an image
 file.
 
 write_multi() - L<Imager::Files> - write multiple image to an image
 file.
 
+write_types() - L<Imager::Files/read_types> - list image types Imager
+can write.
+
 =head1 CONCEPT INDEX
 
 =head1 CONCEPT INDEX
 
-animated GIF - L<Imager::File/"Writing an animated GIF">
+animated GIF - L<Imager::Files/"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>
@@ -3840,23 +4445,27 @@ blur - L<Imager::Filters/guassian>, L<Imager::Filters/conv>
 
 boxes, drawing - L<Imager::Draw/box>
 
 
 boxes, drawing - L<Imager::Draw/box>
 
-changes between image - L<Imager::Filter/"Image Difference">
+changes between image - L<Imager::Filters/"Image Difference">
+
+channels, combine into one image - L<Imager::Transformations/combine>
 
 color - L<Imager::Color>
 
 color names - L<Imager::Color>, L<Imager::Color::Table>
 
 
 color - L<Imager::Color>
 
 color names - L<Imager::Color>, L<Imager::Color::Table>
 
-combine modes - L<Imager::Fill/combine>
+combine modes - L<Imager::Draw/"Combine Types">
 
 
-compare images - L<Imager::Filter/"Image Difference">
+compare images - L<Imager::Filters/"Image Difference">
 
 
-contrast - L<Imager::Filter/contrast>, L<Imager::Filter/autolevels>
+contrast - L<Imager::Filters/contrast>, L<Imager::Filters/autolevels>
 
 
-convolution - L<Imager::Filter/conv>
+convolution - L<Imager::Filters/conv>
 
 cropping - L<Imager::Transformations/crop>
 
 
 cropping - L<Imager::Transformations/crop>
 
-C<diff> images - L<Imager::Filter/"Image Difference">
+CUR files - L<Imager::Files/"ICO (Microsoft Windows Icon) and CUR (Microsoft Windows Cursor)">
+
+C<diff> images - L<Imager::Filters/"Image Difference">
 
 dpi - L<Imager::ImageTypes/i_xres>, 
 L<Imager::Cookbook/"Image spatial resolution">
 
 dpi - L<Imager::ImageTypes/i_xres>, 
 L<Imager::Cookbook/"Image spatial resolution">
@@ -3867,7 +4476,7 @@ drawing lines - L<Imager::Draw/line>
 
 drawing text - L<Imager::Draw/string>, L<Imager::Draw/align_string>
 
 
 drawing text - L<Imager::Draw/string>, L<Imager::Draw/align_string>
 
-error message - L<"Basic Overview">
+error message - L<"ERROR HANDLING">
 
 files, font - L<Imager::Font>
 
 
 files, font - L<Imager::Font>
 
@@ -3902,11 +4511,16 @@ 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>
 
-guassian blur - L<Imager::Filter/guassian>
+gray scale, convert image to - L<Imager::Transformations/convert>
+
+guassian blur - L<Imager::Filters/guassian>
 
 hatch fills - L<Imager::Fill/"Hatched fills">
 
 
 hatch fills - L<Imager::Fill/"Hatched fills">
 
-invert image - L<Imager::Filter/hardinvert>
+ICO files - L<Imager::Files/"ICO (Microsoft Windows Icon) and CUR (Microsoft Windows Cursor)">
+
+invert image - L<Imager::Filters/hardinvert>,
+L<Imager::Filters/hardinvertall>
 
 JPEG - L<Imager::Files/"JPEG">
 
 
 JPEG - L<Imager::Files/"JPEG">
 
@@ -3920,12 +4534,12 @@ L<Imager::Font/transform>
 
 metadata, image - L<Imager::ImageTypes/"Tags">
 
 
 metadata, image - L<Imager::ImageTypes/"Tags">
 
-mosaic - L<Imager::Filter/mosaic>
+mosaic - L<Imager::Filters/mosaic>
 
 
-noise, filter - L<Imager::Filter/noise>
+noise, filter - L<Imager::Filters/noise>
 
 
-noise, rendered - L<Imager::Filter/turbnoise>,
-L<Imager::Filter/radnoise>
+noise, rendered - L<Imager::Filters/turbnoise>,
+L<Imager::Filters/radnoise>
 
 paste - L<Imager::Transformations/paste>,
 L<Imager::Transformations/rubthrough>
 
 paste - L<Imager::Transformations/paste>,
 L<Imager::Transformations/rubthrough>
@@ -3933,21 +4547,27 @@ 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>
 
-posterize - L<Imager::Filter/postlevels>
+=for stopwords posterize
 
 
-png files - L<Imager::Files>, L<Imager::Files/"PNG">
+posterize - L<Imager::Filters/postlevels>
 
 
-pnm - L<Imager::Files/"PNM (Portable aNy Map)">
+PNG files - L<Imager::Files>, L<Imager::Files/"PNG">
+
+PNM - L<Imager::Files/"PNM (Portable aNy Map)">
 
 rectangles, drawing - L<Imager::Draw/box>
 
 resizing an image - L<Imager::Transformations/scale>, 
 L<Imager::Transformations/crop>
 
 
 rectangles, drawing - L<Imager::Draw/box>
 
 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>,
@@ -3964,14 +4584,28 @@ text, wrapping text in an area - L<Imager::Font::Wrap>
 
 text, measuring - L<Imager::Font/bounding_box>, L<Imager::Font::BBox>
 
 
 text, measuring - L<Imager::Font/bounding_box>, L<Imager::Font::BBox>
 
-tiles, color - L<Imager::Filter/mosaic>
+tiles, color - L<Imager::Filters/mosaic>
 
 
-unsharp mask - L<Imager::Filter/unsharpmask>
+transparent images - L<Imager::ImageTypes>,
+L<Imager::Cookbook/"Transparent PNG">
 
 
-watermark - L<Imager::Filter/watermark>
+=for stopwords unsharp
+
+unsharp mask - L<Imager::Filters/unsharpmask>
+
+watermark - L<Imager::Filters/watermark>
 
 writing an image to a file - L<Imager::Files>
 
 
 writing an image to a file - L<Imager::Files>
 
+=head1 THREADS
+
+Imager doesn't support perl threads.
+
+Imager has limited code to prevent double frees if you create images,
+colors etc, and then create a thread, but has no code to prevent two
+threads entering Imager's error handling code, and none is likely to
+be added.
+
 =head1 SUPPORT
 
 The best place to get help with Imager is the mailing list.
 =head1 SUPPORT
 
 The best place to get help with Imager is the mailing list.
@@ -3998,18 +4632,73 @@ L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Imager>
 
 =back
 
 
 =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:
+
+  http://www.cpanforum.com/dist/Imager
+
+You will need to register to post.
+
+=head1 CONTRIBUTING TO IMAGER
+
+=head2 Feedback
 
 
-Bugs are listed individually for relevant pod pages.
+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
+
+=for stopwords Bitcard
+
+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 Request
+Tracker.
+
+=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 C<README> for a
+complete list.
+
+=head1 LICENSE
+
+Imager is licensed under the same terms as perl itself.
+
+=for stopwords
+makeblendedfont Fontforge
+
+A test font, FT2/fontfiles/MMOne.pfb, contains a Postscript operator
+definition copyrighted by Adobe.  See F<adobe.txt> in the source for
+license information.
 
 =head1 SEE ALSO
 
 
 =head1 SEE ALSO