Skip to content
Browse files

enhanced iolayer

multi image/file tiff support
  • Loading branch information...
1 parent 392a350 commit 10461f9a4b1fd147a584973ecc92eeafc941226f Tony Cook committed
Showing with 2,159 additions and 454 deletions.
  1. +12 −2 Changes
  2. +301 −152 Imager.pm
  3. +539 −4 Imager.xs
  4. +4 −3 TODO
  5. +9 −0 bmp.c
  6. +169 −5 gif.c
  7. +7 −1 image.h
  8. +116 −51 iolayer.c
  9. +10 −4 iolayer.h
  10. +2 −0 jpeg.c
  11. +1 −1 lib/Imager/Font.pm
  12. +2 −0 png.c
  13. +1 −0 pnm.c
  14. +3 −0 raw.c
  15. +73 −7 t/t07iolayer.t
  16. +3 −0 t/t105gif.t
  17. +127 −2 t/t106tiff.t
  18. +2 −0 t/t35ttfont.t
  19. +345 −20 t/t50basicoo.t
  20. +3 −0 tga.c
  21. +430 −202 tiff.c
View
14 Changes
@@ -566,8 +566,18 @@ Revision history for Perl extension Imager.
- applied T1 afm patch from Claes Jacobsson
- split IM_INCPATH and IM_LIBPATH with $Config{path_sep}, so they
work on Windows
- - Added memory pools for easy cleanup of temp buffers
- - Added read support for sgi .rgb files.
+ - Added memory pools for easy cleanup of temp buffers
+ - Added read support for sgi .rgb files.
+ - io_new_fd() now creates a FDSEEK io object
+ - implemented i_readgif_wiol()
+ - Imager->read() now uses i_readgif_wiol();
+ - extend callback iolayers at C and Perl levels
+ - implemented i_writegif_wiol()
+ - split out Perl iolayer initialization into private methods
+ - add tests for each type of iolayer in t50basicoo.t
+ - read/write multi-image tiff files
+ - tests in t50basicoo.t for multi-image/file
+
=================================================================
For latest versions check the Imager-devel pages:
View
453 Imager.pm
@@ -85,6 +85,7 @@ use Imager::Font;
i_writepng_wiol
i_readgif
+ i_readgif_wiol
i_readgif_callback
i_writegif
i_writegifmc
@@ -853,12 +854,108 @@ sub deltag {
}
}
+my @needseekcb = qw/tiff/;
+my %needseekcb = map { $_, $_ } @needseekcb;
+
+
+sub _get_reader_io {
+ my ($self, $input, $type) = @_;
+
+ if ($input->{fd}) {
+ return io_new_fd($input->{fd});
+ }
+ elsif ($input->{fh}) {
+ my $fd = fileno($input->{fh});
+ unless ($fd) {
+ $self->_set_error("Handle in fh option not opened");
+ return;
+ }
+ return io_new_fd($fd);
+ }
+ elsif ($input->{file}) {
+ my $file = IO::File->new($input->{file}, "r");
+ unless ($file) {
+ $self->_set_error("Could not open $input->{file}: $!");
+ return;
+ }
+ binmode $file;
+ return (io_new_fd(fileno($file)), $file);
+ }
+ elsif ($input->{data}) {
+ return io_new_buffer($input->{data});
+ }
+ elsif ($input->{callback} || $input->{readcb}) {
+ if ($needseekcb{$type} && !$input->{seekcb}) {
+ $self->_set_error("Format $type needs a seekcb parameter");
+ }
+ if ($input->{maxbuffer}) {
+ return io_new_cb($input->{writecb},
+ $input->{callback} || $input->{readcb},
+ $input->{seekcb}, $input->{closecb},
+ $input->{maxbuffer});
+ }
+ else {
+ return io_new_cb($input->{writecb},
+ $input->{callback} || $input->{readcb},
+ $input->{seekcb}, $input->{closecb});
+ }
+ }
+ else {
+ $self->_set_error("file/fd/fh/data/callback parameter missing");
+ return;
+ }
+}
+
+sub _get_writer_io {
+ my ($self, $input, $type) = @_;
+
+ if ($input->{fd}) {
+ return io_new_fd($input->{fd});
+ }
+ elsif ($input->{fh}) {
+ my $fd = fileno($input->{fh});
+ unless ($fd) {
+ $self->_set_error("Handle in fh option not opened");
+ return;
+ }
+ return io_new_fd($fd);
+ }
+ elsif ($input->{file}) {
+ my $fh = new IO::File($input->{file},"w+");
+ unless ($fh) {
+ $self->_set_error("Could not open file $input->{file}: $!");
+ return;
+ }
+ binmode($fh) or die;
+ return (io_new_fd(fileno($fh)), $fh);
+ }
+ elsif ($input->{data}) {
+ return io_new_bufchain();
+ }
+ elsif ($input->{callback} || $input->{writecb}) {
+ if ($input->{maxbuffer}) {
+ return io_new_cb($input->{callback} || $input->{writecb},
+ $input->{readcb},
+ $input->{seekcb}, $input->{closecb},
+ $input->{maxbuffer});
+ }
+ else {
+ return io_new_cb($input->{callback} || $input->{writecb},
+ $input->{readcb},
+ $input->{seekcb}, $input->{closecb});
+ }
+ }
+ else {
+ $self->_set_error("file/fd/fh/data/callback parameter missing");
+ return;
+ }
+}
+
# Read an image from file
sub read {
my $self = shift;
my %input=@_;
- my ($fh, $fd, $IO);
if (defined($self->{IMG})) {
# let IIM_DESTROY do the destruction, since the image may be
@@ -867,21 +964,6 @@ sub read {
undef($self->{IMG});
}
- if (!$input{fd} and !$input{file} and !$input{data}) {
- $self->{ERRSTR}='no file, fd or data parameter'; return undef;
- }
- if ($input{file}) {
- $fh = new IO::File($input{file},"r");
- if (!defined $fh) {
- $self->{ERRSTR}='Could not open file'; return undef;
- }
- binmode($fh);
- $fd = $fh->fileno();
- }
- if ($input{fd}) {
- $fd=$input{fd};
- }
-
# FIXME: Find the format here if not specified
# yes the code isn't here yet - next week maybe?
# Next week? Are you high or something? That comment
@@ -891,15 +973,20 @@ sub read {
if (!$input{'type'} and $input{file}) {
$input{'type'}=$FORMATGUESS->($input{file});
}
+ unless ($input{'type'}) {
+ $self->_set_error('type parameter missing and not possible to guess from extension');
+ return undef;
+ }
if (!$formats{$input{'type'}}) {
$self->{ERRSTR}='format not supported'; return undef;
}
- my %iolready=(jpeg=>1, png=>1, tiff=>1, pnm=>1, raw=>1, bmp=>1, tga=>1, rgb=>1);
+ my %iolready=(jpeg=>1, png=>1, tiff=>1, pnm=>1, raw=>1, bmp=>1, tga=>1, rgb=>1, gif=>1);
if ($iolready{$input{'type'}}) {
# Setup data source
- $IO = defined $fd ? io_new_fd($fd) : io_new_buffer($input{data});
+ my ($IO, $fh) = $self->_get_reader_io(\%input, $input{'type'})
+ or return;
if ( $input{'type'} eq 'jpeg' ) {
($self->{IMG},$self->{IPTCRAW})=i_readjpeg_wiol( $IO );
@@ -946,6 +1033,29 @@ sub read {
$self->{DEBUG} && print "loading a bmp file\n";
}
+ if ( $input{'type'} eq 'gif' ) {
+ if ($input{colors} && !ref($input{colors})) {
+ # must be a reference to a scalar that accepts the colour map
+ $self->{ERRSTR} = "option 'colors' must be a scalar reference";
+ return undef;
+ }
+ if ($input{colors}) {
+ my $colors;
+ ($self->{IMG}, $colors) =i_readgif_wiol( $IO );
+ if ($colors) {
+ ${ $input{colors} } = [ map { NC(@$_) } @$colors ];
+ }
+ }
+ else {
+ $self->{IMG} =i_readgif_wiol( $IO );
+ }
+ if ( !defined($self->{IMG}) ) {
+ $self->{ERRSTR}=$self->_error_as_msg();
+ return undef;
+ }
+ $self->{DEBUG} && print "loading a gif file\n";
+ }
+
if ( $input{'type'} eq 'tga' ) {
$self->{IMG}=i_readtga_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
if ( !defined($self->{IMG}) ) {
@@ -1003,6 +1113,7 @@ sub read {
return undef;
}
+ my ($fh, $fd);
if ($input{file}) {
$fh = new IO::File($input{file},"r");
if (!defined $fh) {
@@ -1062,13 +1173,13 @@ sub write {
compress=>1,
wierdpack=>0,
fax_fine=>1, @_);
- my ($fh, $rc, $fd, $IO);
+ my $rc;
- my %iolready=( tiff=>1, raw=>1, png=>1, pnm=>1, bmp=>1, jpeg=>1, tga=>1 ); # this will be SO MUCH BETTER once they are all in there
+ my %iolready=( tiff=>1, raw=>1, png=>1, pnm=>1, bmp=>1, jpeg=>1, tga=>1,
+ gif=>1 ); # this will be SO MUCH BETTER once they are all in there
unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
- if (!$input{file} and !$input{'fd'} and !$input{'data'}) { $self->{ERRSTR}='file/fd/data parameter missing'; return undef; }
if (!$input{'type'} and $input{file}) {
$input{'type'}=$FORMATGUESS->($input{file});
}
@@ -1079,21 +1190,11 @@ sub write {
if (!$formats{$input{'type'}}) { $self->{ERRSTR}='format not supported'; return undef; }
- if (exists $input{'fd'}) {
- $fd=$input{'fd'};
- } elsif (exists $input{'data'}) {
- $IO = Imager::io_new_bufchain();
- } else {
- $fh = new IO::File($input{file},"w+");
- if (!defined $fh) { $self->{ERRSTR}='Could not open file'; return undef; }
- binmode($fh) or die;
- $fd = $fh->fileno();
- }
+ my ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
+ or return undef;
+ # this conditional is probably obsolete
if ($iolready{$input{'type'}}) {
- if (defined $fd) {
- $IO = io_new_fd($fd);
- }
if ($input{'type'} eq 'tiff') {
if (defined $input{class} && $input{class} eq 'fax') {
@@ -1144,6 +1245,19 @@ sub write {
return undef;
}
$self->{DEBUG} && print "writing a tga file\n";
+ } elsif ( $input{'type'} eq 'gif' ) {
+ # compatibility with the old interfaces
+ if ($input{gifquant} eq 'lm') {
+ $input{make_colors} = 'addi';
+ $input{translate} = 'perturb';
+ $input{perturb} = $input{lmdither};
+ } elsif ($input{gifquant} eq 'gen') {
+ # just pass options through
+ } else {
+ $input{make_colors} = 'webmap'; # ignored
+ $input{translate} = 'giflib';
+ }
+ $rc = i_writegif_wiol($IO, \%input, $self->{IMG});
}
if (exists $input{'data'}) {
@@ -1155,98 +1269,51 @@ sub write {
${$input{data}} = $data;
}
return $self;
- } else {
- if ( $input{'type'} eq 'gif' ) {
- if (not $input{gifplanes}) {
- my $gp;
- my $count=i_count_colors($self->{IMG}, 256);
- $gp=8 if $count == -1;
- $gp=1 if not $gp and $count <= 2;
- $gp=2 if not $gp and $count <= 4;
- $gp=3 if not $gp and $count <= 8;
- $gp=4 if not $gp and $count <= 16;
- $gp=5 if not $gp and $count <= 32;
- $gp=6 if not $gp and $count <= 64;
- $gp=7 if not $gp and $count <= 128;
- $input{gifplanes} = $gp || 8;
- }
-
- if ($input{gifplanes}>8) {
- $input{gifplanes}=8;
- }
- if ($input{gifquant} eq 'gen' || $input{callback}) {
-
-
- if ($input{gifquant} eq 'lm') {
-
- $input{make_colors} = 'addi';
- $input{translate} = 'perturb';
- $input{perturb} = $input{lmdither};
- } elsif ($input{gifquant} eq 'gen') {
- # just pass options through
- } else {
- $input{make_colors} = 'webmap'; # ignored
- $input{translate} = 'giflib';
- }
-
- if ($input{callback}) {
- defined $input{maxbuffer} or $input{maxbuffer} = -1;
- $rc = i_writegif_callback($input{callback}, $input{maxbuffer},
- \%input, $self->{IMG});
- } else {
- $rc = i_writegif_gen($fd, \%input, $self->{IMG});
- }
-
- } elsif ($input{gifquant} eq 'lm') {
- $rc=i_writegif($self->{IMG},$fd,$input{gifplanes},$input{lmdither},$input{lmfixed});
- } else {
- $rc=i_writegifmc($self->{IMG},$fd,$input{gifplanes});
- }
- if ( !defined($rc) ) {
- $self->{ERRSTR} = "Writing GIF file: "._error_as_msg(); return undef;
- }
- $self->{DEBUG} && print "writing a gif file\n";
-
- }
}
+
return $self;
}
sub write_multi {
my ($class, $opts, @images) = @_;
+ if (!$opts->{'type'} && $opts->{'file'}) {
+ $opts->{'type'} = $FORMATGUESS->($opts->{'file'});
+ }
+ unless ($opts->{'type'}) {
+ $class->_set_error('type parameter missing and not possible to guess from extension');
+ return;
+ }
+ # translate to ImgRaw
+ if (grep !UNIVERSAL::isa($_, 'Imager') || !$_->{IMG}, @images) {
+ $class->_set_error('Usage: Imager->write_multi({ options }, @images)');
+ return 0;
+ }
+ my @work = map $_->{IMG}, @images;
+ my ($IO, $file) = $class->_get_writer_io($opts, $opts->{'type'})
+ or return undef;
if ($opts->{'type'} eq 'gif') {
my $gif_delays = $opts->{gif_delays};
local $opts->{gif_delays} = $gif_delays;
- unless (ref $opts->{gif_delays}) {
+ if ($opts->{gif_delays} && !ref $opts->{gif_delays}) {
# assume the caller wants the same delay for each frame
$opts->{gif_delays} = [ ($gif_delays) x @images ];
}
- # translate to ImgRaw
- if (grep !UNIVERSAL::isa($_, 'Imager') || !$_->{IMG}, @images) {
- $ERRSTR = "Usage: Imager->write_multi({ options }, @images)";
- return 0;
- }
- my @work = map $_->{IMG}, @images;
- if ($opts->{callback}) {
- # Note: you may need to fix giflib for this one to work
- my $maxbuffer = $opts->{maxbuffer};
- defined $maxbuffer or $maxbuffer = -1; # max by default
- return i_writegif_callback($opts->{callback}, $maxbuffer,
- $opts, @work);
- }
- if ($opts->{fd}) {
- return i_writegif_gen($opts->{fd}, $opts, @work);
+ my $res = i_writegif_wiol($IO, $opts, @work);
+ $res or $class->_set_error($class->_error_as_msg());
+ return $res;
+ }
+ elsif ($opts->{'type'} eq 'tiff') {
+ my $res;
+ $opts->{fax_fine} = 1 unless exists $opts->{fax_fine};
+ if ($opts->{'class'} && $opts->{'class'} eq 'fax') {
+ $res = i_writetiff_multi_wiol_faxable($IO, $opts->{fax_fine}, @work);
}
else {
- my $fh = IO::File->new($opts->{file}, "w+");
- unless ($fh) {
- $ERRSTR = "Error creating $opts->{file}: $!";
- return 0;
- }
- binmode($fh);
- return i_writegif_gen(fileno($fh), $opts, @work);
+ $res = i_writetiff_multi_wiol($IO, @work);
}
+ $res or $class->_set_error($class->_error_as_msg());
+ return $res;
}
else {
$ERRSTR = "Sorry, write_multi doesn't support $opts->{'type'} yet";
@@ -1267,52 +1334,24 @@ sub read_multi {
$ERRSTR = "No type parameter supplied and it couldn't be guessed";
return;
}
- my $fd;
- my $file;
- if ($opts{file}) {
- $file = IO::File->new($opts{file}, "r");
- unless ($file) {
- $ERRSTR = "Could not open file $opts{file}: $!";
- return;
- }
- binmode $file;
- $fd = fileno($file);
- }
- elsif ($opts{fh}) {
- $fd = fileno($opts{fh});
- unless ($fd) {
- $ERRSTR = "File handle specified with fh option not open";
- return;
- }
- }
- elsif ($opts{fd}) {
- $fd = $opts{fd};
- }
- elsif ($opts{callback} || $opts{data}) {
- # don't fail here
- }
- else {
- $ERRSTR = "You need to specify one of file, fd, fh, callback or data";
- return;
- }
+ my ($IO, $file) = $class->_get_reader_io(\%opts, $opts{'type'})
+ or return;
if ($opts{'type'} eq 'gif') {
my @imgs;
- if ($fd) {
- @imgs = i_readgif_multi($fd);
+ @imgs = i_readgif_multi_wiol($IO);
+ if (@imgs) {
+ return map {
+ bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager'
+ } @imgs;
}
else {
- if (Imager::i_giflib_version() < 4.0) {
- $ERRSTR = "giflib3.x does not support callbacks";
- return;
- }
- if ($opts{callback}) {
- @imgs = i_readgif_multi_callback($opts{callback})
- }
- else {
- @imgs = i_readgif_multi_scalar($opts{data});
- }
+ $ERRSTR = _error_as_msg();
+ return;
}
+ }
+ elsif ($opts{'type'} eq 'tiff') {
+ my @imgs = i_readtiff_multi_wiol($IO, -1);
if (@imgs) {
return map {
bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager'
@@ -2230,6 +2269,17 @@ sub errstr {
ref $_[0] ? $_[0]->{ERRSTR} : $ERRSTR
}
+sub _set_error {
+ my ($self, $msg) = @_;
+
+ if (ref $self) {
+ $self->{ERRSTR} = $msg;
+ }
+ else {
+ $ERRSTR = $msg;
+ }
+}
+
# Default guess for the type of an image from extension
sub def_guess_type {
@@ -2244,6 +2294,7 @@ sub def_guess_type {
return 'tga' if ($ext eq "tga");
return 'rgb' if ($ext eq "rgb");
return 'gif' if ($ext eq "gif");
+ return 'raw' if ($ext eq "raw");
return ();
}
@@ -2327,9 +2378,8 @@ Imager - Perl extension for Generating 24 bit Images
=head1 SYNOPSIS
- use Imager qw(init);
+ use Imager;
- init();
$img = Imager->new();
$img->open(file=>'image.ppm',type=>'pnm')
|| print "failed: ",$img->{ERRSTR},"\n";
@@ -2433,6 +2483,105 @@ downwards.
=head2 Reading and writing images
+You can read and write a variety of images formats, assuming you have
+the appropriate libraries, and images can be read or written to/from
+files, file handles, file descriptors, scalars, or through callbacks.
+
+To see which image formats Imager is compiled to support the following
+code snippet is sufficient:
+
+ use Imager;
+ print join " ", keys %Imager::formats;
+
+This will include some other information identifying libraries rather
+than file formats.
+
+Reading writing to and from files is simple, use the C<read()>
+method to read an image:
+
+ my $img = Imager->new;
+ $img->read(file=>$filename, type=>$type)
+ or die "Cannot read $filename: ", $img->errstr;
+
+and the C<write()> method to write an image:
+
+ $img->write(file=>$filename, type=>$type)
+ or die "Cannot write $filename: ", $img->errstr;
+
+If the I<filename> includes an extension that Imager recognizes, then
+you don't need the I<type>, but you may want to provide one anyway.
+Imager currently does not check the files magic to determine the
+format. It is possible to override the method for determining the
+filetype from the filename. If the data is given in another form than
+a file name a
+
+When you read an image, Imager may set some tags, possibly including
+information about the spatial resolution, textual information, and
+animation information. See L</Tags> for specifics.
+
+When reading or writing you can specify one of a variety of sources or
+targets:
+
+=over
+
+=item file
+
+The C<file> parameter is the name of the image file to be written to
+or read from. If Imager recognizes the extension of the file you do
+not need to supply a C<type>.
+
+=item fh
+
+C<fh> is a file handle, typically either returned from
+C<<IO::File->new()>>, or a glob from an C<open> call. You should call
+C<binmode> on the handle before passing it to Imager.
+
+=item fd
+
+C<fd> is a file descriptor. You can get this by calling the
+C<fileno()> function on a file handle, or by using one of the standard
+file descriptor numbers.
+
+=item data
+
+When reading data, C<data> is a scalar containing the image file data,
+when writing, C<data> is a reference to the scalar to save the image
+file data too. For GIF images you will need giflib 4 or higher, and
+you may need to patch giflib to use this option for writing.
+
+=item callback
+
+Imager will make calls back to your supplied coderefs to read, write
+and seek from/to/through the image file.
+
+When reading from a file you can use either C<callback> or C<readcb>
+to supply the read callback, and when writing C<callback> or
+C<writecb> to supply the write callback.
+
+When writing you can also supply the C<maxbuffer> option to set the
+maximum amount of data that will be buffered before your write
+callback is called. Note: the amount of data supplied to your
+callback can be smaller or larger than this size.
+
+The read callback is called with 2 parameters, the minimum amount of
+data required, and the maximum amount that Imager will store in it's C
+level buffer. You may want to return the minimum if you have a slow
+data source, or the maximum if you have a fast source and want to
+prevent many calls to your perl callback. The read data should be
+returned as a scalar.
+
+Your write callback takes exactly one parameter, a scalar containing
+the data to be written. Return true for success.
+
+The seek callback takes 2 parameters, a I<POSITION>, and a I<WHENCE>,
+defined in the same way as perl's seek function.
+
+You can also supply a C<closecb> which is called with no parameters
+when there is no more data to be written. This could be used to flush
+buffered data.
+
+=back
+
C<$img-E<gt>read()> generally takes two parameters, 'file' and 'type'.
If the type of the file can be determined from the suffix of the file
it can be omitted. Format dependant parameters are: For images of
View
543 Imager.xs
@@ -130,6 +130,297 @@ static int write_callback(char *userdata, char const *data, int size) {
return success;
}
+#define CBDATA_BUFSIZE 8192
+
+struct cbdata {
+ /* the SVs we use to call back to Perl */
+ SV *writecb;
+ SV *readcb;
+ SV *seekcb;
+ SV *closecb;
+
+ /* we need to remember whether the buffer contains write data or
+ read data
+ */
+ int reading;
+ int writing;
+
+ /* how far we've read into the buffer (not used for writing) */
+ int where;
+
+ /* the amount of space used/data available in the buffer */
+ int used;
+
+ /* the maximum amount to fill the buffer before flushing
+ If any write is larger than this then the buffer is flushed and
+ the full write is performed. The write is _not_ split into
+ maxwrite sized calls
+ */
+ int maxlength;
+
+ char buffer[CBDATA_BUFSIZE];
+};
+
+/*
+
+call_writer(cbd, buf, size)
+
+Low-level function to call the perl writer callback.
+
+*/
+
+static ssize_t call_writer(struct cbdata *cbd, void const *buf, size_t size) {
+ int count;
+ int success;
+ SV *sv;
+ dSP;
+
+ if (!SvOK(cbd->writecb))
+ return -1;
+
+ ENTER;
+ SAVETMPS;
+ EXTEND(SP, 1);
+ PUSHMARK(SP);
+ PUSHs(sv_2mortal(newSVpv((char *)buf, size)));
+ PUTBACK;
+
+ count = perl_call_sv(cbd->writecb, G_SCALAR);
+
+ SPAGAIN;
+ if (count != 1)
+ croak("Result of perl_call_sv(..., G_SCALAR) != 1");
+
+ sv = POPs;
+ success = SvTRUE(sv);
+
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return success ? size : 0;
+}
+
+static ssize_t call_reader(struct cbdata *cbd, void *buf, size_t size,
+ size_t maxread) {
+ int count;
+ int result;
+ SV *data;
+ dSP;
+
+ if (!SvOK(cbd->readcb))
+ return -1;
+
+ ENTER;
+ SAVETMPS;
+ EXTEND(SP, 2);
+ PUSHMARK(SP);
+ PUSHs(sv_2mortal(newSViv(size)));
+ PUSHs(sv_2mortal(newSViv(maxread)));
+ PUTBACK;
+
+ count = perl_call_sv(cbd->readcb, G_SCALAR);
+
+ SPAGAIN;
+
+ if (count != 1)
+ croak("Result of perl_call_sv(..., G_SCALAR) != 1");
+
+ data = POPs;
+
+ if (SvOK(data)) {
+ STRLEN len;
+ char *ptr = SvPV(data, len);
+ if (len > maxread)
+ croak("Too much data returned in reader callback");
+
+ memcpy(buf, ptr, len);
+ result = len;
+ }
+ else {
+ result = -1;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return result;
+}
+
+static ssize_t write_flush(struct cbdata *cbd) {
+ ssize_t result;
+
+ result = call_writer(cbd, cbd->buffer, cbd->used);
+ cbd->used = 0;
+ return result;
+}
+
+static off_t io_seeker(void *p, off_t offset, int whence) {
+ struct cbdata *cbd = p;
+ int count;
+ off_t result;
+ dSP;
+
+ if (!SvOK(cbd->seekcb))
+ return -1;
+
+ if (cbd->writing) {
+ if (cbd->used && write_flush(cbd) <= 0)
+ return -1;
+ cbd->writing = 0;
+ }
+ if (whence == SEEK_CUR && cbd->reading && cbd->where != cbd->used) {
+ offset -= cbd->where - cbd->used;
+ }
+ cbd->reading = 0;
+ cbd->where = cbd->used = 0;
+
+ ENTER;
+ SAVETMPS;
+ EXTEND(SP, 2);
+ PUSHMARK(SP);
+ PUSHs(sv_2mortal(newSViv(offset)));
+ PUSHs(sv_2mortal(newSViv(whence)));
+ PUTBACK;
+
+ count = perl_call_sv(cbd->seekcb, G_SCALAR);
+
+ SPAGAIN;
+
+ if (count != 1)
+ croak("Result of perl_call_sv(..., G_SCALAR) != 1");
+
+ result = POPi;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return result;
+}
+
+static ssize_t io_writer(void *p, void const *data, size_t size) {
+ struct cbdata *cbd = p;
+
+ /*printf("io_writer(%p, %p, %u)\n", p, data, size);*/
+ if (!cbd->writing) {
+ if (cbd->reading && cbd->where < cbd->used) {
+ /* we read past the place where the caller expected us to be
+ so adjust our position a bit */
+ *(char *)0 = 0;
+ if (io_seeker(p, cbd->where - cbd->used, SEEK_CUR) < 0) {
+ return -1;
+ }
+ cbd->reading = 0;
+ }
+ cbd->where = cbd->used = 0;
+ }
+ cbd->writing = 1;
+ if (cbd->used && cbd->used + size > cbd->maxlength) {
+ if (write_flush(cbd) <= 0) {
+ return 0;
+ }
+ cbd->used = 0;
+ }
+ if (cbd->used+size <= cbd->maxlength) {
+ memcpy(cbd->buffer + cbd->used, data, size);
+ cbd->used += size;
+ return size;
+ }
+ /* it doesn't fit - just pass it up */
+ return call_writer(cbd, data, size);
+}
+
+static ssize_t io_reader(void *p, void *data, size_t size) {
+ struct cbdata *cbd = p;
+ ssize_t total;
+ char *out = data; /* so we can do pointer arithmetic */
+ int i;
+
+ if (cbd->writing) {
+ if (write_flush(cbd) <= 0)
+ return 0;
+ cbd->writing = 0;
+ }
+
+ cbd->reading = 1;
+ if (size <= cbd->used - cbd->where) {
+ /* simplest case */
+ memcpy(data, cbd->buffer+cbd->where, size);
+ cbd->where += size;
+ return size;
+ }
+ total = 0;
+ memcpy(out, cbd->buffer + cbd->where, cbd->used - cbd->where);
+ total += cbd->used - cbd->where;
+ size -= cbd->used - cbd->where;
+ out += cbd->used - cbd->where;
+ if (size < sizeof(cbd->buffer)) {
+ int did_read;
+ int copy_size;
+ while (size
+ && (did_read = call_reader(cbd, cbd->buffer, size,
+ sizeof(cbd->buffer))) > 0) {
+ cbd->where = 0;
+ cbd->used = did_read;
+
+ copy_size = min(size, cbd->used);
+ memcpy(out, cbd->buffer, copy_size);
+ cbd->where += copy_size;
+ out += copy_size;
+ total += copy_size;
+ size -= copy_size;
+ }
+ }
+ else {
+ /* just read the rest - too big for our buffer*/
+ int did_read;
+ while ((did_read = call_reader(cbd, out, size, size)) > 0) {
+ size -= did_read;
+ total += did_read;
+ out += did_read;
+ }
+ }
+
+ return total;
+}
+
+static void io_closer(void *p) {
+ struct cbdata *cbd = p;
+
+ if (cbd->writing && cbd->used > 0) {
+ write_flush(cbd);
+ cbd->writing = 0;
+ }
+
+ if (SvOK(cbd->closecb)) {
+ dSP;
+
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ PUTBACK;
+
+ perl_call_sv(cbd->closecb, G_VOID);
+
+ SPAGAIN;
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ }
+}
+
+static void io_destroyer(void *p) {
+ struct cbdata *cbd = p;
+
+ SvREFCNT_dec(cbd->writecb);
+ SvREFCNT_dec(cbd->readcb);
+ SvREFCNT_dec(cbd->seekcb);
+ SvREFCNT_dec(cbd->closecb);
+}
+
struct value_name {
char *name;
int value;
@@ -717,7 +1008,34 @@ io_new_buffer(data)
RETVAL = io_new_buffer(data, length, my_SvREFCNT_dec, ST(0));
OUTPUT:
RETVAL
-
+
+Imager::IO
+io_new_cb(writecb, readcb, seekcb, closecb, maxwrite = CBDATA_BUFSIZE)
+ SV *writecb;
+ SV *readcb;
+ SV *seekcb;
+ SV *closecb;
+ int maxwrite;
+ PREINIT:
+ struct cbdata *cbd;
+ CODE:
+ cbd = mymalloc(sizeof(struct cbdata));
+ SvREFCNT_inc(writecb);
+ cbd->writecb = writecb;
+ SvREFCNT_inc(readcb);
+ cbd->readcb = readcb;
+ SvREFCNT_inc(seekcb);
+ cbd->seekcb = seekcb;
+ SvREFCNT_inc(closecb);
+ cbd->closecb = closecb;
+ cbd->reading = cbd->writing = cbd->where = cbd->used = 0;
+ if (maxwrite > CBDATA_BUFSIZE)
+ maxwrite = CBDATA_BUFSIZE;
+ cbd->maxlength = maxwrite;
+ RETVAL = io_new_cb(cbd, io_reader, io_writer, io_seeker, io_closer,
+ io_destroyer);
+ OUTPUT:
+ RETVAL
void
io_slurp(ig)
@@ -1425,6 +1743,26 @@ i_readtiff_wiol(ig, length)
Imager::IO ig
int length
+void
+i_readtiff_multi_wiol(ig, length)
+ Imager::IO ig
+ int length
+ PREINIT:
+ i_img **imgs;
+ int count;
+ int i;
+ PPCODE:
+ imgs = i_readtiff_multi_wiol(ig, length, &count);
+ if (imgs) {
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i) {
+ SV *sv = sv_newmortal();
+ sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
+ PUSHs(sv);
+ }
+ myfree(imgs);
+ }
+
undef_int
i_writetiff_wiol(im, ig)
@@ -1432,16 +1770,96 @@ i_writetiff_wiol(im, ig)
Imager::IO ig
undef_int
+i_writetiff_multi_wiol(ig, ...)
+ Imager::IO ig
+ PREINIT:
+ int i;
+ int img_count;
+ i_img **imgs;
+ CODE:
+ if (items < 2)
+ croak("Usage: i_writetiff_multi_wiol(ig, images...)");
+ img_count = items - 1;
+ RETVAL = 1;
+ if (img_count < 1) {
+ RETVAL = 0;
+ i_clear_error();
+ i_push_error(0, "You need to specify images to save");
+ }
+ else {
+ imgs = mymalloc(sizeof(i_img *) * img_count);
+ for (i = 0; i < img_count; ++i) {
+ SV *sv = ST(1+i);
+ imgs[i] = NULL;
+ if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
+ imgs[i] = (i_img *)SvIV((SV*)SvRV(sv));
+ }
+ else {
+ i_clear_error();
+ i_push_error(0, "Only images can be saved");
+ myfree(imgs);
+ RETVAL = 0;
+ break;
+ }
+ }
+ if (RETVAL) {
+ RETVAL = i_writetiff_multi_wiol(ig, imgs, img_count);
+ }
+ myfree(imgs);
+ }
+ OUTPUT:
+ RETVAL
+
+undef_int
i_writetiff_wiol_faxable(im, ig, fine)
Imager::ImgRaw im
Imager::IO ig
int fine
-
-#endif /* HAVE_LIBTIFF */
-
+undef_int
+i_writetiff_multi_wiol_faxable(ig, fine, ...)
+ Imager::IO ig
+ int fine
+ PREINIT:
+ int i;
+ int img_count;
+ i_img **imgs;
+ CODE:
+ if (items < 3)
+ croak("Usage: i_writetiff_multi_wiol_faxable(ig, fine, images...)");
+ img_count = items - 2;
+ RETVAL = 1;
+ if (img_count < 1) {
+ RETVAL = 0;
+ i_clear_error();
+ i_push_error(0, "You need to specify images to save");
+ }
+ else {
+ imgs = mymalloc(sizeof(i_img *) * img_count);
+ for (i = 0; i < img_count; ++i) {
+ SV *sv = ST(2+i);
+ imgs[i] = NULL;
+ if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
+ imgs[i] = (i_img *)SvIV((SV*)SvRV(sv));
+ }
+ else {
+ i_clear_error();
+ i_push_error(0, "Only images can be saved");
+ myfree(imgs);
+ RETVAL = 0;
+ break;
+ }
+ }
+ if (RETVAL) {
+ RETVAL = i_writetiff_multi_wiol_faxable(ig, imgs, img_count, fine);
+ }
+ myfree(imgs);
+ }
+ OUTPUT:
+ RETVAL
+#endif /* HAVE_LIBTIFF */
#ifdef HAVE_LIBPNG
@@ -1626,6 +2044,59 @@ i_writegif_callback(cb, maxbuffer,...)
cleanup_gif_opts(&opts);
cleanup_quant_opts(&quant);
+undef_int
+i_writegif_wiol(ig, opts,...)
+ Imager::IO ig
+ PREINIT:
+ i_quantize quant;
+ i_gif_opts opts;
+ i_img **imgs = NULL;
+ int img_count;
+ int i;
+ HV *hv;
+ CODE:
+ if (items < 3)
+ croak("Usage: i_writegif_wiol(IO,hashref, images...)");
+ if (!SvROK(ST(1)) || ! SvTYPE(SvRV(ST(1))))
+ croak("i_writegif_callback: Second argument must be a hash ref");
+ hv = (HV *)SvRV(ST(1));
+ memset(&quant, 0, sizeof(quant));
+ quant.mc_size = 256;
+ memset(&opts, 0, sizeof(opts));
+ handle_quant_opts(&quant, hv);
+ handle_gif_opts(&opts, hv);
+ img_count = items - 2;
+ RETVAL = 1;
+ if (img_count < 1) {
+ RETVAL = 0;
+ }
+ else {
+ imgs = mymalloc(sizeof(i_img *) * img_count);
+ for (i = 0; i < img_count; ++i) {
+ SV *sv = ST(2+i);
+ imgs[i] = NULL;
+ if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
+ imgs[i] = (i_img *)SvIV((SV*)SvRV(sv));
+ }
+ else {
+ RETVAL = 0;
+ break;
+ }
+ }
+ if (RETVAL) {
+ RETVAL = i_writegif_wiol(ig, &quant, &opts, imgs, img_count);
+ }
+ myfree(imgs);
+ if (RETVAL) {
+ copy_colors_back(hv, &quant);
+ }
+ }
+ ST(0) = sv_newmortal();
+ if (RETVAL == 0) ST(0)=&PL_sv_undef;
+ else sv_setiv(ST(0), (IV)RETVAL);
+ cleanup_gif_opts(&opts);
+ cleanup_quant_opts(&quant);
+
void
i_readgif(fd)
int fd
@@ -1674,9 +2145,53 @@ i_readgif(fd)
PUSHs(newRV_noinc((SV*)ct));
}
+void
+i_readgif_wiol(ig)
+ Imager::IO ig
+ PREINIT:
+ int* colour_table;
+ int colours, q, w;
+ i_img* rimg;
+ SV* temp[3];
+ AV* ct;
+ SV* r;
+ PPCODE:
+ colour_table = NULL;
+ colours = 0;
+ if(GIMME_V == G_ARRAY) {
+ rimg = i_readgif_wiol(ig,&colour_table,&colours);
+ } else {
+ /* don't waste time with colours if they aren't wanted */
+ rimg = i_readgif_wiol(ig,NULL,NULL);
+ }
+
+ if (colour_table == NULL) {
+ EXTEND(SP,1);
+ r=sv_newmortal();
+ sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+ PUSHs(r);
+ } else {
+ /* the following creates an [[r,g,b], [r, g, b], [r, g, b]...] */
+ /* I don't know if I have the reference counts right or not :( */
+ /* Neither do I :-) */
+ /* No Idea here either */
+ ct=newAV();
+ av_extend(ct, colours);
+ for(q=0; q<colours; q++) {
+ for(w=0; w<3; w++)
+ temp[w]=sv_2mortal(newSViv(colour_table[q*3 + w]));
+ av_store(ct, q, (SV*)newRV_noinc((SV*)av_make(3, temp)));
+ }
+ myfree(colour_table);
+ EXTEND(SP,2);
+ r = sv_newmortal();
+ sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+ PUSHs(r);
+ PUSHs(newRV_noinc((SV*)ct));
+ }
void
i_readgif_scalar(...)
@@ -1838,6 +2353,26 @@ i_readgif_multi_callback(cb)
myfree(imgs);
}
+void
+i_readgif_multi_wiol(ig)
+ Imager::IO ig
+ PREINIT:
+ i_img **imgs;
+ int count;
+ int i;
+ PPCODE:
+ imgs = i_readgif_multi_wiol(ig, &count);
+ if (imgs) {
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i) {
+ SV *sv = sv_newmortal();
+ sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
+ PUSHs(sv);
+ }
+ myfree(imgs);
+ }
+
+
#endif
View
7 TODO
@@ -26,7 +26,9 @@ MultiImage & metadata support:
interface design that takes these factors into account.
- define common i_* tags for specifying attribute common among images
like spatial resolution (implement for other image types, especially
- TIFF)
+ TIFF) (Spatial resolution is supported for all types that support
+ it - are there any other common properties we can add?)
+
New Features:
- Add mng support, pcx and aalib support.
@@ -37,12 +39,11 @@ New Features:
- FITS
- WMF (extract bitmap data on read)
- gzip or bzip2 compressed raw
+ - postscript for output
- Transforms, interpolated multidimensional lookup tables.
Usefull for CMYK <-> RGB table lookup.
-- Finish antialiased filled polygon function.
-
- advanced font layout (spacing, kerning, alignment) (Artur?)
- ways to check if characters are present in a font, eg. checking if
View
9 bmp.c
@@ -432,6 +432,8 @@ write_1bit_data(io_glue *ig, i_img *im) {
myfree(packed);
myfree(line);
+ ig->closecb(ig);
+
return 1;
}
@@ -480,6 +482,8 @@ write_4bit_data(io_glue *ig, i_img *im) {
myfree(packed);
myfree(line);
+ ig->closecb(ig);
+
return 1;
}
@@ -517,6 +521,8 @@ write_8bit_data(io_glue *ig, i_img *im) {
}
myfree(line);
+ ig->closecb(ig);
+
return 1;
}
@@ -545,6 +551,7 @@ write_24bit_data(io_glue *ig, i_img *im) {
return 0;
chans = im->channels >= 3 ? bgr_chans : grey_chans;
samples = mymalloc(line_size);
+ memset(samples, 0, line_size);
for (y = im->ysize-1; y >= 0; --y) {
i_gsamp(im, 0, im->xsize, y, samples, chans, 3);
if (ig->writecb(ig, samples, line_size) < 0) {
@@ -555,6 +562,8 @@ write_24bit_data(io_glue *ig, i_img *im) {
}
myfree(samples);
+ ig->closecb(ig);
+
return 1;
}
View
174 gif.c
@@ -1,6 +1,11 @@
#include "image.h"
#include <gif_lib.h>
-
+#ifdef _MSCVER
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+#include <errno.h>
/* XXX: Reading still needs to support reading all those gif properties */
/*
@@ -570,9 +575,9 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count) {
results = mymalloc(result_alloc * sizeof(i_img *));
}
else {
- i_img **newresults;
+ /* myrealloc never fails (it just dies if it can't allocate) */
result_alloc *= 2;
- newresults = myrealloc(results, result_alloc * sizeof(i_img *));
+ results = myrealloc(results, result_alloc * sizeof(i_img *));
}
}
results[*count-1] = img;
@@ -732,6 +737,49 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count) {
return results;
}
+#if IM_GIFMAJOR >= 4
+/* giflib declares this incorrectly as EgifOpen */
+extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
+
+static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
+#endif
+
+/*
+=item i_readgif_multi_wiol(ig, int *count)
+
+=cut
+*/
+
+i_img **
+i_readgif_multi_wiol(io_glue *ig, int *count) {
+ io_glue_commit_types(ig);
+
+ if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
+ return i_readgif_multi(ig->source.fdseek.fd, count);
+ }
+ else {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+
+ i_clear_error();
+
+ if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
+ return NULL;
+ }
+
+ return i_readgif_multi_low(GifFile, count);
+#else
+ i_clear_error();
+ i_push_error(0, "callbacks not supported with giflib3");
+
+ return NULL;
+#endif
+ }
+}
+
/*
=item i_readgif_multi(int fd, int *count)
@@ -977,10 +1025,60 @@ i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int
return result;
#else
+ i_clear_error();
+ i_push_error(0, "callbacks not supported with giflib3");
+
return NULL;
#endif
}
+#if IM_GIFMAJOR >= 4
+
+static int
+io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
+ io_glue *ig = (io_glue *)gft->UserData;
+
+ return ig->readcb(ig, buf, length);
+}
+
+#endif
+
+i_img *
+i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
+ io_glue_commit_types(ig);
+
+ if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
+ int fd = dup(ig->source.fdseek.fd);
+ if (fd < 0) {
+ i_push_error(errno, "dup() failed");
+ return 0;
+ }
+ return i_readgif(fd, color_table, colors);
+ }
+ else {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+
+ i_clear_error();
+
+ if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
+ return NULL;
+ }
+
+ return i_readgif_low(GifFile, color_table, colors);
+
+#else
+ i_clear_error();
+ i_push_error(0, "callbacks not supported with giflib3");
+
+ return NULL;
+#endif
+ }
+}
+
/*
=item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
@@ -1149,6 +1247,10 @@ static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
/* giflib spews for 1 colour maps, reasonable, I suppose */
if (map_size == 1)
map_size = 2;
+ while (i < map_size) {
+ colors[i].Red = colors[i].Green = colors[i].Blue = 0;
+ ++i;
+ }
map = MakeMapObject(map_size, colors);
mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
@@ -1705,8 +1807,6 @@ i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
#if IM_GIFMAJOR >= 4
GifFileType *gf;
i_gen_write_data *gwd = i_gen_write_data_new(cb, userdata, maxlength);
- /* giflib declares this incorrectly as EgifOpen */
- extern GifFileType *EGifOpen(void *userData, OutputFunc writeFunc);
int result;
i_clear_error();
@@ -1725,10 +1825,74 @@ i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
result = i_writegif_low(quant, gf, imgs, count, opts);
return free_gen_write_data(gwd, result);
#else
+ i_clear_error();
+ i_push_error(0, "callbacks not supported with giflib3");
+
return 0;
#endif
}
+#if IM_GIFMAJOR >= 4
+
+static int
+io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
+ io_glue *ig = (io_glue *)gft->UserData;
+
+ return ig->writecb(ig, data, length);
+}
+
+#endif
+
+/*
+=item i_writegif_wiol(ig, quant, opts, imgs, count)
+
+=cut
+*/
+undef_int
+i_writegif_wiol(io_glue *ig, i_quantize *quant, i_gif_opts *opts, i_img **imgs,
+ int count) {
+ io_glue_commit_types(ig);
+
+ if (ig->source.type == FDSEEK || ig->source.type == FDNOSEEK) {
+ int fd = dup(ig->source.fdseek.fd);
+ if (fd < 0) {
+ i_push_error(errno, "dup() failed");
+ return 0;
+ }
+ /* giflib opens the fd with fdopen(), which is then closed when fclose()
+ is called - dup it so the caller's fd isn't closed */
+ return i_writegif_gen(quant, fd, imgs, count, opts);
+ }
+ else {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+ int result;
+
+ i_clear_error();
+
+ gif_set_version(quant, opts);
+
+ if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
+ return 0;
+ }
+
+ result = i_writegif_low(quant, GifFile, imgs, count, opts);
+
+ ig->closecb(ig);
+
+ return result;
+#else
+ i_clear_error();
+ i_push_error(0, "callbacks not supported with giflib3");
+
+ return 0;
+#endif
+ }
+}
+
/*
=item gif_error_msg(int code)
View
8 image.h
@@ -525,8 +525,11 @@ undef_int i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor);
#ifdef HAVE_LIBTIFF
i_img * i_readtiff_wiol(io_glue *ig, int length);
+i_img ** i_readtiff_multi_wiol(io_glue *ig, int length, int *count);
undef_int i_writetiff_wiol(i_img *im, io_glue *ig);
+undef_int i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count);
undef_int i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine);
+undef_int i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine);
#endif /* HAVE_LIBTIFF */
@@ -537,17 +540,20 @@ undef_int i_writepng_wiol(i_img *im, io_glue *ig);
#ifdef HAVE_LIBGIF
i_img *i_readgif(int fd, int **colour_table, int *colours);
+i_img *i_readgif_wiol(io_glue *ig, int **colour_table, int *colours);
i_img *i_readgif_scalar(char *data, int length, int **colour_table, int *colours);
i_img *i_readgif_callback(i_read_callback_t callback, char *userdata, int **colour_table, int *colours);
extern i_img **i_readgif_multi(int fd, int *count);
extern i_img **i_readgif_multi_scalar(char *data, int length, int *count);
extern i_img **i_readgif_multi_callback(i_read_callback_t callback, char *userdata, int *count);
+extern i_img **i_readgif_multi_wiol(io_glue *ig, int *count);
undef_int i_writegif(i_img *im,int fd,int colors,int pixdev,int fixedlen,i_color fixed[]);
undef_int i_writegifmc(i_img *im,int fd,int colors);
undef_int i_writegifex(i_img *im,int fd);
undef_int i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts);
undef_int i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata, int maxbuffer, i_img **imgs, int count, i_gif_opts *opts);
-
+undef_int i_writegif_wiol(io_glue *ig, i_quantize *quant, i_gif_opts *opts,
+ i_img **imgs, int count);
void i_qdist(i_img *im);
#endif /* HAVE_LIBGIF */
View
167 iolayer.c
@@ -57,8 +57,11 @@ Some of these functions are internal.
=cut
*/
-
-
+static ssize_t fd_read(io_glue *ig, void *buf, size_t count);
+static ssize_t fd_write(io_glue *ig, const void *buf, size_t count);
+static off_t fd_seek(io_glue *ig, off_t offset, int whence);
+static void fd_close(io_glue *ig);
+static ssize_t fd_size(io_glue *ig);
/*
* Callbacks for sources that cannot seek
@@ -95,14 +98,18 @@ static
ssize_t
realseek_read(io_glue *ig, void *buf, size_t count) {
io_ex_rseek *ier = ig->exdata;
- int fd = (int)ig->source.cb.p;
+ void *p = ig->source.cb.p;
ssize_t rc = 0;
size_t bc = 0;
char *cbuf = buf;
- IOL_DEB( printf("realseek_read: fd = %d, ier->cpos = %ld, buf = %p, count = %d\n", fd, (long) ier->cpos, buf, count) );
- /* Is this a good idea? Would it be better to handle differently? skip handling? */
- while( count!=bc && (rc = ig->source.cb.readcb(fd,cbuf+bc,count-bc))>0 ) bc+=rc;
+ IOL_DEB( printf("realseek_read: fd = %d, ier->cpos = %ld, buf = %p, "
+ "count = %d\n", fd, (long) ier->cpos, buf, count) );
+ /* Is this a good idea? Would it be better to handle differently?
+ skip handling? */
+ while( count!=bc && (rc = ig->source.cb.readcb(p,cbuf+bc,count-bc))>0 ) {
+ bc+=rc;
+ }
ier->cpos += bc;
IOL_DEB( printf("realseek_read: rc = %d, bc = %d\n", rc, bc) );
@@ -126,15 +133,19 @@ static
ssize_t
realseek_write(io_glue *ig, const void *buf, size_t count) {
io_ex_rseek *ier = ig->exdata;
- int fd = (int)ig->source.cb.p;
+ void *p = ig->source.cb.p;
ssize_t rc = 0;
size_t bc = 0;
char *cbuf = (char*)buf;
- IOL_DEB( printf("realseek_write: fd = %d, ier->cpos = %ld, buf = %p, count = %d\n", fd, (long) ier->cpos, buf, count) );
- /* Is this a good idea? Would it be better to handle differently? skip handling? */
+ IOL_DEB( printf("realseek_write: ig = %p, ier->cpos = %ld, buf = %p, "
+ "count = %d\n", ig, (long) ier->cpos, buf, count) );
- while( count!=bc && (rc = ig->source.cb.writecb(fd,cbuf+bc,count-bc))>0 ) bc+=rc;
+ /* Is this a good idea? Would it be better to handle differently?
+ skip handling? */
+ while( count!=bc && (rc = ig->source.cb.writecb(p,cbuf+bc,count-bc))>0 ) {
+ bc+=rc;
+ }
ier->cpos += bc;
IOL_DEB( printf("realseek_write: rc = %d, bc = %d\n", rc, bc) );
@@ -145,19 +156,19 @@ realseek_write(io_glue *ig, const void *buf, size_t count) {
/*
=item realseek_close(ig)
-Closes a source that can be seeked on. Not sure if this should be an actual close
-or not. Does nothing for now. Should be fixed.
+Closes a source that can be seeked on. Not sure if this should be an
+actual close or not. Does nothing for now. Should be fixed.
ig - data source
-=cut
-*/
+=cut */
static
void
realseek_close(io_glue *ig) {
mm_log((1, "realseek_close(ig %p)\n", ig));
- /* FIXME: Do stuff here */
+ if (ig->source.cb.closecb)
+ ig->source.cb.closecb(ig->source.cb.p);
}
@@ -177,38 +188,16 @@ static
off_t
realseek_seek(io_glue *ig, off_t offset, int whence) {
/* io_ex_rseek *ier = ig->exdata; Needed later */
- int fd = (int)ig->source.cb.p;
+ void *p = ig->source.cb.p;
int rc;
IOL_DEB( printf("realseek_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) );
- rc = lseek(fd, offset, whence);
+ rc = ig->source.cb.seekcb(p, offset, whence);
IOL_DEB( printf("realseek_seek: rc %ld\n", (long) rc) );
return rc;
/* FIXME: How about implementing this offset handling stuff? */
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
* Callbacks for sources that are a fixed size buffer
*/
@@ -790,26 +779,36 @@ io_obj_setp_bufchain(io_obj *io) {
/*
-=item io_obj_setp_cb(io, p, readcb, writecb, seekcb)
+=item io_obj_setp_cb2(io, p, readcb, writecb, seekcb, closecb, destroycb)
Sets an io_object for reading from a source that uses callbacks
io - io object that describes a source
- p - pointer to data for callbacks
- readcb - read callback to read from source
- writecb - write callback to write to source
- seekcb - seek callback to seek on source
+ p - pointer to data for callbacks
+ readcb - read callback to read from source
+ writecb - write callback to write to source
+ seekcb - seek callback to seek on source
+ closecb - flush any pending data
+ destroycb - release any extra resources
=cut
*/
void
-io_obj_setp_cb(io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb) {
- io->cb.type = CBSEEK;
- io->cb.p = p;
- io->cb.readcb = readcb;
- io->cb.writecb = writecb;
- io->cb.seekcb = seekcb;
+io_obj_setp_cb2(io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb, closel closecb, destroyl destroycb) {
+ io->cb.type = CBSEEK;
+ io->cb.p = p;
+ io->cb.readcb = readcb;
+ io->cb.writecb = writecb;
+ io->cb.seekcb = seekcb;
+ io->cb.closecb = closecb;
+ io->cb.destroycb = destroycb;
+}
+
+void
+io_obj_setp_cb(io_obj *io, void *p, readl readcb, writel writecb,
+ seekl seekcb) {
+ io_obj_setp_cb2(io, p, readcb, writecb, seekcb, NULL, NULL);
}
/*
@@ -878,6 +877,15 @@ io_glue_commit_types(io_glue *ig) {
ig->closecb = buffer_close;
}
break;
+ case FDSEEK:
+ {
+ ig->exdata = NULL;
+ ig->readcb = fd_read;
+ ig->writecb = fd_write;
+ ig->seekcb = fd_seek;
+ ig->closecb = fd_close;
+ break;
+ }
}
}
@@ -972,16 +980,32 @@ io_new_fd(int fd) {
mm_log((1, "io_new_fd(fd %d)\n", fd));
ig = mymalloc(sizeof(io_glue));
memset(ig, 0, sizeof(*ig));
+ ig->source.type = FDSEEK;
+ ig->source.fdseek.fd = fd;
+#if 0
#ifdef _MSC_VER
io_obj_setp_cb(&ig->source, (void*)fd, _read, _write, _lseek);
#else
io_obj_setp_cb(&ig->source, (void*)fd, read, write, lseek);
#endif
+#endif
mm_log((1, "(%p) <- io_new_fd\n", ig));
return ig;
}
+io_glue *io_new_cb(void *p, readl readcb, writel writecb, seekl seekcb,
+ closel closecb, destroyl destroycb) {
+ io_glue *ig;
+ mm_log((1, "io_new_cb(p %p, readcb %p, writecb %p, seekcb %p, closecb %p, "
+ "destroycb %p)\n", p, readcb, writecb, seekcb, closecb, destroycb));
+ ig = mymalloc(sizeof(io_glue));
+ memset(ig, 0, sizeof(ig));
+ io_obj_setp_cb2(&ig->source, p, readcb, writecb, seekcb, closecb, destroycb);
+ mm_log((1, "(%p) <- io_new_cb\n", ig));
+
+ return ig;
+}
/*
=item io_slurp(ig)
@@ -1024,6 +1048,44 @@ io_slurp(io_glue *ig, unsigned char **c) {
return rc;
}
+/*
+=item fd_read(ig, buf, count)
+
+=cut
+*/
+static ssize_t fd_read(io_glue *ig, void *buf, size_t count) {
+#ifdef _MSC_VER
+ return _read(ig->source.fdseek.fd, buf, count);
+#else
+ return read(ig->source.fdseek.fd, buf, count);
+#endif
+}
+
+static ssize_t fd_write(io_glue *ig, const void *buf, size_t count) {
+#ifdef _MSC_VER
+ return _write(ig->source.fdseek.fd, buf, count);
+#else
+ return write(ig->source.fdseek.fd, buf, count);
+#endif
+}
+
+static off_t fd_seek(io_glue *ig, off_t offset, int whence) {
+#ifdef _MSC_VER
+ return _lseek(ig->source.fdseek.fd, offset, whence);
+#else
+ return lseek(ig->source.fdseek.fd, offset, whence);
+#endif
+}
+
+static void fd_close(io_glue *ig) {
+ /* no, we don't close it */
+}
+
+static ssize_t fd_size(io_glue *ig) {
+ mm_log((1, "fd_size(ig %p) unimplemented\n", ig));
+
+ return -1;
+}
/*
=item io_glue_DESTROY(ig)
@@ -1050,9 +1112,10 @@ io_glue_DESTROY(io_glue *ig) {
}
break;
case CBSEEK:
- default:
{
io_ex_rseek *ier = ig->exdata;
+ if (ig->source.cb.destroycb)
+ ig->source.cb.destroycb(ig->source.cb.p);
myfree(ier);
}
break;
@@ -1066,6 +1129,8 @@ io_glue_DESTROY(io_glue *ig) {
myfree(ieb);
}
break;
+ default:
+ break;
}
myfree(ig);
}
View
14 iolayer.h
@@ -47,10 +47,12 @@ typedef void (*closebufp)(void *p);
/* Callbacks we get */
-typedef ssize_t(*readl) (int fd, void *buf, size_t count);
-typedef ssize_t(*writel)(int fd, const void *buf, size_t count);
-typedef off_t (*seekl) (int fd, off_t offset, int whence);
-typedef ssize_t(*sizel) (int fd);
+typedef ssize_t(*readl) (void *p, void *buf, size_t count);
+typedef ssize_t(*writel)(void *p, const void *buf, size_t count);
+typedef off_t (*seekl) (void *p, off_t offset, int whence);
+typedef void (*closel)(void *p);
+typedef void (*destroyl)(void *p);
+typedef ssize_t(*sizel) (void *p);
extern char *io_type_names[];
@@ -122,6 +124,8 @@ typedef struct {
readl readcb;
writel writecb;
seekl seekcb;
+ closel closecb;
+ destroyl destroycb;
} io_cb;
typedef union {
@@ -144,6 +148,7 @@ typedef struct _io_glue {
void io_obj_setp_buffer(io_obj *io, char *p, size_t len, closebufp closecb, void *closedata);
void io_obj_setp_cb (io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb);
+void io_obj_setp_cb2 (io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb, closel closecb, destroyl destroycb);
void io_glue_commit_types(io_glue *ig);
void io_glue_gettypes (io_glue *ig, int reqmeth);
@@ -152,6 +157,7 @@ void io_glue_gettypes (io_glue *ig, int reqmeth);
io_glue *io_new_fd(int fd);
io_glue *io_new_bufchain(void);
io_glue *io_new_buffer(char *data, size_t len, closebufp closecb, void *closedata);
+io_glue *io_new_cb(void *p, readl readcb, writel writecb, seekl seekcb, closel closecb, destroyl destroycb);
size_t io_slurp(io_glue *ig, unsigned char **c);
void io_glue_DESTROY(io_glue *ig);
View
2 jpeg.c
@@ -488,6 +488,8 @@ i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
jpeg_destroy_compress(&cinfo);
+ ig->closecb(ig);
+
return(1);
}
View
2 lib/Imager/Font.pm
@@ -475,7 +475,7 @@ Checks if the characters in $text are defined by the font.
In a list context returns a list of true or false value corresponding
to the characters in $text, true if the character is defined, false if
not. In scalar context returns a string of NUL or non-NUL
-characters. Supports UTF8.
+characters. Supports UTF8 where the font driver supports UTF8.
Not all fonts support this method (use $font->can("has_chars") to
check.)
View
2 png.c
@@ -181,6 +181,8 @@ i_writepng_wiol(i_img *im, io_glue *ig) {
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ ig->closecb(ig);
+
return(1);
}
View
1 pnm.c
@@ -498,6 +498,7 @@ i_writeppm_wiol(i_img *im, io_glue *ig) {
mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
return(0);
}
+ ig->closecb(ig);
return(1);
}
View
3 raw.c
@@ -155,5 +155,8 @@ i_writeraw_wiol(i_img* im, io_glue *ig) {
}
}
}
+
+ ig->closecb(ig);
+
return(1);
}
View
80 t/t07iolayer.t
@@ -1,4 +1,4 @@
-BEGIN { $|=1; print "1..7\n"; }
+BEGIN { $|=1; print "1..20\n"; }
END { print "not ok 1\n" unless $loaded; };
use Imager qw(:all);
++$loaded;
@@ -35,7 +35,7 @@ $IO3 = Imager::io_new_buffer($data);
$im = Imager::i_readpnm_wiol($IO3, -1);
print "ok 3\n";
-
+undef $IO3;
open(FH, "<testimg/penguin-base.ppm") or die $!;
binmode(FH);
@@ -61,8 +61,74 @@ print "ok 6\n";
$IO6 = Imager::io_new_buffer($data2);
$im3 = Imager::i_readpnm_wiol($IO6, -1);
-Imager::i_img_diff($im, $im3) ? print "not ok 7\n" : print "ok 7\n";
-
-
-
-
+ok(7, Imager::i_img_diff($im, $im3) == 0, "read from buffer");
+
+my $work = $data;
+my $pos = 0;
+sub io_reader {
+ my ($size, $maxread) = @_;
+ my $out = substr($work, $pos, $maxread);
+ $pos += length $out;
+ $out;
+}
+sub io_reader2 {
+ my ($size, $maxread) = @_;
+ my $out = substr($work, $pos, $maxread);
+ $pos += length $out;
+ $out;
+}
+my $IO7 = Imager::io_new_cb(undef, \&io_reader, undef, undef);
+ok(8, $IO7, "making readcb object");
+my $im4 = Imager::i_readpnm_wiol($IO7, -1);
+ok(9, $im4, "read from cb");
+ok(10, Imager::i_img_diff($im, $im4) == 0, "read from cb image match");
+
+$pos = 0;
+$IO7 = Imager::io_new_cb(undef, \&io_reader2, undef, undef);
+ok(11, $IO7, "making short readcb object");
+my $im5 = Imager::i_readpnm_wiol($IO7, -1);
+ok(12, $im4, "read from cb2");
+ok(13, Imager::i_img_diff($im, $im5) == 0, "read from cb2 image match");
+
+sub io_writer {
+ my ($what) = @_;
+ substr($work, $pos, $pos+length $what) = $what;
+ $pos += length $what;
+
+ 1;
+}
+
+my $did_close;
+sub io_close {
+ ++$did_close;
+}
+
+my $IO8 = Imager::io_new_cb(\&io_writer, undef, undef, \&io_close);
+ok(14, $IO8, "making writecb object");
+$pos = 0;
+$work = '';
+ok(15, Imager::i_writeppm_wiol($im, $IO8), "write to cb");
+# I originally compared this to $data, but that doesn't include the
+# Imager header
+ok(16, $work eq $data2, "write image match");
+ok(17, $did_close, "did close");
+
+# with a short buffer, no closer
+my $IO9 = Imager::io_new_cb(\&io_writer, undef, undef, undef, 1);
+ok(18, $IO9, "making short writecb object");
+$pos = 0;
+$work = '';
+ok(19, Imager::i_writeppm_wiol($im, $IO9), "write to short cb");
+ok(20, $work eq $data2, "short write image match");
+
+sub ok {
+ my ($num, $ok, $what) = @_;
+
+ if ($ok) {
+ print "ok $num # $what\n";
+ }
+ else {
+ print "not ok $num # $what\n";
+ }
+ $ok;
+}
View
3 t/t105gif.t
@@ -168,7 +168,9 @@ if (!i_has_format("gif")) {
close FH;
print "ok 13\n";
+ my $can_write_callback = 0;
if ($gifver >= 4.0) {
+ ++$can_write_callback;
unless (fork) {
# this can SIGSEGV with some versions of giflib
open FH, ">testout/t105_anim_cb.gif" or die $!;
@@ -188,6 +190,7 @@ if (!i_has_format("gif")) {
exit;
}
if (wait > 0 && $?) {
+ $can_write_callback = 0;
print "not ok 14 # you probably need to patch giflib\n";
print <<EOS;
#--- egif_lib.c 2000/12/11 07:33:12 1.1
View
129 t/t106tiff.t
@@ -1,5 +1,5 @@
#!perl -w
-print "1..43\n";
+print "1..69\n";
use Imager qw(:all);
$^W=1; # warnings during command-line tests
$|=1; # give us some progress in the test harness
@@ -24,7 +24,7 @@ i_box_filled($timg, 2, 2, 18, 18, $trans);
my $test_num;
if (!i_has_format("tiff")) {
- for (1..43) {
+ for (1..69) {
print "ok $_ # skip no tiff support\n";
}
} else {
@@ -206,6 +206,131 @@ if (!i_has_format("tiff")) {
$diff = i_img_diff($img4->{IMG}, $cmp4->{IMG});
print "# diff $diff\n";
ok($diff == 0, "written image doesn't match read");
+
+ my $work;
+ my $seekpos;
+ sub io_writer {
+ my ($what) = @_;
+ if ($seekpos > length $work) {
+ $work .= "\0" x ($seekpos - length $work);
+ }
+ substr($work, $seekpos, length $what) = $what;
+ $seekpos += length $what;
+
+ 1;
+ }
+ sub io_reader {
+ my ($size, $maxread) = @_;
+ #print "io_reader($size, $maxread) pos $seekpos\n";
+ my $out = substr($work, $seekpos, $maxread);
+ $seekpos += length $out;
+ $out;
+ }
+ sub io_reader2 {
+ my ($size, $maxread) = @_;
+ #print "io_reader2($size, $maxread) pos $seekpos\n";
+ my $out = substr($work, $seekpos, $size);
+ $seekpos += length $out;
+ $out;
+ }
+ use IO::Seekable;
+ sub io_seeker {
+ my ($offset, $whence) = @_;
+ #print "io_seeker($offset, $whence)\n";
+ if ($whence == SEEK_SET) {
+ $seekpos = $offset;
+ }
+ elsif ($whence == SEEK_CUR) {
+ $seekpos += $offset;
+ }
+ else { # SEEK_END
+ $seekpos = length($work) + $offset;
+ }
+ #print "-> $seekpos\n";
+ $seekpos;
+ }
+ my $did_close;
+ sub io_closer {
+ ++$did_close;
+ }
+
+ # read via cb
+ $work = $tiffdata;
+ $seekpos = 0;
+ my $IO2 = Imager::io_new_cb(undef, \&io_reader, \&io_seeker, undef);
+ ok($IO2, "new readcb obj");
+ my $img5 = i_readtiff_wiol($IO2, -1);
+ ok($img5, "read via cb");
+ ok(i_img_diff($img5, $img) == 0, "read from cb diff");
+
+ # read via cb2
+ $work = $tiffdata;
+ $seekpos = 0;
+ my $IO3 = Imager::io_new_cb(undef, \&io_reader2, \&io_seeker, undef);
+ ok($IO3, "new readcb2 obj");
+ my $img6 = i_readtiff_wiol($IO3, -1);
+ ok($img6, "read via cb2");
+ ok(i_img_diff($img6, $img) == 0, "read from cb2 diff");
+
+ # write via cb
+ $work = '';
+ $seekpos = 0;
+ my $IO4 = Imager::io_new_cb(\&io_writer, \&io_reader, \&io_seeker,
+ \&io_closer);
+ ok($IO4, "new writecb obj");
+ ok(i_writetiff_wiol($img, $IO4), "write to cb");
+ ok($work eq $odata, "write cb match");
+ ok($did_close, "write cb did close");
+ open D1, ">d1.tiff" or die;
+ print D1 $work;
+ close D1;
+ open D2, ">d2.tiff" or die;
+ print D2 $tiffdata;
+ close D2;
+
+ # write via cb2
+ $work = '';
+ $seekpos = 0;
+ $did_close = 0;
+ my $IO5 = Imager::io_new_cb(\&io_writer, \&io_reader, \&io_seeker,
+ \&io_closer, 1);
+ ok($IO5, "new writecb obj 2");
+ ok(i_writetiff_wiol($img, $IO5), "write to cb2");
+ ok($work eq $odata, "write cb2 match");
+ ok($did_close, "write cb2 did close");
+
+ open D3, ">d3.tiff" or die;
+ print D3 $work;
+ close D3;
+
+ # multi-image write/read
+ my @imgs;
+ push(@imgs, map $ooim->copy(), 1..3);
+ for my $i (0..$#imgs) {
+ $imgs[$i]->addtag(name=>"tiff_pagename", value=>"Page ".($i+1));
+ }
+ my $rc = Imager->write_multi({file=>'testout/t106_multi.tif'}, @imgs);
+ ok($rc, "writing multiple images to tiff");
+ my @out = Imager->read_multi(file=>'testout/t106_multi.tif');
+ ok(@out == @imgs, "reading multiple images from tiff");
+ @out == @imgs or print "# ",scalar @out, " "