]> git.imager.perl.org - imager.git/blobdiff - Imager.pm
[rt.cpan.org #29938] add matrix() method to Imager::Matrix2d
[imager.git] / Imager.pm
index b7d475cc4a375abbdbd8e981030945171e050ac5..869dabc4fc153bc3915161bccae7c76a2afa265b 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -1,7 +1,7 @@
 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;
@@ -74,24 +74,6 @@ use Imager::Font;
                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
 
@@ -173,7 +155,7 @@ my %defaults;
 BEGIN {
   require Exporter;
   @ISA = qw(Exporter);
-  $VERSION = '0.72';
+  $VERSION = '0.80';
   eval {
     require XSLoader;
     XSLoader::load(Imager => $VERSION);
@@ -185,14 +167,21 @@ BEGIN {
   }
 }
 
-BEGIN {
-  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'} and !$formats{'tt'} 
-      && !$formats{'ft2'} && !$formats{'w32'}) {
-    $fontstate='no font support';
-  }
+tie %formats, "Imager::FORMATS", \%formats_low, \%format_classes;
+
+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]);
 
@@ -225,6 +214,13 @@ BEGIN {
                         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 },
@@ -644,7 +640,8 @@ sub new {
         defined $hsh{fh} ||
         defined $hsh{fd} ||
         defined $hsh{callback} ||
-        defined $hsh{readcb}) {
+        defined $hsh{readcb} ||
+        defined $hsh{data}) {
     # allow $img = Imager->new(file => $filename)
     my %extras;
     
@@ -916,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});
+  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 ];
 
-  $result;
+  return $result;
 }
 
 # convert an RGB image into a paletted image
@@ -1359,36 +1361,15 @@ sub read {
     return $readers{$input{type}}{single}->($self, $IO, %input);
   }
 
-  unless ($formats{$input{'type'}}) {
+  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;
   }
 
-  # 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;
 
