- 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:
i_writepng_wiol
i_readgif
+ i_readgif_wiol
i_readgif_callback
i_writegif
i_writegifmc
}
}
+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
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
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 );
$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}) ) {
return undef;
}
+ my ($fh, $fd);
if ($input{file}) {
$fh = new IO::File($input{file},"r");
if (!defined $fh) {
compress=>1,
wierdpack=>0,
fax_fine=>1, @_);
- my ($fh, $rc, $fd, $IO);
+ my $rc;
- my %iolready=( tiff=>1, raw=>1, png=>1, pnm=>1, bmp=>1, jpeg=>1, tga=>1 ); # this will be SO MUCH BETTER once they are all in there
+ my %iolready=( tiff=>1, raw=>1, png=>1, pnm=>1, bmp=>1, jpeg=>1, tga=>1,
+ gif=>1 ); # this will be SO MUCH BETTER once they are all in there
unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
- if (!$input{file} and !$input{'fd'} and !$input{'data'}) { $self->{ERRSTR}='file/fd/data parameter missing'; return undef; }
if (!$input{'type'} and $input{file}) {
$input{'type'}=$FORMATGUESS->($input{file});
}
if (!$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') {
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'}) {
${$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";
$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'
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 {
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 ();
}
=head1 SYNOPSIS
- use Imager qw(init);
+ use Imager;
- init();
$img = Imager->new();
$img->open(file=>'image.ppm',type=>'pnm')
|| print "failed: ",$img->{ERRSTR},"\n";
=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
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;
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)
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)
Imager::ImgRaw im
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
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
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(...)
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
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.
- 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
myfree(packed);
myfree(line);
+ ig->closecb(ig);
+
return 1;
}
myfree(packed);
myfree(line);
+ ig->closecb(ig);
+
return 1;
}
}
myfree(line);
+ ig->closecb(ig);
+
return 1;
}
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) {
}
myfree(samples);
+ ig->closecb(ig);
+
return 1;
}
#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 */
/*
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;
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)
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)
/* 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));
#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();
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)
#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 */
#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 */
=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
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) );
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) );
/*
=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);
}
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
*/
/*
-=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);
}
/*
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;
+ }
}
}
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)
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)
}
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;
myfree(ieb);
}
break;
+ default:
+ break;
}
myfree(ig);
}
/* 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[];
readl readcb;
writel writecb;
seekl seekcb;
+ closel closecb;
+ destroyl destroycb;
} io_cb;
typedef union {
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);
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);
jpeg_destroy_compress(&cinfo);
+ ig->closecb(ig);
+
return(1);
}
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.)
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ ig->closecb(ig);
+
return(1);
}
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);
}
}
}
}
+
+ ig->closecb(ig);
+
return(1);
}
-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;
$im = Imager::i_readpnm_wiol($IO3, -1);
print "ok 3\n";
-
+undef $IO3;
open(FH, "<testimg/penguin-base.ppm") or die $!;
binmode(FH);
$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;
+}
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 $!;
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
#!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
my $test_num;
if (!i_has_format("tiff")) {
- for (1..43) {
+ for (1..69) {
print "ok $_ # skip no tiff support\n";
}
} else {
$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, " ",Imager->errstr,"\n";
+ for my $i (0..$#imgs) {
+ ok(i_img_diff($imgs[$i]{IMG}, $out[$i]{IMG}) == 0,
+ "comparing image $i");
+ my ($tag) = $out[$i]->tags(name=>'tiff_pagename');
+ ok($tag eq "Page ".($i+1),
+ "tag doesn't match original image");
+ }
+
+ # multi-image fax files
+ ok(Imager->write_multi({file=>'testout/t106_faxmulti.tiff', class=>'fax'},
+ $oofim, $oofim), "write multi fax image");
+ @imgs = Imager->read_multi(file=>'testout/t106_faxmulti.tiff');
+ ok(@imgs == 2, "reading multipage fax");
+ ok(Imager::i_img_diff($imgs[0]{IMG}, $oofim->{IMG}) == 0,
+ "compare first fax image");
+ ok(Imager::i_img_diff($imgs[1]{IMG}, $oofim->{IMG}) == 0,
+ "compare second fax image");
}
sub ok {
my $ugly = Imager::i_tt_new("./fontfiles/ImUgly.ttf");
i_tt_text($ugly, $backgr,100, 50, $bgcolor, 14, 'g%g', 3, 1);
i_tt_text($ugly, $backgr,150, 50, $bgcolor, 14, 'delta', 5, 1);
+i_tt_text($ttraw, $backgr, 20, 10, $bgcolor, 14, 'abcdefghijklmnopqrstuvwxyz{|}', 29, 1);
+i_tt_text($ttraw, $backgr, 20, 30, $bgcolor, 14, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 26, 1);
open(FH,">testout/t35ttfont2.ppm") || die "cannot open testout/t35ttfont.ppm\n";
-# Before `make install' is performed this script should be runnable with
-# `make test'. After `make install' it should work as `perl test.pl'
-
+#!perl -w
######################### We start with some black magic to print on failure.
-# Change 1..1 below to 1..last_test_to_print .
-# (It may become useful if the test is moved to ./t subdirectory.)
+# this used to do the check for the load of Imager, but I want to be able
+# to count tests, which means I need to load Imager first
+# since many of the early tests already do this, we don't really need to
-BEGIN { $| = 1; print "1..2\n"; }
-END {print "not ok 1\n" unless $loaded;}
+use strict;
use Imager;
+use IO::Seekable;
-$loaded = 1;
+Imager::init("log"=>"testout/t50basicoo.log");
-print "ok 1\n";
+# single image/file types
+my @types = qw( jpeg png raw ppm gif tiff bmp tga );
-Imager::init("log"=>"testout/t50basicoo.log");
+# multiple image/file formats
+my @mtypes = qw(tiff gif);
+
+my %hsh=%Imager::formats;
+
+my $test_num = 0;
+my $count;
+for my $type (@types) {
+ $count += 31 if $hsh{$type};
+}
+for my $type (@mtypes) {
+ $count += 7 if $hsh{$type};
+}
-%hsh=%Imager::formats;
+print "1..$count\n";
print "# avaliable formats:\n";
for(keys %hsh) { print "# $_\n"; }
#print Dumper(\%hsh);
-$img = Imager->new();
-
-@types = qw( jpeg png raw ppm gif tiff );
+my $img = Imager->new();
+my %files;
@files{@types} = ({ file => "testout/t101.jpg" },
{ file => "testout/t102.png" },
- { file => "testout/t103.raw", xsize=>150, ysize=>150, type=>"raw" },
+ { file => "testout/t103.raw", xsize=>150, ysize=>150
+ #, type=>"raw" # TODO: was this there for a reason?
+ },
{ file => "testout/t104.ppm" },
{ file => "testout/t105.gif" },
- { file => "testout/t106.tiff" });
+ { file => "testout/t106.tiff" },
+ { file => "testout/t107_24bit.bmp" },
+ { file => "testout/t108_24bit.tga" }, );
+my %writeopts =
+ (
+ gif=> { make_colors=>'webmap', translate=>'closest', gifquant=>'gen' },
+ );
-for $type (@types) {
+for my $type (@types) {
next unless $hsh{$type};
+ print "# type $type\n";
my %opts = %{$files{$type}};
my @a = map { "$_=>${opts{$_}}" } keys %opts;
print "#opening Format: $type, options: @a\n";
- $img->read( %opts ) or die "failed: ",$img->errstr,"\n";
+ ok($img->read( %opts ), "reading from file", $img);
+ #or die "failed: ",$img->errstr,"\n";
+
+ my %mopts = %opts;
+ delete $mopts{file};
+
+ # read from a file handle
+ my $fh = IO::File->new($opts{file}, "r");
+ if (ok($fh, "opening $opts{file}")) {
+ binmode $fh;
+ my $fhimg = Imager->new;
+ my $fhrc = $fhimg->read(fh=>$fh, %mopts);
+ if (ok(!$fhrc, "check that type is required")) {
+ ok ($fhimg->errstr =~ /type parameter missing/, "check for no type error");
+ }
+ else {
+ skip("previous test failed");
+ }
+ if (ok($fhimg->read(fh=>$fh, %mopts, type=>$type), "read from fh")) {
+ ok(Imager::i_img_diff($img->{IMG}, $fhimg->{IMG}) == 0,
+ "image comparison after fh read");
+ }
+ else {
+ skip("no image to compare");
+ }
+ ok($fh->seek(0, SEEK_SET), "seek after read");
+
+ # read from a fd
+ my $fdimg = Imager->new;
+ if (ok($fdimg->read(fd=>fileno($fh), %mopts, type=>$type),
+ "read from fd")) {
+ ok(Imager::i_img_diff($img->{IMG}, $fdimg->{IMG}) == 0,
+ "image comparistion after fd read");
+ }
+ else {
+ skip("no image to compare");
+ }
+ ok($fh->seek(0, SEEK_SET), "seek after fd read");
+ ok($fh->close, "close fh after reads");
+ }
+ else {
+ skip("couldn't open the damn file: $!", 7);
+ }
+
+ if ($type ne 'gif' || Imager::i_giflib_version() >= 4) {
+ # read from a memory buffer
+ open DATA, "< $opts{file}"
+ or die "Cannot open $opts{file}: $!";
+ binmode DATA;
+ my $data = do { local $/; <DATA> };
+ close DATA;
+ my $bimg = Imager->new;
+
+ if (ok($bimg->read(data=>$data, %mopts, type=>$type), "read from buffer",
+ $img)) {
+ ok(Imager::i_img_diff($img->{IMG}, $bimg->{IMG}) == 0,
+ "comparing buffer read image");
+ }
+ else {
+ skip("nothing to compare");
+ }
+
+ # read from callbacks, both with minimum and maximum reads
+ my $buf = $data;
+ my $seekpos = 0;
+ my $reader_min =
+ sub {
+ my ($size, $maxread) = @_;
+ my $out = substr($buf, $seekpos, $size);
+ $seekpos += length $out;
+ $out;
+ };
+ my $reader_max =
+ sub {
+ my ($size, $maxread) = @_;
+ my $out = substr($buf, $seekpos, $maxread);
+ $seekpos += length $out;
+ $out;
+ };
+ my $seeker =
+ sub {
+ 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($buf) + $offset;
+ }
+ #print "-> $seekpos\n";
+ $seekpos;
+ };
+ my $cbimg = Imager->new;
+ ok($cbimg->read(callback=>$reader_min, seekcb=>$seeker, type=>$type, %mopts),
+ "read from callback min", $cbimg);
+ ok(Imager::i_img_diff($cbimg->{IMG}, $img->{IMG}) == 0,
+ "comparing mincb image");
+ $seekpos = 0;
+ ok($cbimg->read(callback=>$reader_max, seekcb=>$seeker, type=>$type, %mopts),
+ "read from callback max", $cbimg);
+ ok(Imager::i_img_diff($cbimg->{IMG}, $img->{IMG}) == 0,
+ "comparing maxcb image");
+ }
+ else {
+ skip("giflib < 4 doesn't support callbacks", 6);
+ }
}
-$img2 = $img->crop(width=>50, height=>50);
+for my $type (@types) {
+ next unless $hsh{$type};
+
+ print "# write tests for $type\n";
+ # test writes
+ next unless $hsh{$type};
+ my $file = "testout/t50out.$type";
+ my $wimg = Imager->new;
+ # if this doesn't work, we're so screwed up anyway
+
+ ok($wimg->read(file=>"testout/t104.ppm"),
+ "cannot read base file", $wimg);
+
+ # first to a file
+ print "# writing $type to a file\n";
+ my %extraopts;
+ %extraopts = %{$writeopts{$type}} if $writeopts{$type};
+ ok($wimg->write(file=>$file, %extraopts),
+ "writing $type to a file $file", $wimg);
+
+ print "# writing $type to a FH\n";
+ # to a FH
+ my $fh = IO::File->new($file, "w+")
+ or die "Could not create $file: $!";
+ binmode $fh;
+ ok($wimg->write(fh=>$fh, %extraopts, type=>$type),
+ "writing $type to a FH", $wimg);
+ ok($fh->seek(0, SEEK_END) > 0,
+ "seek after writing $type to a FH");
+ ok(print($fh "SUFFIX\n"),
+ "write to FH after writing $type");
+ ok($fh->close, "closing FH after writing $type");
+
+ if ($type ne 'gif' || Imager::i_giflib_version() >= 4) {
+ if (ok(open(DATA, "< $file"), "opening data source")) {
+ binmode DATA;
+ my $data = do { local $/; <DATA> };
+ close DATA;
+
+ # writing to a buffer
+ print "# writing $type to a buffer\n";
+ my $buf = '';
+ ok($wimg->write(data=>\$buf, %extraopts, type=>$type),
+ "writing $type to a buffer", $wimg);
+ $buf .= "SUFFIX\n";
+ open DATA, "> testout/t50_buf.$type"
+ or die "Cannot create $type buffer file: $!";
+ binmode DATA;
+ print DATA $buf;
+ close DATA;
+ ok($data eq $buf, "comparing file data to buffer");
+
+ $buf = '';
+ my $seekpos = 0;
+ my $did_close;
+ my $writer =
+ sub {
+ my ($what) = @_;
+ if ($seekpos > length $buf) {
+ $buf .= "\0" x ($seekpos - length $buf);
+ }
+ substr($buf, $seekpos, length $what) = $what;
+ $seekpos += length $what;
+ $did_close = 0; # the close must be last
+ 1;
+ };
+ my $reader_min =
+ sub {
+ my ($size, $maxread) = @_;
+ my $out = substr($buf, $seekpos, $size);
+ $seekpos += length $out;
+ $out;
+ };
+ my $reader_max =
+ sub {
+ my ($size, $maxread) = @_;
+ my $out = substr($buf, $seekpos, $maxread);
+ $seekpos += length $out;
+ $out;
+ };
+ use IO::Seekable;
+ my $seeker =
+ sub {
+ 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($buf) + $offset;
+ }
+ #print "-> $seekpos\n";
+ $seekpos;
+ };
+
+ my $closer = sub { ++$did_close; };
+
+ print "# writing $type via callbacks (mb=1)\n";
+ ok($wimg->write(writecb=>$writer, seekcb=>$seeker, closecb=>$closer,
+ readcb=>$reader_min,
+ %extraopts, type=>$type, maxbuffer=>1),
+ "writing $type to callback (mb=1)", $wimg);
+
+ ok($did_close, "checking closecb called");
+ $buf .= "SUFFIX\n";
+ ok($data eq $buf, "comparing callback output to file data");
+ print "# writing $type via callbacks (no mb)\n";
+ $buf = '';
+ $did_close = 0;
+ $seekpos = 0;
+ # we don't use the closecb here - used to make sure we don't get
+ # a warning/error on an attempt to call an undef close sub
+ ok($wimg->write(writecb=>$writer, seekcb=>$seeker, readcb=>$reader_min,
+ %extraopts, type=>$type),
+ "writing $type to callback (no mb)", $wimg);
+ $buf .= "SUFFIX\n";
+ ok($data eq $buf, "comparing callback output to file data");
+ }
+ else {
+ skip("couldn't open data source", 7);
+ }
+ }
+ else {
+ skip("giflib < 4 doesn't support callbacks", 8);
+ }
+}
+
+my $img2 = $img->crop(width=>50, height=>50);
$img2 -> write(file=> 'testout/t50.ppm', type=>'pnm');
undef($img);
+# multi image/file tests
+print "# multi-image write tests\n";
+for my $type (@mtypes) {
+ next unless $hsh{$type};
+ print "# $type\n";
+
+ my $file = "testout/t50out.$type";
+ my $wimg = Imager->new;
+
+ # if this doesn't work, we're so screwed up anyway
+ ok($wimg->read(file=>"testout/t50out.$type"),
+ "reading base file", $wimg);
+
+ ok(my $wimg2 = $wimg->copy, "copying base image", $wimg);
+ ok($wimg2->flip(dir=>'h'), "flipping base image", $wimg2);
+
+ my @out = ($wimg, $wimg2);
+ my %extraopts;
+ %extraopts = %{$writeopts{$type}} if $writeopts{$type};
+ ok(Imager->write_multi({ file=>"testout/t50_multi.$type", %extraopts },
+ @out),
+ "writing multiple to a file", "Imager");
+
+ # make sure we get the same back
+ my @images = Imager->read_multi(file=>"testout/t50_multi.$type");
+ if (ok(@images == @out, "checking read image count")) {
+ for my $i (0 .. $#out) {
+ my $diff = Imager::i_img_diff($out[$i]{IMG}, $images[$i]{IMG});
+ print "# diff $diff\n";
+ ok($diff == 0, "comparing image $i");
+ }
+ }
+ else {
+ skip("wrong number of images read", 2);
+ }
+}
+
+
Imager::malloc_state();
-print "ok 2\n";
+#print "ok 2\n";
+
+sub ok {
+ my ($ok, $msg, $img) = @_;
+
+ ++$test_num;
+ if ($ok) {
+ print "ok $test_num # $msg\n";
+ }
+ else {
+ my $err;
+ $err = $img->errstr if $img;
+ # VMS (if we ever support it) wants the whole line in one print
+ my $line = "not ok $test_num # line ".(caller)[2].": $msg";
+ $line .= ": $err" if $err;
+ print $line, "\n";
+ }
+
+ $ok;
+}
+
+sub skip {
+ my ($why, $skipcount) = @_;
+
+ $skipcount ||= 1;
+ for (1.. $skipcount) {
+ ++$test_num;
+ print "ok $test_num # skipped $why\n";
+ }
+}
myfree(buf);
myfree(vals);
}
+
+ ig->closecb(ig);
+
return 1;
}
return (toff_t) ig->seekcb(ig, o, w);
}
-
-/*
-=item i_readtiff_wiol(ig, length)
-
-Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
-
- ig - io_glue object
- length - maximum length to read from data source, before closing it
-
-=cut
-*/
-
-i_img*
-i_readtiff_wiol(io_glue *ig, int length) {
+static i_img *read_one_tiff(TIFF *tif) {
i_img *im;
uint32 width, height;
uint16 channels;
uint32* raster = NULL;
int tiled, error;
- TIFF* tif;
float xres, yres;
uint16 resunit;
int gotXres, gotYres;
uint16 bits_per_sample;
int i;
int ch;
- TIFFErrorHandler old_handler;
-
- i_clear_error();
- old_handler = TIFFSetErrorHandler(error_handler);
error = 0;
- /* Add code to get the filename info from the iolayer */
- /* Also add code to check for mmapped code */
-
- io_glue_commit_types(ig);
- mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
-
- tif = TIFFClientOpen("(Iolayer)",
- "rm",
- (thandle_t) ig,
- (TIFFReadWriteProc) ig->readcb,
- (TIFFReadWriteProc) ig->writecb,
- (TIFFSeekProc) comp_seek,
- (TIFFCloseProc) ig->closecb,
- (TIFFSizeProc) ig->sizecb,
- (TIFFMapFileProc) NULL,
- (TIFFUnmapFileProc) NULL);
-
- if (!tif) {
- mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
- i_push_error(0, "opening file");
- TIFFSetErrorHandler(old_handler);
- return NULL;
- }
-
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &channels);
if (!TIFFGetField(tif, TIFFTAG_COLORMAP, maps+0, maps+1, maps+2)) {
i_push_error(0, "Cannot get colormap for paletted image");
- TIFFSetErrorHandler(old_handler);
i_img_destroy(im);
- TIFFClose(tif);
return NULL;
}
buffer = (unsigned char *)_TIFFmalloc(width+2);
if (!buffer) {
i_push_error(0, "out of memory");
- TIFFSetErrorHandler(old_handler);
i_img_destroy(im);
- TIFFClose(tif);
return NULL;
}
row = 0;
if (!raster) {
i_img_destroy(im);
i_push_error(0, "No space for raster buffer");
- TIFFSetErrorHandler(old_handler);
- TIFFClose(tif);
return NULL;
}
if (!raster) {
i_img_destroy(im);
i_push_error(0, "No space for raster buffer");
- TIFFSetErrorHandler(old_handler);
- TIFFClose(tif);
return NULL;
}
}
if (raster)
_TIFFfree( raster );
+
+ return im;
+}
+
+/*
+=item i_readtiff_wiol(im, ig)
+
+=cut
+*/
+i_img*
+i_readtiff_wiol(io_glue *ig, int length) {
+ TIFF* tif;
+ TIFFErrorHandler old_handler;
+ i_img *im;
+
+ i_clear_error();
+ old_handler = TIFFSetErrorHandler(error_handler);
+
+ /* Add code to get the filename info from the iolayer */
+ /* Also add code to check for mmapped code */
+
+ io_glue_commit_types(ig);
+ mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
+
+ tif = TIFFClientOpen("(Iolayer)",
+ "rm",
+ (thandle_t) ig,
+ (TIFFReadWriteProc) ig->readcb,
+ (TIFFReadWriteProc) ig->writecb,
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ (TIFFSizeProc) ig->sizecb,
+ (TIFFMapFileProc) NULL,
+ (TIFFUnmapFileProc) NULL);
+
+ if (!tif) {
+ mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
+ i_push_error(0, "opening file");
+ TIFFSetErrorHandler(old_handler);
+ return NULL;
+ }
+
+ im = read_one_tiff(tif);
+
if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
TIFFSetErrorHandler(old_handler);
TIFFClose(tif);
return im;
}
+/*
+=item i_readtiff_multi_wiol(ig, length, *count)
+Reads multiple images from a TIFF.
-/*
-=item i_writetif_wiol(im, ig)
+=cut
+*/
+i_img**
+i_readtiff_multi_wiol(io_glue *ig, int length, int *count) {
+ TIFF* tif;
+ TIFFErrorHandler old_handler;
+ i_img **results = NULL;
+ int result_alloc = 0;
+ int dirnum = 0;
-Stores an image in the iolayer object.
+ i_clear_error();
+ old_handler = TIFFSetErrorHandler(error_handler);
- im - image object to write out
- ig - io_object that defines source to write to
+ /* Add code to get the filename info from the iolayer */
+ /* Also add code to check for mmapped code */
-=cut
-*/
+ io_glue_commit_types(ig);
+ mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
+
+ tif = TIFFClientOpen("(Iolayer)",
+ "rm",
+ (thandle_t) ig,
+ (TIFFReadWriteProc) ig->readcb,
+ (TIFFReadWriteProc) ig->writecb,
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ (TIFFSizeProc) ig->sizecb,
+ (TIFFMapFileProc) NULL,
+ (TIFFUnmapFileProc) NULL);
+
+ if (!tif) {
+ mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
+ i_push_error(0, "opening file");
+ TIFFSetErrorHandler(old_handler);
+ return NULL;
+ }
-/* FIXME: Add an options array in here soonish */
+ *count = 0;
+ do {
+ i_img *im = read_one_tiff(tif);
+ if (!im)
+ break;
+ if (++*count > result_alloc) {
+ if (result_alloc == 0) {
+ result_alloc = 5;
+ results = mymalloc(result_alloc * sizeof(i_img *));
+ }
+ else {
+ i_img **newresults;
+ result_alloc *= 2;
+ newresults = myrealloc(results, result_alloc * sizeof(i_img *));
+ }
+ }
+ results[*count-1] = im;
+ } while (TIFFSetDirectory(tif, ++dirnum));
+
+ TIFFSetErrorHandler(old_handler);
+ TIFFClose(tif);
+ return results;
+}
undef_int
-i_writetiff_wiol(i_img *im, io_glue *ig) {
+i_writetiff_low_faxable(TIFF *tif, i_img *im, int fine) {
+ uint32 width, height;
+ unsigned char *linebuf = NULL;
+ uint32 y;
+ int rc;
+ uint32 x;
+ int luma_mask;
+ uint32 rowsperstrip;
+ float vres = fine ? 196 : 98;
+ int luma_chan;
+
+ width = im->xsize;
+ height = im->ysize;
+
+ switch (im->channels) {
+ case 1:
+ case 2:
+ luma_chan = 0;
+ break;
+ case 3:
+ case 4:
+ luma_chan = 1;
+ break;
+ default:
+ /* This means a colorspace we don't handle yet */
+ mm_log((1, "i_writetiff_wiol_faxable: don't handle %d channel images.\n", im->channels));
+ return 0;
+ }
+
+ /* Add code to get the filename info from the iolayer */
+ /* Also add code to check for mmapped code */
+
+
+ mm_log((1, "i_writetiff_wiol_faxable: width=%d, height=%d, channels=%d\n", width, height, im->channels));
+
+ if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) )
+ { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField width=%d failed\n", width)); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) )
+ { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField length=%d failed\n", height)); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1))
+ { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField samplesperpixel=1 failed\n")); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT))
+ { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Orientation=topleft\n")); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 1) )
+ { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField bitpersample=1\n")); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG))
+ { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField planarconfig\n")); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK))
+ { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField photometric=%d\n", PHOTOMETRIC_MINISBLACK)); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, 3))
+ { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField compression=3\n")); return 0; }
+
+ linebuf = (unsigned char *)_TIFFmalloc( TIFFScanlineSize(tif) );
+
+ if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) {
+ mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField rowsperstrip=-1\n")); return 0; }
+
+ TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+ TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
+
+ mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
+ mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
+ mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
+
+ if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)204))
+ { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Xresolution=204\n")); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, vres))
+ { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Yresolution=196\n")); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) {
+ mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0;
+ }
+
+ if (!save_tiff_tags(tif, im)) {
+ return 0;
+ }
+
+ for (y=0; y<height; y++) {
+ int linebufpos=0;
+ for(x=0; x<width; x+=8) {
+ int bits;
+ int bitpos;
+ i_sample_t luma[8];
+ uint8 bitval = 128;
+ linebuf[linebufpos]=0;
+ bits = width-x; if(bits>8) bits=8;
+ i_gsamp(im, x, x+8, y, luma, &luma_chan, 1);
+ for(bitpos=0;bitpos<bits;bitpos++) {
+ linebuf[linebufpos] |= ((luma[bitpos]>=128)?bitval:0);
+ bitval >>= 1;
+ }
+ linebufpos++;
+ }
+ if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
+ mm_log((1, "i_writetiff_wiol_faxable: TIFFWriteScanline failed.\n"));
+ break;
+ }
+ }
+ if (linebuf) _TIFFfree(linebuf);
+
+ return 1;
+}
+
+undef_int
+i_writetiff_low(TIFF *tif, i_img *im) {
uint32 width, height;
uint16 channels;
uint16 predictor = 0;
tsize_t linebytes;
int ch, ci, rc;
uint32 x;
- TIFF* tif;
int got_xres, got_yres, got_aspectonly, aspect_only, resunit;
double xres, yres;
uint16 bitspersample = 8;
uint16 samplesperpixel;
uint16 *colors = NULL;
- char *cc = mymalloc( 123 );
- myfree(cc);
-
-
width = im->xsize;
height = im->ysize;
channels = im->channels;
/* Add code to get the filename info from the iolayer */
/* Also add code to check for mmapped code */
- io_glue_commit_types(ig);
- mm_log((1, "i_writetiff_wiol(im 0x%p, ig 0x%p)\n", im, ig));
+ /*io_glue_commit_types(ig);*/
+ /*mm_log((1, "i_writetiff_wiol(im 0x%p, ig 0x%p)\n", im, ig));*/
- /* FIXME: Enable the mmap interface */
+ mm_log((1, "i_writetiff_low: width=%d, height=%d, channels=%d\n", width, height, channels));
- tif = TIFFClientOpen("No name",
- "wm",
- (thandle_t) ig,
- (TIFFReadWriteProc) ig->readcb,
- (TIFFReadWriteProc) ig->writecb,
- (TIFFSeekProc) comp_seek,
- (TIFFCloseProc) ig->closecb,
- (TIFFSizeProc) ig->sizecb,
- (TIFFMapFileProc) NULL,
- (TIFFUnmapFileProc) NULL);
-
-
-
- if (!tif) {
- mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
- return 0;
+ if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) ) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField width=%d failed\n", width));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) ) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n"));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n"));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField photometric=%d\n", photometric));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, compression)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression));
+ return 0;
}
-
- mm_log((1, "i_writetiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
-
- if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField width=%d failed\n", width)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) { mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric)) { mm_log((1, "i_writetiff_wiol: TIFFSetField photometric=%d\n", photometric)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, compression)) { mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression)); return 0; }
samplesperpixel = channels;
if (photometric == PHOTOMETRIC_PALETTE) {
uint16 *out[3];
switch (compression) {
case COMPRESSION_JPEG:
mm_log((1, "i_writetiff_wiol: jpeg compression\n"));
- if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField jpegquality=%d\n", quality)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, jpegcolormode)) { mm_log((1, "i_writetiff_wiol: TIFFSetField jpegcolormode=%d\n", jpegcolormode)); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality) ) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField jpegquality=%d\n", quality));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, jpegcolormode)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField jpegcolormode=%d\n", jpegcolormode));
+ return 0;
+ }
break;
case COMPRESSION_LZW:
mm_log((1, "i_writetiff_wiol: lzw compression\n"));
case COMPRESSION_DEFLATE:
mm_log((1, "i_writetiff_wiol: deflate compression\n"));
if (predictor != 0)
- if (!TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor)) { mm_log((1, "i_writetiff_wiol: TIFFSetField predictor=%d\n", predictor)); return 0; }
+ if (!TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField predictor=%d\n", predictor));
+ return 0;
+ }
break;
case COMPRESSION_PACKBITS:
mm_log((1, "i_writetiff_wiol: packbits compression\n"));
}
}
if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres)) {
- TIFFClose(tif);
- i_img_destroy(im);
i_push_error(0, "cannot set TIFFTAG_XRESOLUTION tag");
return 0;
}
if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres)) {
- TIFFClose(tif);
- i_img_destroy(im);
i_push_error(0, "cannot set TIFFTAG_YRESOLUTION tag");
return 0;
}
if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16)resunit)) {
- TIFFClose(tif);
- i_img_destroy(im);
i_push_error(0, "cannot set TIFFTAG_RESOLUTIONUNIT tag");
return 0;
}
}
if (!save_tiff_tags(tif, im)) {
- TIFFClose(tif);
return 0;
}
pack_4bit_hl(linebuf, width);
if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
- break;
+ if (linebuf) _TIFFfree(linebuf);
+ if (colors) _TIFFfree(colors);
+ return 0;
}
}
}
}
if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
- break;
+ if (linebuf) _TIFFfree(linebuf);
+ if (colors) _TIFFfree(colors);
+ return 0;
}
}
}
- (void) TIFFClose(tif);
if (linebuf) _TIFFfree(linebuf);
if (colors) _TIFFfree(colors);
return 1;
}
/*
-=item i_writetiff_wiol_faxable(i_img *, io_glue *)
+=item i_writetiff_multi_wiol(ig, imgs, count, fine_mode)
-Stores an image in the iolayer object in faxable tiff format.
+Stores an image in the iolayer object.
- im - image object to write out
ig - io_object that defines source to write to
+ imgs,count - the images to write
-Note, this may be rewritten to use to simply be a call to a
-lower-level function that gives more options for writing tiff at some
-point.
-
-=cut
+=cut
*/
undef_int
-i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
- uint32 width, height;
- unsigned char *linebuf = NULL;
- uint32 y;
- int rc;
- uint32 x;
+i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
TIFF* tif;
- int luma_mask;
- uint32 rowsperstrip;
- float vres = fine ? 196 : 98;
- int luma_chan;
+ int i;
- width = im->xsize;
- height = im->ysize;
+ io_glue_commit_types(ig);
+ i_clear_error();
+ mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n",
+ ig, imgs, count));
- switch (im->channels) {
- case 1:
- case 2:
- luma_chan = 0;
- break;
- case 3:
- case 4:
- luma_chan = 1;
- break;
- default:
- /* This means a colorspace we don't handle yet */
- mm_log((1, "i_writetiff_wiol_faxable: don't handle %d channel images.\n", im->channels));
+ /* FIXME: Enable the mmap interface */
+
+ tif = TIFFClientOpen("No name",
+ "wm",
+ (thandle_t) ig,
+ (TIFFReadWriteProc) ig->readcb,
+ (TIFFReadWriteProc) ig->writecb,
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ (TIFFSizeProc) ig->sizecb,
+ (TIFFMapFileProc) NULL,
+ (TIFFUnmapFileProc) NULL);
+
+
+
+ if (!tif) {
+ mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n"));
return 0;
}
- /* Add code to get the filename info from the iolayer */
- /* Also add code to check for mmapped code */
+ for (i = 0; i < count; ++i) {
+ if (!i_writetiff_low(tif, imgs[i])) {
+ TIFFClose(tif);
+ return 0;
+ }
+
+ if (!TIFFWriteDirectory(tif)) {
+ i_push_error(0, "Cannot write TIFF directory");
+ TIFFClose(tif);
+ return 0;
+ }
+ }
+
+ (void) TIFFClose(tif);
+ return 1;
+}
+
+/*
+=item i_writetiff_multi_wiol_faxable(ig, imgs, count, fine_mode)
+
+Stores an image in the iolayer object.
+
+ ig - io_object that defines source to write to
+ imgs,count - the images to write
+ fine_mode - select fine or normal mode fax images
+
+=cut
+*/
+
+
+undef_int
+i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
+ TIFF* tif;
+ int i;
io_glue_commit_types(ig);
- mm_log((1, "i_writetiff_wiol_faxable(im 0x%p, ig 0x%p)\n", im, ig));
+ i_clear_error();
+ mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n",
+ ig, imgs, count));
/* FIXME: Enable the mmap interface */
(TIFFSizeProc) ig->sizecb,
(TIFFMapFileProc) NULL,
(TIFFUnmapFileProc) NULL);
+
+
if (!tif) {
- mm_log((1, "i_writetiff_wiol_faxable: Unable to open tif file for writing\n"));
+ mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n"));
return 0;
}
- mm_log((1, "i_writetiff_wiol_faxable: width=%d, height=%d, channels=%d\n", width, height, im->channels));
-
- if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) )
- { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField width=%d failed\n", width)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) )
- { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField length=%d failed\n", height)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1))
- { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField samplesperpixel=1 failed\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT))
- { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Orientation=topleft\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 1) )
- { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField bitpersample=1\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG))
- { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField planarconfig\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK))
- { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField photometric=%d\n", PHOTOMETRIC_MINISBLACK)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, 3))
- { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField compression=3\n")); return 0; }
+ for (i = 0; i < count; ++i) {
+ if (!i_writetiff_low_faxable(tif, imgs[i], fine)) {
+ TIFFClose(tif);
+ return 0;
+ }
- linebuf = (unsigned char *)_TIFFmalloc( TIFFScanlineSize(tif) );
-
- if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) {
- mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField rowsperstrip=-1\n")); return 0; }
+ if (!TIFFWriteDirectory(tif)) {
+ i_push_error(0, "Cannot write TIFF directory");
+ TIFFClose(tif);
+ return 0;
+ }
+ }
- TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
- TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
+ (void) TIFFClose(tif);
+ return 1;
+}
- mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
- mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
- mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
+/*
+=item i_writetiff_wiol(im, ig)
- if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)204))
- { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Xresolution=204\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, vres))
- { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Yresolution=196\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) {
- mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0;
+Stores an image in the iolayer object.
+
+ im - image object to write out
+ ig - io_object that defines source to write to
+
+=cut
+*/
+undef_int
+i_writetiff_wiol(i_img *img, io_glue *ig) {
+ TIFF* tif;
+ int i;
+
+ io_glue_commit_types(ig);
+ i_clear_error();
+ mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", img, ig));
+
+ /* FIXME: Enable the mmap interface */
+
+ tif = TIFFClientOpen("No name",
+ "wm",
+ (thandle_t) ig,
+ (TIFFReadWriteProc) ig->readcb,
+ (TIFFReadWriteProc) ig->writecb,
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ (TIFFSizeProc) ig->sizecb,
+ (TIFFMapFileProc) NULL,
+ (TIFFUnmapFileProc) NULL);
+
+
+
+ if (!tif) {
+ mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
+ return 0;
}
- if (!save_tiff_tags(tif, im)) {
+ if (!i_writetiff_low(tif, img)) {
TIFFClose(tif);
return 0;
}
- for (y=0; y<height; y++) {
- int linebufpos=0;
- for(x=0; x<width; x+=8) {
- int bits;
- int bitpos;
- i_sample_t luma[8];
- uint8 bitval = 128;
- linebuf[linebufpos]=0;
- bits = width-x; if(bits>8) bits=8;
- i_gsamp(im, x, x+8, y, luma, &luma_chan, 1);
- for(bitpos=0;bitpos<bits;bitpos++) {
- linebuf[linebufpos] |= ((luma[bitpos]>=128)?bitval:0);
- bitval >>= 1;
- }
- linebufpos++;
- }
- if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
- mm_log((1, "i_writetiff_wiol_faxable: TIFFWriteScanline failed.\n"));
- break;
- }
+ (void) TIFFClose(tif);
+ return 1;
+}
+
+
+
+/*
+=item i_writetiff_wiol_faxable(i_img *, io_glue *)
+
+Stores an image in the iolayer object in faxable tiff format.
+
+ im - image object to write out
+ ig - io_object that defines source to write to
+
+Note, this may be rewritten to use to simply be a call to a
+lower-level function that gives more options for writing tiff at some
+point.
+
+=cut
+*/
+
+undef_int
+i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
+ TIFF* tif;
+ int i;
+
+ io_glue_commit_types(ig);
+ i_clear_error();
+ mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", im, ig));
+
+ /* FIXME: Enable the mmap interface */
+
+ tif = TIFFClientOpen("No name",
+ "wm",
+ (thandle_t) ig,
+ (TIFFReadWriteProc) ig->readcb,
+ (TIFFReadWriteProc) ig->writecb,
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ (TIFFSizeProc) ig->sizecb,
+ (TIFFMapFileProc) NULL,
+ (TIFFUnmapFileProc) NULL);
+
+
+
+ if (!tif) {
+ mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
+ return 0;
}
+
+ if (!i_writetiff_low_faxable(tif, im, fine)) {
+ TIFFClose(tif);
+ return 0;
+ }
+
(void) TIFFClose(tif);
- if (linebuf) _TIFFfree(linebuf);
return 1;
}
static int save_tiff_tags(TIFF *tif, i_img *im) {
int i;
-
+
for (i = 0; i < text_tag_count; ++i) {
int entry;
if (i_tags_find(&im->tags, text_tag_names[i].name, 0, &entry)) {
if (!TIFFSetField(tif, text_tag_names[i].tag,
- im->tags.tags[entry].data)) {
- i_push_errorf(0, "cannot save %s to TIFF", text_tag_names[i].name);
- return 0;
+ im->tags.tags[entry].data)) {
+ i_push_errorf(0, "cannot save %s to TIFF", text_tag_names[i].name);
+ return 0;
}
}
}
-
+
return 1;
}
+
/*
=item expand_4bit_hl(buf, count)