-  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}) ) {
@@ -1399,15 +1380,6 @@ sub read {
     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}) ) {
@@ -1564,7 +1536,7 @@ sub write_types {
 sub _reader_autoload {
   my $type = shift;
 
-  return if $formats{$type} || $readers{$type};
+  return if $formats_low{$type} || $readers{$type};
 
   return unless $type =~ /^\w+$/;
 
@@ -1592,7 +1564,7 @@ sub _reader_autoload {
 sub _writer_autoload {
   my $type = shift;
 
-  return if $formats{$type} || $readers{$type};
+  return if $formats_low{$type} || $readers{$type};
 
   return unless $type =~ /^\w+$/;
 
@@ -1751,7 +1723,7 @@ sub write {
       or return undef;
   }
   else {
-    if (!$formats{$input{'type'}}) { 
+    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;
@@ -1760,24 +1732,7 @@ sub write {
     ($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) ) {
@@ -1793,14 +1748,6 @@ sub write {
         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;
@@ -1901,37 +1848,7 @@ sub write_multi {
     ($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) {
@@ -1985,30 +1902,15 @@ sub read_multi {
     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;
@@ -2016,9 +1918,16 @@ sub read_multi {
       return ( $img );
     }
     Imager->_set_error($img->errstr);
+    return;
   }
 
+  if (!@imgs) {
+    $ERRSTR = _error_as_msg();
   return;
+  }
+  return map { 
+        bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager' 
+      } @imgs;
 }
 
 # Destroy an Imager object
@@ -2744,25 +2653,46 @@ sub i_color_set {
 # 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'}) { 
-    $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}) { 
-    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')) {
@@ -2773,30 +2703,47 @@ sub box {
         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 {
-    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;
     }
-    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; }
-  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')) {
@@ -2810,7 +2757,7 @@ sub arc {
       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; 
@@ -2825,6 +2772,15 @@ sub arc {
                 $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}) {
@@ -2843,12 +2799,26 @@ sub arc {
       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;
 }
@@ -3077,9 +3047,18 @@ sub flood_fill {
 }
 
 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';
@@ -3088,8 +3067,6 @@ sub setpixel {
 
   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';
@@ -3494,6 +3471,46 @@ sub convert {
   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
 
@@ -3552,16 +3569,26 @@ sub border {
 
 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;
-  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
@@ -3705,7 +3732,8 @@ sub align_string {
   }
 
   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'");
@@ -3755,7 +3783,10 @@ sub get_file_limits {
 
 sub newcolor { Imager::Color->new(@_); }
 sub newfont  { Imager::Font->new(@_); }
-sub NCF { Imager::Color::Float->new(@_) }
+sub NCF {
+  require Imager::Color::Float;
+  return Imager::Color::Float->new(@_);
+}
 
 *NC=*newcolour=*newcolor;
 *NF=*newfont;
@@ -3887,6 +3918,107 @@ sub Inline {
 # 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!
@@ -3909,7 +4041,7 @@ Imager - Perl extension for Generating 24 bit Images
   my $format;
 
   # see Imager::Files for information on the read() method
-  my $im = Imager->new(file=>$file)
+  my $img = Imager->new(file=>$file)
     or die Imager->errstr();
 
   $file =~ s/\.[^.]*$//;
@@ -4003,7 +4135,7 @@ C<transform()>, C<transform2()> and C<matrix_transform()>.
 =item *
 
 L<Imager::Filters> - Filters, sharpen, blur, noise, convolve etc. and
-filter plugins.
+filter plug-ins.
 
 =item *
 
@@ -4056,11 +4188,11 @@ This example creates a completely black image of width 400 and height
 =head1 ERROR HANDLING
 
 In general a method will return false when it fails, if it does use
-the errstr() method to find out why:
+the C<errstr()> method to find out why:
 
 =over
 
-=item errstr
+=item C<errstr>
 
 Returns the last error message in that context.
 
@@ -4108,6 +4240,9 @@ 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.
+
 combines() - L<Imager::Draw/combines> - return a list of the different
 combine type keywords
 
@@ -4123,7 +4258,7 @@ image
 crop() - L<Imager::Transformations/crop> - extract part of an image
 
 def_guess_type() - L<Imager::Files/def_guess_type> - default function
-used to guess the output file format based on the output filename
+used to guess the output file format based on the output file name
 
 deltag() -  L<Imager::ImageTypes/deltag> - delete image tags
 
@@ -4168,10 +4303,10 @@ getpixel() - L<Imager::Draw/getpixel> - retrieve one or more pixel
 colors
 
 getsamples() - L<Imager::Draw/getsamples> - retrieve samples from a
-row or subrow of pixels.
+row or partial row of pixels.
 
 getscanline() - L<Imager::Draw/getscanline> - retrieve colors for a
-row or subrow of pixels.
+row or partial row of pixels.
 
 getwidth() - L<Imager::ImageTypes/getwidth> - width of the image in
 pixels.
@@ -4182,8 +4317,8 @@ for a new image.
 init() - L<Imager::ImageTypes/init>
 
 is_bilevel() - L<Imager::ImageTypes/is_bilevel> - returns whether
-image write functions should write the image in their bi-level (blank
-and white, no grey levels) format
+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
 
@@ -4214,6 +4349,8 @@ NF() - L<Imager::Handy/NF>
 
 open() - L<Imager::Files> - an alias for read()
 
+=for stopwords IPTC
+
 parseiptc() - L<Imager::Files/parseiptc> - parse IPTC data from a JPEG
 image
 
@@ -4310,6 +4447,8 @@ boxes, drawing - L<Imager::Draw/box>
 
 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>
@@ -4372,7 +4511,7 @@ gradient fill - L<Imager::Fill/"Fountain fills">,
 L<Imager::Filters/fountain>, L<Imager::Fountain>,
 L<Imager::Filters/gradgen>
 
-grayscale, convert image to - L<Imager::Transformations/convert>
+gray scale, convert image to - L<Imager::Transformations/convert>
 
 guassian blur - L<Imager::Filters/guassian>
 
@@ -4380,7 +4519,8 @@ hatch fills - L<Imager::Fill/"Hatched fills">
 
 ICO files - L<Imager::Files/"ICO (Microsoft Windows Icon) and CUR (Microsoft Windows Cursor)">
 
-invert image - L<Imager::Filters/hardinvert>
+invert image - L<Imager::Filters/hardinvert>,
+L<Imager::Filters/hardinvertall>
 
 JPEG - L<Imager::Files/"JPEG">
 
@@ -4407,11 +4547,13 @@ L<Imager::Transformations/rubthrough>
 pseudo-color image - L<Imager::ImageTypes/to_paletted>,
 L<Imager::ImageTypes/new>
 
+=for stopwords posterize
+
 posterize - L<Imager::Filters/postlevels>
 
-png files - L<Imager::Files>, L<Imager::Files/"PNG">
+PNG files - L<Imager::Files>, L<Imager::Files/"PNG">
 
-pnm - L<Imager::Files/"PNM (Portable aNy Map)">
+PNM - L<Imager::Files/"PNM (Portable aNy Map)">
 
 rectangles, drawing - L<Imager::Draw/box>
 
@@ -4444,6 +4586,11 @@ text, measuring - L<Imager::Font/bounding_box>, L<Imager::Font::BBox>
 
 tiles, color - L<Imager::Filters/mosaic>
 
+transparent images - L<Imager::ImageTypes>,
+L<Imager::Cookbook/"Transparent PNG">
+
+=for stopwords unsharp
+
 unsharp mask - L<Imager::Filters/unsharpmask>
 
 watermark - L<Imager::Filters/watermark>
@@ -4515,11 +4662,14 @@ at CPAN Ratings:
 
   http://cpanratings.perl.org/dist/Imager
 
-This requires a Bitcard Account (http://www.bitcard.org).
+=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 RT.
+If you send me a bug report via email, it will be copied to Request
+Tracker.
 
 =head2 Patches
 
@@ -4536,9 +4686,20 @@ Tony Cook <tony@imager.perl.org> is the current maintainer for Imager.
 
 Arnar M. Hrafnkelsson is the original author of Imager.
 
-Many others have contributed to Imager, please see the README for a
+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
 
 L<perl>(1), L<Imager::ImageTypes>(3), L<Imager::Files>(3),