Imager 0.62 - not yet released
===========
+ - major TIFF support re-work
+ http://rt.cpan.org/Ticket/Display.html?id=20329
+
+ - added a C level image interface for accessing samples from 1-32
+ bits, exposed this at the perl level in getsamples()
+
+ - the conv filter now works at floating point precision for high bit
+ images
+
+ - added is_bilevel method to test whether an image should be written as
+ a bilevel image if the image format supports it.
+
+ - added -log-stderr as an Imager import list option
+
+ - added some important types to Imager::APIRef
+
+ - added test_image_double() to Imager::Test
+
Bug fixes:
- Imager::Fountain couldn't read GIMP gradient files with 10 or more
newcolour
NC
NF
+ NCF
);
@EXPORT=qw(
newcolor
NF
NC
+ NCF
)],
all => [@EXPORT_OK],
default => [qw(
# initlize Imager
# NOTE: this might be moved to an import override later on
-#sub import {
-# my $pack = shift;
-# (look through @_ for special tags, process, and remove them);
-# use Data::Dumper;
-# print Dumper($pack);
-# print Dumper(@_);
-#}
+sub import {
+ my $i = 1;
+ while ($i < @_) {
+ if ($_[$i] eq '-log-stderr') {
+ init_log(undef, 4);
+ splice(@_, $i, 1);
+ }
+ else {
+ ++$i;
+ }
+ }
+ goto &Exporter::import;
+}
sub init_log {
i_init_log($_[0],$_[1]);
$self->{IMG} and i_img_virtual($self->{IMG});
}
+sub is_bilevel {
+ my ($self) = @_;
+
+ $self->{IMG} or return;
+
+ return i_img_is_monochrome($self->{IMG});
+}
+
sub tags {
my ($self, %opts) = @_;
sub getsamples {
my $self = shift;
- my %opts = ( type => '8bit', x=>0, @_);
+ my %opts = ( type => '8bit', x=>0, offset => 0, @_);
defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
$opts{channels} = [ 0 .. $self->getchannels()-1 ];
}
- if ($opts{type} eq '8bit') {
- return i_gsamp($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
- $opts{y}, @{$opts{channels}});
- }
- elsif ($opts{type} eq 'float') {
- return i_gsampf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
- $opts{y}, @{$opts{channels}});
+ if ($opts{target}) {
+ my $target = $opts{target};
+ my $offset = $opts{offset};
+ if ($opts{type} eq '8bit') {
+ my @samples = i_gsamp($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
+ $opts{y}, @{$opts{channels}})
+ or return;
+ @{$target}{$offset .. $offset + @samples - 1} = @samples;
+ return scalar(@samples);
+ }
+ elsif ($opts{type} eq 'float') {
+ my @samples = i_gsampf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
+ $opts{y}, @{$opts{channels}});
+ @{$target}{$offset .. $offset + @samples - 1} = @samples;
+ return scalar(@samples);
+ }
+ elsif ($opts{type} =~ /^(\d+)bit$/) {
+ my $bits = $1;
+
+ my @data;
+ my $count = i_gsamp_bits($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
+ $opts{y}, $bits, $target,
+ $offset, @{$opts{channels}});
+ unless (defined $count) {
+ $self->_set_error(Imager->_error_as_msg);
+ return;
+ }
+
+ return $count;
+ }
+ else {
+ $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
+ return;
+ }
}
else {
- $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
+ if ($opts{type} eq '8bit') {
+ return i_gsamp($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
+ $opts{y}, @{$opts{channels}});
+ }
+ elsif ($opts{type} eq 'float') {
+ return i_gsampf($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
+ $opts{y}, @{$opts{channels}});
+ }
+ elsif ($opts{type} =~ /^(\d+)bit$/) {
+ my $bits = $1;
+
+ my @data;
+ i_gsamp_bits($self->{IMG}, $opts{x}, $opts{x}+$opts{width},
+ $opts{y}, $bits, \@data, 0, @{$opts{channels}})
+ or return;
+ return @data;
+ }
+ else {
+ $self->_set_error("invalid type parameter - must be '8bit' or 'float'");
+ return;
+ }
+ }
+}
+
+sub setsamples {
+ my $self = shift;
+ my %opts = ( x => 0, offset => 0, @_ );
+
+ unless ($self->{IMG}) {
+ $self->_set_error('setsamples: empty input image');
+ return;
+ }
+
+ unless(defined $opts{data} && ref $opts{data}) {
+ $self->_set_error('setsamples: data parameter missing or invalid');
return;
}
+
+ unless ($opts{channels}) {
+ $opts{channels} = [ 0 .. $self->getchannels()-1 ];
+ }
+
+ unless ($opts{type} && $opts{type} =~ /^(\d+)bit$/) {
+ $self->_set_error('setsamples: type parameter missing or invalid');
+ return;
+ }
+ my $bits = $1;
+
+ unless (defined $opts{width}) {
+ $opts{width} = $self->getwidth() - $opts{x};
+ }
+
+ my $count = i_psamp_bits($self->{IMG}, $opts{x}, $opts{y}, $bits,
+ $opts{channels}, $opts{data}, $opts{offset},
+ $opts{width});
+ unless (defined $count) {
+ $self->_set_error(Imager->_error_as_msg);
+ return;
+ }
+
+ return $count;
}
# make an identity matrix of the given size
sub newcolor { Imager::Color->new(@_); }
sub newfont { Imager::Font->new(@_); }
+sub NCF { Imager::Color::Float->new(@_) }
*NC=*newcolour=*newcolor;
*NF=*newfont;
init() - L<Imager::ImageTypes/init>
+is_bilevel - L<Imager::ImageTypes/is_bilevel>
+
line() - L<Imager::Draw/line>
load_plugin() - L<Imager::Filters/load_plugin>
NC() - L<Imager::Handy/NC>
+NCF() - L<Imager::Handy/NCF>
+
new() - L<Imager::ImageTypes/new>
newcolor() - L<Imager::Handy/newcolor>
setpixel() - L<Imager::Draw/setpixel>
+setsamples() - L<Imager::Draw/setsamples>
+
setscanline() - L<Imager::Draw/setscanline>
settag() - L<Imager::ImageTypes/settag>
int channels
void
-i_init_log(name,level)
- char* name
+i_init_log(name_sv,level)
+ SV* name_sv
int level
+ PREINIT:
+ const char *name = SvOK(name_sv) ? SvPV_nolen(name_sv) : NULL;
+ CODE:
+ i_init_log(name, level);
void
i_log_entry(string,level)
sv_2mortal(newSVpv((char *)im->idata, im->bytes))
: &PL_sv_undef);
+void
+i_img_is_monochrome(im)
+ Imager::ImgRaw im
+ PREINIT:
+ int zero_is_white;
+ int result;
+ PPCODE:
+ result = i_img_is_monochrome(im, &zero_is_white);
+ if (result) {
+ if (GIMME_V == G_ARRAY) {
+ EXTEND(SP, 2);
+ PUSHs(&PL_sv_yes);
+ PUSHs(sv_2mortal(newSViv(zero_is_white)));
+ }
+ else {
+ EXTEND(SP, 1);
+ PUSHs(&PL_sv_yes);
+ }
+ }
void
i_line(im,x1,y1,x2,y2,val,endp)
OUTPUT:
RETVAL
+const char *
+i_tiff_libversion()
+
+bool
+i_tiff_has_compression(name)
+ const char *name
#endif /* HAVE_LIBTIFF */
}
}
+undef_neg_int
+i_gsamp_bits(im, l, r, y, bits, target, offset, ...)
+ Imager::ImgRaw im
+ int l
+ int r
+ int y
+ int bits
+ AV *target
+ int offset
+ PREINIT:
+ int *chans;
+ int chan_count;
+ unsigned *data;
+ int count, i;
+ CODE:
+ i_clear_error();
+ if (items < 8)
+ croak("No channel numbers supplied to g_samp()");
+ if (l < r) {
+ chan_count = items - 7;
+ chans = mymalloc(sizeof(int) * chan_count);
+ for (i = 0; i < chan_count; ++i)
+ chans[i] = SvIV(ST(i+7));
+ data = mymalloc(sizeof(unsigned) * (r-l) * chan_count);
+ count = i_gsamp_bits(im, l, r, y, data, chans, chan_count, bits);
+ myfree(chans);
+ for (i = 0; i < count; ++i) {
+ av_store(target, i+offset, newSVuv(data[i]));
+ }
+ myfree(data);
+ RETVAL = count;
+ }
+ else {
+ RETVAL = 0;
+ }
+ OUTPUT:
+ RETVAL
+
+undef_neg_int
+i_psamp_bits(im, l, y, bits, channels_sv, data_av, data_offset = 0, pixel_count = -1)
+ Imager::ImgRaw im
+ int l
+ int y
+ int bits
+ SV *channels_sv
+ AV *data_av
+ int data_offset
+ int pixel_count
+ PREINIT:
+ int chan_count;
+ int *channels;
+ int data_count;
+ int data_used;
+ unsigned *data;
+ int i;
+ CODE:
+ i_clear_error();
+ if (SvOK(channels_sv)) {
+ AV *channels_av;
+ if (!SvROK(channels_sv) || SvTYPE(SvRV(channels_sv)) != SVt_PVAV) {
+ croak("channels is not an array ref");
+ }
+ channels_av = (AV *)SvRV(channels_sv);
+ chan_count = av_len(channels_av) + 1;
+ if (chan_count < 1) {
+ croak("i_psamp_bits: no channels provided");
+ }
+ channels = mymalloc(sizeof(int) * chan_count);
+ for (i = 0; i < chan_count; ++i)
+ channels[i] = SvIV(*av_fetch(channels_av, i, 0));
+ }
+ else {
+ chan_count = im->channels;
+ channels = NULL;
+ }
+
+ data_count = av_len(data_av) + 1;
+ if (data_offset < 0) {
+ croak("data_offset must by non-negative");
+ }
+ if (data_offset > data_count) {
+ croak("data_offset greater than number of samples supplied");
+ }
+ if (pixel_count == -1 ||
+ data_offset + pixel_count * chan_count > data_count) {
+ pixel_count = (data_count - data_offset) / chan_count;
+ }
+
+ data_used = pixel_count * chan_count;
+ data = mymalloc(sizeof(unsigned) * data_count);
+ for (i = 0; i < data_used; ++i)
+ data[i] = SvUV(*av_fetch(data_av, data_offset + i, 0));
+
+ RETVAL = i_psamp_bits(im, l, l + pixel_count, y, data, channels,
+ chan_count, bits);
+
+ if (data)
+ myfree(data);
+ if (channels)
+ myfree(channels);
+ OUTPUT:
+ RETVAL
Imager::ImgRaw
i_img_masked_new(targ, mask, x, y, w, h)
bigtest.perl Library selection tester
bmp.c Reading and writing Windows BMP files
color.c Color translation and handling
-conv.c
+conv.im
convert.c
doco.perl
datatypes.c
t/tr18561b.t
tags.c
testimg/209_yonge.jpg Regression test: #17981
+testimg/alpha.tif Alpha scaling test image
testimg/bad1oflow.bmp 1-bit/pixel, overflow integer on 32-bit machines
testimg/bad1wid0.bmp 1-bit/pixel, zero width
testimg/bad24comp.bmp 24-bit/pixel, bad compression
testimg/comp4.bmp Compressed 4-bit/pixel BMP
testimg/comp4.tif 4-bit/pixel paletted TIFF
testimg/comp4bad.tif corrupted 4-bit/pixel paletted TIFF
+testimg/comp4t.tif 4-bit/pixel paletted TIFF (tiled)
testimg/comp8.bmp Compressed 8-bit/pixel BMP
testimg/comp8.tif 8-bit/pixel paletted TIFF
testimg/exiftest.jpg Test image for EXIF parsing
testimg/expected.gif
testimg/gimpgrad A GIMP gradient file
+testimg/gralpha.tif Grey alpha test image
+testimg/grey16.tif 16-bit/sample greyscale TIFF
+testimg/grey32.tif 32-bit/sample greyscale+alpha TIFF
+testimg/imager.pbm Test bi-level
+testimg/imager.tif Test bi-level
testimg/junk.ppm
testimg/loccmap.gif
testimg/longid.tga Test TGA with a long id string
testimg/palette.png
testimg/palette_out.png
testimg/penguin-base.ppm
+testimg/pengtile.tif Tiled tiff image, same as penguin-base.ppm
testimg/pgm.pgm Simple pgm for testing the right sample is in the right place
+testimg/rgb16.tif 16-bit/sample RGB image - strips
+testimg/rgb16t.tif 16-bit/sample RGB image - tiled
testimg/scale.gif
testimg/scale.ppm
testimg/scalei.gif
testimg/scmyk.tif Simple CMYK TIFF image
testimg/scmyk.jpg Simple CMYK JPEG image
testimg/scmyka.tif CMYK with one alpha channel
+testimg/scmyka16.tif CMYK with one alpha channel (16-bit)
testimg/scmykaa.tif CMYK with 2 alpha channels
testimg/screen2.gif
testimg/screen3.gif
testimg/slab.tif Lab color image
testimg/srgb.tif Simple RGB image
testimg/srgba.tif RGB with one alpha
+testimg/srgba16.tif
+testimg/srgba32.tif
testimg/srgbaa.tif RGB with 2 alpha
testimg/test_gimp_pal A simple GIMP palette file
testimg/tiffwarn.tif Generates a warning while being read
have a problem that version of linungif (or later), let us know and
we'll look into it.
-Imager needs to have a libtiff version of at least 3.5.5. In the
-future we might consider supporting older libtiff versions. For
-now you can either configure Imager manually (by
-setting the IM_MANUAL environment variable to 1, in sh:
+Imager needs to have a libtiff version of at least 3.5.5, but you
+should use a later version since some noticable bugs have been fixed.
+
+For now you can either configure Imager manually (by setting the
+IM_MANUAL environment variable to 1, in sh:
$ IM_MANUAL=1 perl Makefile.PL
For 0.62:
-TIFF improvements (to be detailed) (#20329)
+TIFF improvements (to be detailed) (#20329) (done)
- read/write 16-bit RGB w/ and w/o alpha
- read 16-bit CMYK w/ and w/o alpha
- read/write 32-bit RGB w/ and w/o alpha
# look for files to parse
my $mani = maniread;
-my @files = grep /\.(c|im)$/, keys %$mani;
+my @files = grep /\.(c|im|h)$/, keys %$mani;
# scan each file for =item <func>\b
my $func;
sub make_func_list {
- my $funcs;
+ my @funcs = qw(i_img i_color i_fcolor i_fill_t mm_log);
open FUNCS, "< imexttypes.h"
or die "Cannot open imexttypes.h: $!\n";
my $in_struct;
+++ /dev/null
-#include "imager.h"
-
-/*
- General convolution for 2d decoupled filters
- end effects are acounted for by increasing
- scaling the result with the sum of used coefficients
-
- coeff: (float array) coefficients for filter
- len: length of filter.. number of coefficients
- note that this has to be an odd number
- (since the filter is even);
-*/
-
-void
-i_conv(i_img *im,const float *coeff,int len) {
- int i,l,c,ch,center;
- float pc;
- i_color rcolor;
- float res[11];
- i_img timg;
-
- mm_log((1,"i_conv(im %p, coeff %p, len %d)\n",im,coeff,len));
-
- i_img_empty_ch(&timg,im->xsize,im->ysize,im->channels);
-
- center=(len-1)/2;
-
- /* don't move the calculation of pc up here, it depends on which pixels
- are readable */
- for(l=0;l<im->ysize;l++) {
- for(i=0;i<im->xsize;i++) {
- pc=0.0;
- for(ch=0;ch<im->channels;ch++) res[ch]=0;
- for(c=0;c<len;c++)
- if (i_gpix(im,i+c-center,l,&rcolor)!=-1) {
- for(ch=0;ch<im->channels;ch++)
- res[ch]+=(float)(rcolor.channel[ch])*coeff[c];
- pc+=coeff[c];
- }
- for(ch=0;ch<im->channels;ch++) {
- double temp = res[ch]/pc;
- rcolor.channel[ch] =
- temp < 0 ? 0 : temp > 255 ? 255 : (unsigned char)temp;
- }
- i_ppix(&timg,i,l,&rcolor);
- }
- }
-
- for(l=0;l<im->xsize;l++)
- {
- for(i=0;i<im->ysize;i++)
- {
- pc=0.0;
- for(ch=0;ch<im->channels;ch++) res[ch]=0;
- for(c=0;c<len;c++)
- if (i_gpix(&timg,l,i+c-center,&rcolor)!=-1)
- {
- for(ch=0;ch<im->channels;ch++)
- res[ch]+=(float)(rcolor.channel[ch])*coeff[c];
- pc+=coeff[c];
- }
- for(ch=0;ch<im->channels;ch++) {
- double temp = res[ch]/pc;
- rcolor.channel[ch]=
- temp < 0 ? 0 : temp > 255 ? 255 : (unsigned char)temp;
- }
- i_ppix(im,l,i,&rcolor);
- }
- }
- i_img_exorcise(&timg);
-}
-
-
-
-
-
-
-
-
-
--- /dev/null
+#include "imager.h"
+
+/*
+ General convolution for 2d decoupled filters
+ end effects are acounted for by increasing
+ scaling the result with the sum of used coefficients
+
+ coeff: (float array) coefficients for filter
+ len: length of filter.. number of coefficients
+ note that this has to be an odd number
+ (since the filter is even);
+*/
+
+void
+i_conv(i_img *im,const float *coeff,int len) {
+ int i,l,c,ch,center;
+ double pc;
+ double res[11];
+ i_img *timg;
+
+ mm_log((1,"i_conv(im %p, coeff %p, len %d)\n",im,coeff,len));
+
+ timg = i_sametype(im, im->xsize, im->ysize);
+
+ center=(len-1)/2;
+
+#code im->bits <= 8
+ IM_COLOR rcolor;
+ /* don't move the calculation of pc up here, it depends on which pixels
+ are readable */
+ for(l=0;l<im->ysize;l++) {
+ for(i=0;i<im->xsize;i++) {
+ pc=0.0;
+ for(ch=0;ch<im->channels;ch++)
+ res[ch]=0;
+ for(c=0;c<len;c++)
+ if (IM_GPIX(im,i+c-center,l,&rcolor)!=-1) {
+ for(ch=0;ch<im->channels;ch++)
+ res[ch] += (rcolor.channel[ch])*coeff[c];
+ pc+=coeff[c];
+ }
+ for(ch=0;ch<im->channels;ch++) {
+ double temp = res[ch]/pc;
+ rcolor.channel[ch] =
+ temp < 0 ? 0 : temp > IM_SAMPLE_MAX ? IM_SAMPLE_MAX : (IM_SAMPLE_T)temp;
+ }
+ IM_PPIX(timg,i,l,&rcolor);
+ }
+ }
+
+ for(l=0;l<im->xsize;l++) {
+ for(i=0;i<im->ysize;i++) {
+ pc=0.0;
+ for(ch=0;ch<im->channels;ch++) res[ch]=0;
+ for(c=0;c<len;c++) {
+ if (IM_GPIX(timg,l,i+c-center,&rcolor)!=-1) {
+ for(ch=0;ch<im->channels;ch++)
+ res[ch] += (rcolor.channel[ch])*coeff[c];
+ pc+=coeff[c];
+ }
+ }
+ for(ch=0;ch<im->channels;ch++) {
+ double temp = res[ch]/pc;
+ rcolor.channel[ch]=
+ temp < 0 ? 0 : temp > IM_SAMPLE_MAX ? IM_SAMPLE_MAX : (IM_SAMPLE_T)temp;
+ }
+ IM_PPIX(im,l,i,&rcolor);
+ }
+ }
+#/code
+
+ i_img_destroy(timg);
+}
+
+
+
+
+
+
+
+
+
}
for(ch=0;ch<im->channels;ch++) {
double value = res[ch] / pc;
- rcolor.channel[ch] = value > IM_SAMPLE_MAX ? IM_SAMPLE_MAX : value;
+ rcolor.channel[ch] = value > IM_SAMPLE_MAX ? IM_SAMPLE_MAX : IM_ROUND(value);
}
IM_PPIX(timg,i,l,&rcolor);
}
}
for(ch=0;ch<im->channels;ch++) {
double value = res[ch]/pc;
- rcolor.channel[ch] = value > IM_SAMPLE_MAX ? IM_SAMPLE_MAX : value;
+ rcolor.channel[ch] = value > IM_SAMPLE_MAX ? IM_SAMPLE_MAX : IM_ROUND(value);
}
IM_PPIX(im,l,i,&rcolor);
}
static int i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals);
static int i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, const int *chans, int chan_count);
static int i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, const int *chans, int chan_count);
-/*static int i_psamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count);
- static int i_psampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count);*/
+
+/*
+=item i_img_alloc()
+=category Image Implementation
+
+Allocates a new i_img structure.
+
+When implementing a new image type perform the following steps in your
+image object creation function:
+
+=over
+
+=item 1.
+
+allocate the image with i_img_alloc().
+
+=item 2.
+
+initialize any function pointers or other data as needed, you can
+overwrite the whole block if you need to.
+
+=item 3.
+
+initialize Imager's internal data by calling i_img_init() on the image
+object.
+
+=back
+
+=cut
+*/
+
+i_img *
+i_img_alloc(void) {
+ return mymalloc(sizeof(i_img));
+}
+
+/*
+=item i_img_init(img)
+=category Image Implementation
+
+Imager interal initialization of images.
+
+Currently this does very little, in the future it may be used to
+support threads, or color profiles.
+
+=cut
+*/
+
+void
+i_img_init(i_img *img) {
+ img->im_data = NULL;
+}
/*
=item ICL_new_internal(r, g, b, a)
NULL, /* i_f_setcolors */
NULL, /* i_f_destroy */
+
+ i_gsamp_bits_fb,
+ NULL, /* i_f_psamp_bits */
};
/*static void set_8bit_direct(i_img *im) {
i_img *im;
mm_log((1,"i_img_struct()\n"));
- if ( (im=mymalloc(sizeof(i_img))) == NULL)
- i_fatal(2,"malloc() error\n");
+
+ im = i_img_alloc();
*im = IIM_base_8bit_direct;
im->xsize=0;
im->ch_mask=MAXINT;
im->bytes=0;
im->idata=NULL;
+
+ i_img_init(im);
mm_log((1,"(%p) <- i_img_struct\n",im));
return im;
}
if (im == NULL)
- if ( (im=mymalloc(sizeof(i_img))) == NULL)
- i_fatal(2,"malloc() error\n");
+ im = i_img_alloc();
memcpy(im, &IIM_base_8bit_direct, sizeof(i_img));
i_tags_new(&im->tags);
memset(im->idata,0,(size_t)im->bytes);
im->ext_data = NULL;
+
+ i_img_init(im);
mm_log((1,"(%p) <- i_img_empty_ch\n",im));
return im;
}
}
else {
- i_color temp;
- int index;
- int count;
i_palidx *vals;
- /* paletted image */
- i_img_pal_new_low(im, x1, y1, src->channels, i_maxcolors(src));
- /* copy across the palette */
- count = i_colorcount(src);
- for (index = 0; index < count; ++index) {
- i_getcolors(src, index, &temp, 1);
- i_addcolors(im, &temp, 1);
- }
-
vals = mymalloc(sizeof(i_palidx) * x1);
for (y = 0; y < y1; ++y) {
i_gpal(src, 0, x1, y, vals);
/*
=back
+=head2 Fallback handler
+
+=over
+
+=item i_gsamp_bits_fb
+
+=cut
+*/
+
+int
+i_gsamp_bits_fb(i_img *im, int l, int r, int y, unsigned *samps,
+ const int *chans, int chan_count, int bits) {
+ if (bits < 1 || bits > 32) {
+ i_push_error(0, "Invalid bits, must be 1..32");
+ return -1;
+ }
+
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ double scale;
+ int ch, count, i, w;
+
+ if (bits == 32)
+ scale = 4294967295.0;
+ else
+ scale = (double)(1 << bits) - 1;
+
+ if (r > im->xsize)
+ r = im->xsize;
+ w = r - l;
+ count = 0;
+
+ if (chans) {
+ /* make sure we have good channel numbers */
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ return -1;
+ }
+ }
+ for (i = 0; i < w; ++i) {
+ i_fcolor c;
+ i_gpixf(im, l+i, y, &c);
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = (unsigned)(c.channel[ch] * scale + 0.5);
+ ++count;
+ }
+ }
+ }
+ else {
+ if (chan_count <= 0 || chan_count > im->channels) {
+ i_push_error(0, "Invalid channel count");
+ return -1;
+ }
+ for (i = 0; i < w; ++i) {
+ i_fcolor c;
+ i_gpixf(im, l+i, y, &c);
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = (unsigned)(c.channel[ch] * scale + 0.5);
+ ++count;
+ }
+ }
+ }
+
+ return count;
+ }
+ else {
+ i_push_error(0, "Image position outside of image");
+ return -1;
+ }
+}
+
+/*
+=back
+
=head2 Stream reading and writing wrapper functions
=over
colors[1].rgb.r == 0 &&
colors[1].rgb.g == 0 &&
colors[1].rgb.b == 0) {
- *zero_is_white = 0;
+ *zero_is_white = 1;
return 1;
}
else if (colors[0].rgb.r == 0 &&
colors[1].rgb.r == 255 &&
colors[1].rgb.g == 255 &&
colors[1].rgb.b == 255) {
- *zero_is_white = 1;
+ *zero_is_white = 0;
return 1;
}
}
else if (im->channels == 1) {
if (colors[0].channel[0] == 255 &&
- colors[1].channel[1] == 0) {
- *zero_is_white = 0;
+ colors[1].channel[0] == 0) {
+ *zero_is_white = 1;
return 1;
}
else if (colors[0].channel[0] == 0 &&
- colors[0].channel[0] == 255) {
- *zero_is_white = 1;
+ colors[1].channel[0] == 255) {
+ *zero_is_white = 0;
return 1;
}
}
i_img *i_img_empty_ch(i_img *im,int x,int y,int ch);
void i_img_exorcise(i_img *im);
void i_img_destroy(i_img *im);
+i_img *i_img_alloc(void);
+void i_img_init(i_img *im);
void i_img_info(i_img *im,int *info);
#define i_gsampf(im, l, r, y, samps, chans, count) \
(((im)->i_f_gsampf)((im), (l), (r), (y), (samps), (chans), (count)))
+#define i_gsamp_bits(im, l, r, y, samps, chans, count, bits) \
+ (((im)->i_f_gsamp_bits) ? ((im)->i_f_gsamp_bits)((im), (l), (r), (y), (samps), (chans), (count), (bits)) : -1)
+#define i_psamp_bits(im, l, r, y, samps, chans, count, bits) \
+ (((im)->i_f_psamp_bits) ? ((im)->i_f_psamp_bits)((im), (l), (r), (y), (samps), (chans), (count), (bits)) : -1)
+
#define i_findcolor(im, color, entry) \
(((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0)
extern void i_quant_transparent(i_quantize *quant, i_palidx *indices, i_img *img, i_palidx trans_index);
extern i_img *i_img_pal_new(int x, int y, int channels, int maxpal);
-extern i_img *i_img_pal_new_low(i_img *im, int x, int y, int channels, int maxpal);
extern i_img *i_img_to_pal(i_img *src, i_quantize *quant);
extern i_img *i_img_to_rgb(i_img *src);
extern i_img *i_img_masked_new(i_img *targ, i_img *mask, int x, int y,
int w, int h);
extern i_img *i_img_16_new(int x, int y, int ch);
-extern i_img *i_img_16_new_low(i_img *im, int x, int y, int ch);
extern i_img *i_img_to_rgb16(i_img *im);
extern i_img *i_img_double_new(int x, int y, int ch);
-extern i_img *i_img_double_new_low(i_img *im, int x, int y, int ch);
extern int i_img_is_monochrome(i_img *im, int *zero_is_white);
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);
+char const * i_tiff_libversion(void);
+int i_tiff_has_compression(char const *name);
#endif /* HAVE_LIBTIFF */
extern int i_setcolors_forward(i_img *im, int index, const i_color *colors,
int count);
+/* fallback handler for gsamp_bits */
+extern int i_gsamp_bits_fb(i_img *im, int x, int r, int y, unsigned *samp,
+ const int *chans, int chan_count, int bits);
+
#define SampleFTo16(num) ((int)((num) * 65535.0 + 0.01))
/* we add that little bit to avoid rounding issues */
#define Sample16ToF(num) ((num) / 65535.0)
typedef void (*i_f_destroy_t)(i_img *im);
+typedef int (*i_f_gsamp_bits_t)(i_img *im, int x, int r, int y, unsigned *samp,
+ const int *chans, int chan_count, int bits);
+typedef int (*i_f_psamp_bits_t)(i_img *im, int x, int r, int y, unsigned const *samp,
+ const int *chans, int chan_count, int bits);
+
typedef int i_img_dim;
+/*
+=item i_img
+=category Data Types
+=synopsis i_img *img;
+
+This is Imager's image type.
+
+It contains the following members:
+
+=over
+
+=item *
+
+channels - the number of channels in the image
+
+=item *
+
+xsize, ysize - the width and height of the image in pixels
+
+=item *
+
+bytes - the number of bytes used to store the image data. Undefined
+where virtual is non-zero.
+
+=item *
+
+ch_mask - a mask of writable channels. eg. if this is 6 then only
+channels 1 and 2 are writable. There may be bits set for which there
+are no channels in the image.
+
+=item *
+
+bits - the number of bits stored per sample. Should be one of
+i_8_bits, i_16_bits, i_double_bits.
+
+=item *
+
+type - either i_direct_type for direct color images, or i_palette_type
+for paletted images.
+
+=item *
+
+virtual - if zero then this image is-self contained. If non-zero then
+this image could be an interface to some other implementation.
+
+=item *
+
+idata - the image data. This should not be directly accessed. A new
+image implementation can use this to store its image data.
+i_img_destroy() will myfree() this pointer if it's non-null.
+
+=item *
+
+tags - a structure storing the image's tags. This should only be
+accessed via the i_tags_*() functions.
+
+=item *
+
+ext_data - a pointer for use internal to an image implementation.
+This should be freed by the image's destroy handler.
+
+=item *
+
+im_data - data internal to Imager. This is initialized by
+i_img_init().
+
+=item *
+
+i_f_ppix, i_f_ppixf, i_f_plin, i_f_plinf, i_f_gpix, i_f_gpixf,
+i_f_glin, i_f_glinf, i_f_gsamp, i_f_gampf - implementations for each
+of the required image functions. An image implementation should
+initialize these between calling i_img_alloc() and i_img_init().
+
+=item *
+
+i_f_gpal, i_f_ppal, i_f_addcolors, i_f_getcolors, i_f_colorcount,
+i_f_maxcolors, i_f_findcolor, i_f_setcolors - implementations for each
+paletted image function.
+
+=item *
+
+i_f_destroy - custom image destruction function. This should be used
+to release memory if necessary.
+
+=item *
+
+i_f_gsamp_bits - implements i_gsamp_bits() for this image.
+
+=item *
+
+i_f_psamp_bits - implements i_psamp_bits() for this image.
+
+=back
+
+=cut
+*/
+
struct i_img_ {
int channels;
i_img_dim xsize,ysize;
i_f_setcolors_t i_f_setcolors;
i_f_destroy_t i_f_destroy;
+
+ /* as of 0.61 */
+ i_f_gsamp_bits_t i_f_gsamp_bits;
+ i_f_psamp_bits_t i_f_psamp_bits;
+
+ void *im_data;
};
/* ext_data for paletted images
i_img_get_width,
i_img_get_height,
i_lhead,
- i_loog
+ i_loog,
+
+ /* IMAGER_API_LEVEL 4 functions */
+ i_img_alloc,
+ i_img_init,
};
/* in general these functions aren't called by Imager internally, but
#define i_lhead(file, line) ((im_extt->f_i_lhead)((file), (line)))
#define i_loog (im_extt->f_i_loog)
+#define i_img_alloc() ((im_extt->f_i_img_alloc)())
+#define i_img_init(img) ((im_extt->f_i_img_init)(img))
+
#endif
will result in an increment of IMAGER_API_LEVEL.
*/
-#define IMAGER_API_LEVEL 3
+#define IMAGER_API_LEVEL 4
typedef struct {
int version;
void (*f_i_loog)(int level, const char *msg, ...);
/* IMAGER_API_LEVEL 4 functions will be added here */
+ i_img *(*f_i_img_alloc)(void);
+ void (*f_i_img_init)(i_img *);
+
+ /* IMAGER_API_LEVEL 5 functions will be added here */
} im_ext_funcs;
#define PERL_FUNCTION_TABLE_NAME "Imager::__ext_func_table"
int const *chans, int chan_count);
static int i_gsampf_d16(i_img *im, int l, int r, int y, i_fsample_t *samps,
int const *chans, int chan_count);
+static int i_gsamp_bits_d16(i_img *im, int l, int r, int y, unsigned *samps,
+ int const *chans, int chan_count, int bits);
+static int i_psamp_bits_d16(i_img *im, int l, int r, int y, unsigned const *samps,
+ int const *chans, int chan_count, int bits);
/*
=item IIM_base_16bit_direct
NULL, /* i_f_gpal */
NULL, /* i_f_ppal */
- NULL, /* i_f_addcolor */
- NULL, /* i_f_getcolor */
+ NULL, /* i_f_addcolors */
+ NULL, /* i_f_getcolors */
NULL, /* i_f_colorcount */
+ NULL, /* i_f_maxcolors */
NULL, /* i_f_findcolor */
+ NULL, /* i_f_setcolors */
NULL, /* i_f_destroy */
+
+ i_gsamp_bits_d16,
+ i_psamp_bits_d16,
};
/* it's possible some platforms won't have a 16-bit integer type,
#define STORE16(bytes, offset, word) \
(((i_sample16_t *)(bytes))[offset] = (word))
#define STORE8as16(bytes, offset, byte) \
- (((i_sample16_t *)(bytes))[offset] = (byte) * 256)
+ (((i_sample16_t *)(bytes))[offset] = (byte) * 256 + (byte))
#define GET16(bytes, offset) \
(((i_sample16_t *)(bytes))[offset])
-#define GET16as8(bytes, offset) \
- (((i_sample16_t *)(bytes))[offset] / 256)
-
#else
/* we have to do this the hard way */
(((unsigned char *)(bytes))[(offset)*2+1] = (word) & 0xFF))
#define STORE8as16(bytes, offset, byte) \
((((unsigned char *)(bytes))[(offset)*2] = (byte)), \
- (((unsigned char *)(bytes))[(offset)*2+1] = 0))
+ (((unsigned char *)(bytes))[(offset)*2+1] = (byte)))
#define GET16(bytes, offset) \
(((unsigned char *)(bytes))[(offset)*2] * 256 \
+ ((unsigned char *)(bytes))[(offset)*2+1])
-#define GET16as8(bytes, offset) \
- (((unsigned char *)(bytes))[(offset)*2] << 8)
#endif
+#define GET16as8(bytes, offset) \
+ ((((i_sample16_t *)(bytes))[offset]+127) / 257)
+
/*
-=item i_img_16_new_low(int x, int y, int ch)
+=item i_img_16_new(x, y, ch)
-Creates a new 16-bit per sample image.
+=category Image creation
+=synopsis i_img *img = i_img_16_new(width, height, channels);
+
+Create a new 16-bit/sample image.
+
+Returns the image on success, or NULL on failure.
=cut
*/
-i_img *i_img_16_new_low(i_img *im, int x, int y, int ch) {
+
+i_img *i_img_16_new(int x, int y, int ch) {
+ i_img *im;
int bytes, line_bytes;
+
mm_log((1,"i_img_16_new(x %d, y %d, ch %d)\n", x, y, ch));
if (x < 1 || y < 1) {
return NULL;
}
+ im = i_img_alloc();
*im = IIM_base_16bit_direct;
i_tags_new(&im->tags);
im->xsize = x;
im->bytes = bytes;
im->ext_data = NULL;
im->idata = mymalloc(im->bytes);
- if (im->idata) {
- memset(im->idata, 0, im->bytes);
- }
- else {
- i_tags_destroy(&im->tags);
- im = NULL;
- }
-
- return im;
-}
-
-/*
-=item i_img_16_new(x, y, ch)
-
-=category Image creation/destruction
-=synopsis i_img *img = i_img_16_new(width, height, channels);
-
-Create a new 16-bit/sample image.
-
-Returns the image on success, or NULL on failure.
-
-=cut
-*/
+ memset(im->idata, 0, im->bytes);
-i_img *i_img_16_new(int x, int y, int ch) {
- i_img *im;
-
- i_clear_error();
+ i_img_init(im);
- im = mymalloc(sizeof(i_img));
- if (im) {
- if (!i_img_16_new_low(im, x, y, ch)) {
- myfree(im);
- im = NULL;
- }
- }
-
- mm_log((1, "(%p) <- i_img_16_new\n", im));
-
return im;
}
}
}
+static int
+i_gsamp_bits_d16(i_img *im, int l, int r, int y, unsigned *samps,
+ int const *chans, int chan_count, int bits) {
+ int ch, count, i, w;
+ int off;
+
+ if (bits != 16) {
+ return i_gsamp_bits_fb(im, l, r, y, samps, chans, chan_count, bits);
+ }
+
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ off = (l+y*im->xsize) * im->channels;
+ w = r - l;
+ count = 0;
+
+ if (chans) {
+ /* make sure we have good channel numbers */
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ return -1;
+ }
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = GET16(im->idata, off+chans[ch]);
+ ++count;
+ }
+ off += im->channels;
+ }
+ }
+ else {
+ if (chan_count <= 0 || chan_count > im->channels) {
+ i_push_error(0, "Invalid channel count");
+ return -1;
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = GET16(im->idata, off+ch);
+ ++count;
+ }
+ off += im->channels;
+ }
+ }
+
+ return count;
+ }
+ else {
+ i_push_error(0, "Image position outside of image");
+ return -1;
+ }
+}
+
+static int
+i_psamp_bits_d16(i_img *im, int l, int r, int y, unsigned const *samps,
+ int const *chans, int chan_count, int bits) {
+ int ch, count, i, w;
+ int off;
+
+ if (bits != 16) {
+ i_push_error(0, "Invalid bits for 16-bit image");
+ return -1;
+ }
+
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ off = (l+y*im->xsize) * im->channels;
+ w = r - l;
+ count = 0;
+
+ if (chans) {
+ /* make sure we have good channel numbers */
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ return -1;
+ }
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (im->ch_mask & (1 << ch))
+ STORE16(im->idata, off+chans[ch], *samps);
+ ++samps;
+ ++count;
+ }
+ off += im->channels;
+ }
+ }
+ else {
+ if (chan_count <= 0 || chan_count > im->channels) {
+ i_push_error(0, "Invalid channel count");
+ return -1;
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (im->ch_mask & (1 << ch))
+ STORE16(im->idata, off+ch, *samps);
+ ++samps;
+ ++count;
+ }
+ off += im->channels;
+ }
+ }
+
+ return count;
+ }
+ else {
+ i_push_error(0, "Image position outside of image");
+ return -1;
+ }
+}
+
/*
=back
NULL, /* i_f_gpal */
NULL, /* i_f_ppal */
- NULL, /* i_f_addcolor */
- NULL, /* i_f_getcolor */
+ NULL, /* i_f_addcolors */
+ NULL, /* i_f_getcolors */
NULL, /* i_f_colorcount */
+ NULL, /* i_f_maxcolors */
NULL, /* i_f_findcolor */
+ NULL, /* i_f_setcolors */
NULL, /* i_f_destroy */
+
+ i_gsamp_bits_fb,
+ NULL, /* i_f_psamp_bits */
};
/*
=item i_img_double_new(int x, int y, int ch)
-
=category Image creation/destruction
=synopsis i_img *img = i_img_double_new(width, height, channels);
=cut
*/
-i_img *i_img_double_new_low(i_img *im, int x, int y, int ch) {
+i_img *i_img_double_new(int x, int y, int ch) {
int bytes;
+ i_img *im;
mm_log((1,"i_img_double_new(x %d, y %d, ch %d)\n", x, y, ch));
return NULL;
}
+ im = i_img_alloc();
*im = IIM_base_double_direct;
i_tags_new(&im->tags);
im->xsize = x;
im->bytes = bytes;
im->ext_data = NULL;
im->idata = mymalloc(im->bytes);
- if (im->idata) {
- memset(im->idata, 0, im->bytes);
- }
- else {
- i_tags_destroy(&im->tags);
- im = NULL;
- }
-
- return im;
-}
-
-i_img *i_img_double_new(int x, int y, int ch) {
- i_img *im;
-
- i_clear_error();
-
- im = mymalloc(sizeof(i_img));
- if (im) {
- if (!i_img_double_new_low(im, x, y, ch)) {
- myfree(im);
- im = NULL;
- }
- }
-
- mm_log((1, "(%p) <- i_img_double_new\n", im));
+ memset(im->idata, 0, im->bytes);
+ i_img_init(im);
return im;
}
color.rgba.red = 255; color.rgba.green = 0; color.rgba.blue = 255;
+ # Data Types
+ i_img *img;
+
# Drawing
i_arc(im, 50, 50, 20, 45, 135, &color);
i_arc_aa(im, 50, 50, 35, 90, 135, &color);
# Image
- # Image creation/destruction
+ # Image creation
i_img *img = i_img_16_new(width, height, channels);
+
+ # Image creation/destruction
i_img *img = i_img_8_new(width, height, channels);
i_img_destroy(img)
i_img *img = i_img_double_new(width, height, channels);
i_img *img = i_sametype(src, width, height);
i_img *img = i_sametype_chans(src, width, height, channels);
+ # Image Implementation
+
# Image quantization
+ # Logging
+
# Paletted images
# Tags
=head1 DESCRIPTION
+=head2 Data Types
+
+=over
+
+=item i_img
+
+This is Imager's image type.
+
+It contains the following members:
+
+=over
+
+=item *
+
+channels - the number of channels in the image
+
+=item *
+
+xsize, ysize - the width and height of the image in pixels
+
+=item *
+
+bytes - the number of bytes used to store the image data. Undefined
+where virtual is non-zero.
+
+=item *
+
+ch_mask - a mask of writable channels. eg. if this is 6 then only
+channels 1 and 2 are writable. There may be bits set for which there
+are no channels in the image.
+
+=item *
+
+bits - the number of bits stored per sample. Should be one of
+i_8_bits, i_16_bits, i_double_bits.
+
+=item *
+
+type - either i_direct_type for direct color images, or i_palette_type
+for paletted images.
+
+=item *
+
+virtual - if zero then this image is-self contained. If non-zero then
+this image could be an interface to some other implementation.
+
+=item *
+
+idata - the image data. This should not be directly accessed. A new
+image implementation can use this to store its image data.
+i_img_destroy() will myfree() this pointer if it's non-null.
+
+=item *
+
+tags - a structure storing the image's tags. This should only be
+accessed via the i_tags_*() functions.
+
+=item *
+
+ext_data - a pointer for use internal to an image implementation.
+This should be freed by the image's destroy handler.
+
+=item *
+
+im_data - data internal to Imager. This is initialized by
+i_img_init().
+
+=item *
+
+i_f_ppix, i_f_ppixf, i_f_plin, i_f_plinf, i_f_gpix, i_f_gpixf,
+i_f_glin, i_f_glinf, i_f_gsamp, i_f_gampf - implementations for each
+of the required image functions. An image implementation should
+initialize these between calling i_img_alloc() and i_img_init().
+
+=item *
+
+i_f_gpal, i_f_ppal, i_f_addcolors, i_f_getcolors, i_f_colorcount,
+i_f_maxcolors, i_f_findcolor, i_f_setcolors - implementations for each
+paletted image function.
+
+=item *
+
+i_f_destroy - custom image destruction function. This should be used
+to release memory if necessary.
+
+=item *
+
+i_f_gsamp_bits - implements i_gsamp_bits() for this image.
+
+=item *
+
+i_f_psamp_bits - implements i_psamp_bits() for this image.
+
+=back
+
+
+=for comment
+From: File imdatatypes.h
+
+
+=back
+
=head2 Drawing
=over
=back
-=head2 Image creation/destruction
+=head2 Image creation
=over
=for comment
From: File img16.c
+
+=back
+
+=head2 Image creation/destruction
+
+=over
+
=item i_img_8_new(x, y, ch)
=item i_img_double_new(int x, int y, int ch)
-
Creates a new double per sample image.
From: File image.c
+=back
+
+=head2 Image Implementation
+
+=over
+
+=item i_img_alloc()
+
+Allocates a new i_img structure.
+
+When implementing a new image type perform the following steps in your
+image object creation function:
+
+=over
+
+=item 1.
+
+allocate the image with i_img_alloc().
+
+=item 2.
+
+initialize any function pointers or other data as needed, you can
+overwrite the whole block if you need to.
+
+=item 3.
+
+initialize Imager's internal data by calling i_img_init() on the image
+object.
+
+=back
+
+
+=for comment
+From: File image.c
+
+=item i_img_init(img)
+
+Imager interal initialization of images.
+
+Currently this does very little, in the future it may be used to
+support threads, or color profiles.
+
+
+=for comment
+From: File image.c
+
+
=back
=head2 Image quantization
From: File quant.c
+=back
+
+=head2 Logging
+
+=over
+
+=item i_loog(level, format, ...)
+
+This is an internal function called by the mm_log() macro.
+
+
+=for comment
+From: File log.c
+
+=item mm_log((level, format, ...))
+
+This is the main entry point to logging. Note that the extra set of
+parentheses are required due to limitations in C89 macros.
+
+This will format a string with the current file and line number to the
+log file if logging is enabled.
+
+
+=for comment
+From: File log.h
+
+
=back
=head2 Paletted images
=item *
-B<i_lhead>
+B<i_color>
+
+=item *
+
+B<i_fcolor>
=item *
-B<i_loog>
+B<i_fill_t>
+
+=item *
+
+B<i_lhead>
Permited values are C<8bit> and C<float>.
+As of Imager 0.61 this can be C<16bit> only for 16 bit images.
+
=item *
channels - a reference to an array of channels to return, where 0 is
the first channel. Default: C< [ 0 .. $self->getchannels()-1 ] >
+=item *
+
+target - if an array reference is supplied in target then the samples
+will be stored here instead of being returned.
+
+=item *
+
+offset - the offset within the array referenced by I<target>
+
=back
In list context this will return a list of integers between 0 and 255
C< pack("C*", ...) > when I<type> is C<8bit> or a string of packed
doubles as with C< pack("d*", ...) > when I<type> is C<float>.
+If the I<target> option is supplied then only a count of samples is
+returned.
+
Example: Check if any pixels in an image have a non-zero alpha
channel:
$out->setscanline(y=>$y, pixels=>$data);
}
+Retrieve 16-bit samples:
+
+ if ($img->bits == 16) {
+ my @samples;
+ $img->getsamples(x => 0, y => $y, target => \@samples, type => '16bit');
+ }
+
+=item setsamples
+
+This allows writing of samples back to some images. Currently this is
+only supported for 16-bit/sample images.
+
+Parameters:
+
+=over
+
+=item *
+
+y - vertical position of the scanline. This parameter is required.
+
+=item *
+
+x - position to start on the scanline. Default: 0
+
+=item *
+
+width - number of pixels to write. Default: $img->getwidth - x. The
+minimum of this and the number of pixels represented by the samples
+provided will be written.
+
+=item *
+
+type - the type of sample data to write. This parameter is required.
+
+As of Imager 0.61 this can only be C<16bit> only for 16 bit images.
+
+=item *
+
+channels - a reference to an array of channels to return, where 0 is
+the first channel. Default: C< [ 0 .. $self->getchannels()-1 ] >
+
+=item *
+
+data - a reference to an array of samples to write. Required.
+
+=item *
+
+offset - the starting offset within the array referenced by I<data>
+
+=back
+
+Returns the number of samples written.
+
=back
=head1 Packed Color Data
use $img->to_paletted() to product a bi-level image. This way you can
use dithering:
- my $bilevel = $img->to_paletted(colors=>[ NC(0,0,0), NC(255,255,255) ],
- make_colors => 'none',
+ my $bilevel = $img->to_paletted(make_colors => 'mono',
translate => 'errdiff',
errdiff => 'stucki');
TIFF supports the spatial resolution tags. See the
C<tiff_resolutionunit> tag for some extra options.
+As of Imager 0.62 Imager reads:
+
+=over
+
+=item *
+
+16-bit grey, RGB, or CMYK image, including a possible alpha channel as
+a 16-bit/sample image.
+
+=item *
+
+32-bit grey, RGB image, including a possible alpha channel as a
+double/sample image.
+
+=item *
+
+bi-level images as paletted images containing only black and white,
+which other formats will also write as bi-level.
+
+=item *
+
+tiled paletted images are now handled correctly
+
+=back
+
The following tags are set in a TIFF image when read, and can be set
to control output:
=over
+=item tiff_compression
+
+When reading an image this is set to the numeric value of the TIFF
+compression tag.
+
+On writing you can set this to either a numeric compression tag value,
+or one of the following values:
+
+ Ident Number Description
+ none 1 No compression
+ packbits 32773 Macintosh RLE
+ ccittrle 2 CCITT RLE
+ fax3 3 CCITT Group 3 fax encoding (T.4)
+ t4 3 As above
+ fax4 4 CCITT Group 4 fax encoding (T.6)
+ t6 4 As above
+ lzw 5 LZW
+ jpeg 7 JPEG
+ zip 8 Deflate (GZIP) Non-standard
+ deflate 8 As above.
+ oldzip 32946 Deflate with an older code.
+ ccittrlew 32771 Word aligned CCITT RLE
+
+In general a compression setting will be ignored where it doesn't make
+sense, eg. C<jpeg> will be ignored for compression if the image is
+being written as bilevel.
+
+Imager attempts to check that your build of libtiff supports the given
+compression, and will fallback to C<packbits> if it isn't enabled.
+eg. older distributions didn't include LZW compression, and JPEG
+compression is only available if libtiff is configured with libjpeg's
+location.
+
+ $im->write(file => 'foo.tif', tiff_compression => 'lzw')
+ or die $im->errstr;
+
+=item tiff_jpegquality
+
+If I<tiff_compression> if C<jpeg> then this can be a number from 1 to
+100 giving the JPEG compression quality. High values are better
+quality and larger files.
+
=item tiff_resolutionunit
The value of the ResolutionUnit tag. This is ignored on writing if
my $font = NF(file => 'foo.ttf');
+=item NCF
+
+Create a new L<Imager::Color::Font> object, supplying any parameter to
+the new() method.
+
+ my $colorf = NCF(1.0, 0, 0);
+
=back
=head1 BUGS
This may also be used for non-native Imager images in the future, for
example, for an Imager object that draws on an SDL surface.
+=item is_bilevel
+
+Tests if the image will be written as a monochrome or bi-level image
+for formats that support that image organization.
+
+In scalar context, returns true if the image is bi-level.
+
+In list context returns a list:
+
+ ($is_bilevel, $zero_is_white) = $img->is_bilevel;
+
+An image is considered bi-level, if all of the following are true:
+
+=over
+
+=item *
+
+the image is a paletted image
+
+=item *
+
+the image has 1 or 3 channels
+
+=item *
+
+the image has only 2 colors in the palette
+
+=item *
+
+those 2 colors are black and white, in either order.
+
+=back
+
+If a real bi-level organization image is ever added to Imager, this
+function will return true for that too.
+
=back
=head2 Direct Type Images
require Exporter;
use vars qw(@ISA @EXPORT_OK);
@ISA = qw(Exporter);
-@EXPORT_OK = qw(diff_text_with_nul test_image_raw test_image_16 test_image
+@EXPORT_OK = qw(diff_text_with_nul
+ test_image_raw test_image_16 test_image test_image_double
is_color3 is_color1 is_color4
is_fcolor4
is_image is_image_similar
- image_bounds_checks);
+ image_bounds_checks mask_tests
+ test_colorf_gpix test_color_gpix test_colorf_glin);
sub diff_text_with_nul {
my ($desc, $text1, $text2, @params) = @_;
$img;
}
+sub test_image_double {
+ my $green = Imager::Color->new(0, 255, 0, 255);
+ my $blue = Imager::Color->new(0, 0, 255, 255);
+ my $red = Imager::Color->new(255, 0, 0, 255);
+ my $img = Imager->new(xsize => 150, ysize => 150, bits => 'double');
+ $img->box(filled => 1, color => $green, box => [ 70, 25, 130, 125 ]);
+ $img->box(filled => 1, color => $blue, box => [ 20, 25, 80, 125 ]);
+ $img->arc(x => 75, y => 75, r => 30, color => $red);
+ $img->filter(type => 'conv', coef => [ 0.1, 0.2, 0.4, 0.2, 0.1 ]);
+
+ $img;
+}
+
sub is_image_similar($$$$) {
my ($left, $right, $limit, $comment) = @_;
if ($diff > $limit) {
$builder->ok(0, $comment);
$builder->diag("image data difference > $limit - $diff");
+
+ if ($limit == 0) {
+ # find the first mismatch
+ CHECK:
+ for my $y (0 .. $left->getheight()-1) {
+ for my $x (0.. $left->getwidth()-1) {
+ my @lsamples = $left->getsamples(x => $x, y => $y, width => 1);
+ my @rsamples = $right->getsamples(x => $x, y => $y, width => 1);
+ if ("@lsamples" ne "@rsamples") {
+ $builder->diag("first mismatch at ($x, $y) - @lsamples vs @rsamples");
+ last CHECK;
+ }
+ }
+ }
+ }
+
return;
}
$builder->ok(!$im->setpixel(x => 0, y => 10, color => $blackf), 'bounds check set (0, 10) float');
}
+sub test_colorf_gpix {
+ my ($im, $x, $y, $expected, $epsilon, $comment) = @_;
+
+ my $builder = Test::Builder->new;
+
+ defined $comment or $comment = '';
+
+ my $c = Imager::i_gpixf($im, $x, $y);
+ unless ($c) {
+ $builder->ok(0, "$comment - retrieve color at ($x,$y)");
+ return;
+ }
+ unless ($builder->ok(_colorf_cmp($c, $expected, $epsilon) == 0,
+ "$comment - got right color ($x, $y)")) {
+ print "# got: (", join(",", ($c->rgba)[0,1,2]), ")\n";
+ print "# expected: (", join(",", ($expected->rgba)[0,1,2]), ")\n";
+ return;
+ }
+ 1;
+}
+
+sub test_color_gpix {
+ my ($im, $x, $y, $expected, $comment) = @_;
+
+ my $builder = Test::Builder->new;
+
+ defined $comment or $comment = '';
+ my $c = Imager::i_get_pixel($im, $x, $y);
+ unless ($c) {
+ $builder->ok(0, "$comment - retrieve color at ($x,$y)");
+ return;
+ }
+ unless ($builder->ok(_color_cmp($c, $expected) == 0,
+ "got right color ($x, $y)")) {
+ print "# got: (", join(",", ($c->rgba)[0,1,2]), ")\n";
+ print "# expected: (", join(",", ($expected->rgba)[0,1,2]), ")\n";
+ return;
+ }
+
+ return 1;
+}
+
+sub test_colorf_glin {
+ my ($im, $x, $y, $pels, $comment) = @_;
+
+ my $builder = Test::Builder->new;
+
+ my @got = Imager::i_glinf($im, $x, $x+@$pels, $y);
+ @got == @$pels
+ or return $builder->is_num(scalar(@got), scalar(@$pels), "$comment - pixels retrieved");
+
+ return $builder->ok(!grep(_colorf_cmp($pels->[$_], $got[$_], 0.005), 0..$#got),
+ "$comment - check colors ($x, $y)");
+}
+
+sub _colorf_cmp {
+ my ($c1, $c2, $epsilon) = @_;
+
+ defined $epsilon or $epsilon = 0;
+
+ my @s1 = $c1->rgba;
+ my @s2 = $c2->rgba;
+
+ # print "# (",join(",", @s1[0..2]),") <=> (",join(",", @s2[0..2]),")\n";
+ return abs($s1[0]-$s2[0]) >= $epsilon && $s1[0] <=> $s2[0]
+ || abs($s1[1]-$s2[1]) >= $epsilon && $s1[1] <=> $s2[1]
+ || abs($s1[2]-$s2[2]) >= $epsilon && $s1[2] <=> $s2[2];
+}
+
+sub _color_cmp {
+ my ($c1, $c2) = @_;
+
+ my @s1 = $c1->rgba;
+ my @s2 = $c2->rgba;
+
+ return $s1[0] <=> $s2[0]
+ || $s1[1] <=> $s2[1]
+ || $s1[2] <=> $s2[2];
+}
+
+# these test the action of the channel mask on the image supplied
+# which should be an OO image.
+sub mask_tests {
+ my ($im, $epsilon) = @_;
+
+ my $builder = Test::Builder->new;
+
+ defined $epsilon or $epsilon = 0;
+
+ # we want to check all four of ppix() and plin(), ppix() and plinf()
+ # basic test procedure:
+ # first using default/all 1s mask, set to white
+ # make sure we got white
+ # set mask to skip a channel, set to grey
+ # make sure only the right channels set
+
+ print "# channel mask tests\n";
+ # 8-bit color tests
+ my $white = Imager::NC(255, 255, 255);
+ my $grey = Imager::NC(128, 128, 128);
+ my $white_grey = Imager::NC(128, 255, 128);
+
+ print "# with ppix\n";
+ $builder->ok($im->setmask(mask=>~0), "set to default mask");
+ $builder->ok($im->setpixel(x=>0, 'y'=>0, color=>$white), "set to white all channels");
+ test_color_gpix($im->{IMG}, 0, 0, $white, "ppix");
+ $builder->ok($im->setmask(mask=>0xF-0x2), "set channel to exclude channel1");
+ $builder->ok($im->setpixel(x=>0, 'y'=>0, color=>$grey), "set to grey, no channel 2");
+ test_color_gpix($im->{IMG}, 0, 0, $white_grey, "ppix masked");
+
+ print "# with plin\n";
+ $builder->ok($im->setmask(mask=>~0), "set to default mask");
+ $builder->ok($im->setscanline(x=>0, 'y'=>1, pixels => [$white]),
+ "set to white all channels");
+ test_color_gpix($im->{IMG}, 0, 1, $white, "plin");
+ $builder->ok($im->setmask(mask=>0xF-0x2), "set channel to exclude channel1");
+ $builder->ok($im->setscanline(x=>0, 'y'=>1, pixels=>[$grey]),
+ "set to grey, no channel 2");
+ test_color_gpix($im->{IMG}, 0, 1, $white_grey, "plin masked");
+
+ # float color tests
+ my $whitef = Imager::NCF(1.0, 1.0, 1.0);
+ my $greyf = Imager::NCF(0.5, 0.5, 0.5);
+ my $white_greyf = Imager::NCF(0.5, 1.0, 0.5);
+
+ print "# with ppixf\n";
+ $builder->ok($im->setmask(mask=>~0), "set to default mask");
+ $builder->ok($im->setpixel(x=>0, 'y'=>2, color=>$whitef), "set to white all channels");
+ test_colorf_gpix($im->{IMG}, 0, 2, $whitef, $epsilon, "ppixf");
+ $builder->ok($im->setmask(mask=>0xF-0x2), "set channel to exclude channel1");
+ $builder->ok($im->setpixel(x=>0, 'y'=>2, color=>$greyf), "set to grey, no channel 2");
+ test_colorf_gpix($im->{IMG}, 0, 2, $white_greyf, $epsilon, "ppixf masked");
+
+ print "# with plinf\n";
+ $builder->ok($im->setmask(mask=>~0), "set to default mask");
+ $builder->ok($im->setscanline(x=>0, 'y'=>3, pixels => [$whitef]),
+ "set to white all channels");
+ test_colorf_gpix($im->{IMG}, 0, 3, $whitef, $epsilon, "plinf");
+ $builder->ok($im->setmask(mask=>0xF-0x2), "set channel to exclude channel1");
+ $builder->ok($im->setscanline(x=>0, 'y'=>3, pixels=>[$greyf]),
+ "set to grey, no channel 2");
+ test_colorf_gpix($im->{IMG}, 0, 3, $white_greyf, $epsilon, "plinf masked");
+
+}
+
1;
__END__
Returns a 150x150x3 16-bit/sample OO test image.
+=item test_image_double()
+
+Returns a 150x150x3 double/sample OO test image.
+
=item diff_text_with_nul($test_name, $text1, $text2, @options)
Creates 2 test images and writes $text1 to the first image and $text2
Any new image type should pass these tests. Does 16 separate tests.
+=item test_colorf_gpix($im, $x, $y, $expected, $epsilon, $comment)
+
+Retrieves the pixel ($x,$y) from the low-level image $im and compares
+it to the floating point color $expected, with a tolerance of epsilon.
+
+=item test_color_gpix($im, $x, $y, $expected, $comment)
+
+Retrieves the pixel ($x,$y) from the low-level image $im and compares
+it to the floating point color $expected.
+
+=item test_colorf_glin($im, $x, $y, $pels, $comment)
+
+Retrieves the floating point pixels ($x, $y)-[$x+@$pels, $y] from the
+low level image $im and compares them against @$pels.
+
+=item mask_tests($im, $epsilon)
+
+Perform a standard set of mask tests on the OO image $im.
+
=back
=head1 AUTHOR
#endif
+/*
+=item i_loog(level, format, ...)
+=category Logging
+
+This is an internal function called by the mm_log() macro.
+
+=cut
+*/
void
i_loog(int level,const char *fmt, ... ) {
void i_loog(int level,const char *msg, ... );
void i_fatal ( int exitcode,const char *fmt, ... );
+/*
+=item mm_log((level, format, ...))
+=category Logging
+
+This is the main entry point to logging. Note that the extra set of
+parentheses are required due to limitations in C89 macros.
+
+This will format a string with the current file and line number to the
+log file if logging is enabled.
+
+=cut
+*/
#ifdef IMAGER_LOG
#define mm_log(x) { i_lhead(__FILE__,__LINE__); i_loog x; }
i_setcolors_p, /* i_f_setcolors */
i_destroy_p, /* i_f_destroy */
+
+ i_gsamp_bits_fb,
+ NULL, /* i_f_psamp_bits */
};
/*
-=item i_img_pal_new_low(im, x, y, channels, maxpal)
+=item i_img_pal_new(x, y, channels, maxpal)
+
+=category Image creation/destruction
+=synopsis i_img *img = i_img_pal_new(width, height, channels, max_palette_size)
+
+Creates a new paletted image of the supplied dimensions.
-Creates a new paletted image.
+I<maxpal> is the maximum palette size and should normally be 256.
-Currently 0 < maxpal <= 256
+Returns a new image or NULL on failure.
=cut
*/
-i_img *i_img_pal_new_low(i_img *im, int x, int y, int channels, int maxpal) {
+i_img *
+i_img_pal_new(int x, int y, int channels, int maxpal) {
+ i_img *im;
i_img_pal_ext *palext;
int bytes, line_bytes;
return NULL;
}
+ im = i_img_alloc();
memcpy(im, &IIM_base_8bit_pal, sizeof(i_img));
palext = mymalloc(sizeof(i_img_pal_ext));
palext->pal = mymalloc(sizeof(i_color) * maxpal);
memset(im->idata, 0, im->bytes);
im->xsize = x;
im->ysize = y;
-
- return im;
-}
-
-/*
-=item i_img_pal_new(x, y, channels, maxpal)
-
-=category Image creation/destruction
-=synopsis i_img *img = i_img_pal_new(width, height, channels, max_palette_size)
-
-Creates a new paletted image of the supplied dimensions.
-
-I<maxpal> is the maximum palette size and should normally be 256.
-
-Returns a new image or NULL on failure.
-
-=cut
-*/
-
-i_img *i_img_pal_new(int x, int y, int channels, int maxpal) {
- i_img *im;
- mm_log((1, "i_img_pal_new(x %d, y %d, channels %d, maxpal %d)\n", x, y, channels, maxpal));
- im = mymalloc(sizeof(i_img));
- if (!i_img_pal_new_low(im, x, y, channels, maxpal)) {
- myfree(im);
- im = NULL;
- }
+ i_img_init(im);
+
return im;
}
# to make sure we get expected values
use strict;
-use Test::More tests => 223;
+use Test::More tests => 228;
BEGIN { use_ok(Imager => qw(:handy :all)) }
is($impal3->type, 'paletted', "and is paletted");
}
+{ # basic checks, 8-bit direct images
+ my $im = Imager->new(xsize => 2, ysize => 3);
+ ok($im, 'create 8-bit direct image');
+ is($im->bits, 8, '8 bits');
+ ok(!$im->virtual, 'not virtual');
+ is($im->type, 'direct', 'direct image');
+ ok(!$im->is_bilevel, 'not mono');
+}
+
ok(!Imager->new(xsize=>0, ysize=>1), "fail to create 0 height image");
cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
"0 height error message check");
#!perl -w
use strict;
-use Test::More tests => 103;
+use Test::More tests => 104;
BEGIN { use_ok(Imager=>qw(:all :handy)) }
init_log("testout/t021sixteen.log", 1);
-require "t/testtools.pl";
-
use Imager::Color::Float;
-use Imager::Test qw(test_image is_image image_bounds_checks);
+use Imager::Test qw(test_image is_image image_bounds_checks test_colorf_gpix
+ test_colorf_glin mask_tests is_color3);
my $im_g = Imager::i_img_16_new(100, 101, 1);
}
pass("fill with red");
# basic sanity
-test_colorf_gpix($im_rgb, 0, 0, $redf);
-test_colorf_gpix($im_rgb, 99, 0, $redf);
-test_colorf_gpix($im_rgb, 0, 100, $redf);
-test_colorf_gpix($im_rgb, 99, 100, $redf);
-test_colorf_glin($im_rgb, 0, 0, ($redf) x 100);
-test_colorf_glin($im_rgb, 0, 100, ($redf) x 100);
+test_colorf_gpix($im_rgb, 0, 0, $redf, 0, "top-left");
+test_colorf_gpix($im_rgb, 99, 0, $redf, 0, "top-right");
+test_colorf_gpix($im_rgb, 0, 100, $redf, 0, "bottom left");
+test_colorf_gpix($im_rgb, 99, 100, $redf, 0, "bottom right");
+test_colorf_glin($im_rgb, 0, 0, [ ($redf) x 100 ], "first line");
+test_colorf_glin($im_rgb, 0, 100, [ ($redf) x 100 ], "last line");
Imager::i_plinf($im_rgb, 20, 1, ($greenf) x 60);
test_colorf_glin($im_rgb, 0, 1,
- ($redf) x 20, ($greenf) x 60, ($redf) x 20);
+ [ ($redf) x 20, ($greenf) x 60, ($redf) x 20 ],
+ "added some green in the middle");
+{
+ my @samples;
+ is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 16, \@samples, 0, 0 .. 2), 12,
+ "i_gsamp_bits all channels - count")
+ or print "# ", Imager->_error_as_msg(), "\n";
+ is_deeply(\@samples, [ 65535, 0, 0, 65535, 0, 0,
+ 0, 65535, 0, 0, 65535, 0 ],
+ "check samples retrieved");
+ @samples = ();
+ is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 16, \@samples, 0, 0, 2), 8,
+ "i_gsamp_bits some channels - count")
+ or print "# ", Imager->_error_as_msg(), "\n";
+ is_deeply(\@samples, [ 65535, 0, 65535, 0,
+ 0, 0, 0, 0 ],
+ "check samples retrieved");
+ # fail gsamp
+ is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 16, \@samples, 0, 0, 3), undef,
+ "i_gsamp_bits fail bad channel");
+ is(Imager->_error_as_msg(), 'No channel 3 in this image', 'check message');
+
+ is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 17, \@samples, 0, 0, 2), 8,
+ "i_gsamp_bits succeed high bits");
+ is($samples[0], 131071, "check correct with high bits");
+
+ # write some samples back
+ my @wr_samples =
+ (
+ 0, 0, 65535,
+ 65535, 0, 0,
+ 0, 65535, 0,
+ 65535, 65535, 0
+ );
+ is(Imager::i_psamp_bits($im_rgb, 18, 2, 16, [ 0 .. 2 ], \@wr_samples),
+ 12, "write 16-bit samples")
+ or print "# ", Imager->_error_as_msg(), "\n";
+ @samples = ();
+ is(Imager::i_gsamp_bits($im_rgb, 18, 22, 2, 16, \@samples, 0, 0 .. 2), 12,
+ "read them back")
+ or print "# ", Imager->_error_as_msg(), "\n";
+ is_deeply(\@samples, \@wr_samples, "check they match");
+ my $c = Imager::i_get_pixel($im_rgb, 18, 2);
+ is_color3($c, 0, 0, 255, "check it write to the right places");
+}
# basic OO tests
my $oo16img = Imager->new(xsize=>200, ysize=>201, bits=>16);
ok($oo16img, "make a 16-bit oo image");
is($oo16img->bits, 16, "test bits");
-
+isnt($oo16img->is_bilevel, "should not be considered mono");
# make sure of error handling
ok(!Imager->new(xsize=>0, ysize=>1, bits=>16),
"fail to create a 0 pixel wide image");
my $im = Imager->new(xsize => 10, ysize => 10, bits => 16);
image_bounds_checks($im);
}
+
+{
+ my $im = Imager->new(xsize => 10, ysize => 10, bits => 16, channels => 3);
+ my @wr_samples = map int(rand 65536), 1..30;
+ is($im->setsamples('y' => 1, data => \@wr_samples, type => '16bit'),
+ 30, "write 16-bit to OO image")
+ or print "# ", $im->errstr, "\n";
+ my @samples;
+ is($im->getsamples(y => 1, target => \@samples, type => '16bit'),
+ 30, "read 16-bit from OO image")
+ or print "# ", $im->errstr, "\n";
+ is_deeply(\@wr_samples, \@samples, "check it matches");
+}
#!perl -w
use strict;
-use Test::More tests => 97;
+use Test::More tests => 98;
BEGIN { use_ok(Imager => qw(:all :handy)) }
require "t/testtools.pl";
my $ooimg = Imager->new(xsize=>200, ysize=>201, bits=>'double');
ok($ooimg, "couldn't make double image");
is($ooimg->bits, 'double', "oo didn't give double image");
+ok(!$ooimg->is_bilevel, 'not monochrome');
# check that the image is copied correctly
my $oocopy = $ooimg->copy;
#!perl -w
# some of this is tested in t01introvert.t too
use strict;
-use Test::More tests => 107;
+use Test::More tests => 121;
BEGIN { use_ok("Imager"); }
use Imager::Test qw(image_bounds_checks);
translate => 'closest');
is($mono->type, 'paletted', "check we get right image type");
is($mono->colorcount, 2, "only 2 colors");
+ my ($is_mono, $ziw) = $mono->is_bilevel;
+ ok($is_mono, "check monochrome check true");
+ is($ziw, 0, "check ziw false");
my @colors = $mono->getcolors;
iscolor($colors[0], $black, "check first entry");
iscolor($colors[1], $white, "check second entry");
is($pixels[2], 0, "check black pixel");
}
+{ # check for the various mono images we accept
+ my $mono_8_bw_3 = Imager->new(xsize => 2, ysize => 2, channels => 3,
+ type => 'paletted');
+ ok($mono_8_bw_3->addcolors(colors => [ qw/000000 FFFFFF/ ]),
+ "mono8bw3 - add colors");
+ ok($mono_8_bw_3->is_bilevel, "it's mono");
+ is(($mono_8_bw_3->is_bilevel)[1], 0, 'zero not white');
+
+ my $mono_8_wb_3 = Imager->new(xsize => 2, ysize => 2, channels => 3,
+ type => 'paletted');
+ ok($mono_8_wb_3->addcolors(colors => [ qw/FFFFFF 000000/ ]),
+ "mono8wb3 - add colors");
+ ok($mono_8_wb_3->is_bilevel, "it's mono");
+ is(($mono_8_wb_3->is_bilevel)[1], 1, 'zero is white');
+
+ my $mono_8_bw_1 = Imager->new(xsize => 2, ysize => 2, channels => 1,
+ type => 'paletted');
+ ok($mono_8_bw_1->addcolors(colors => [ qw/000000 FFFFFF/ ]),
+ "mono8bw - add colors");
+ ok($mono_8_bw_1->is_bilevel, "it's mono");
+ is(($mono_8_bw_1->is_bilevel)[1], 0, 'zero not white');
+
+ my $mono_8_wb_1 = Imager->new(xsize => 2, ysize => 2, channels => 1,
+ type => 'paletted');
+ ok($mono_8_wb_1->addcolors(colors => [ qw/FFFFFF 000000/ ]),
+ "mono8wb - add colors");
+ ok($mono_8_wb_1->is_bilevel, "it's mono");
+ is(($mono_8_wb_1->is_bilevel)[1], 1, 'zero is white');
+}
+
{ # check bounds checking
my $im = Imager->new(xsize => 10, ysize => 10, type=>'paletted');
ok($im->addcolors(colors => [ $black ]), "add color of pixel bounds check writes");
#!perl -w
use strict;
-use Test::More tests => 129;
+use Test::More tests => 213;
use Imager qw(:all);
+use Imager::Test qw(is_image is_image_similar test_image test_image_16 test_image_double);
$^W=1; # warnings during command-line tests
$|=1; # give us some progress in the test harness
init_log("testout/t106tiff.log",1);
cmp_ok($im->errstr, '=~', "format 'tiff' not supported", "check no tiff message");
ok(!grep($_ eq 'tiff', Imager->read_types), "check tiff not in read types");
ok(!grep($_ eq 'tiff', Imager->write_types), "check tiff not in write types");
- skip("no tiff support", 123);
+ skip("no tiff support", 209);
+ }
+
+ my $ver_string = Imager::i_tiff_libversion();
+ ok(my ($full, $major, $minor, $point) =
+ $ver_string =~ /Version +((\d+)\.(\d+).(\d+))/,
+ "extract library version");
+ # make something we can compare
+ my $cmp_ver = sprintf("%03d%03d%03d", $major, $minor, $point);
+ if ($cmp_ver lt '003007000') {
+ diag("You have an old version of libtiff - $full, some tests will be skipped");
}
Imager::i_tags_add($img, "i_xres", 0, "300", 0);
open(FH,">testout/t106.tiff") || die "cannot open testout/t106.tiff for writing\n";
binmode(FH);
my $IO = Imager::io_new_fd(fileno(FH));
- ok(i_writetiff_wiol($img, $IO), "write low level");
+ ok(i_writetiff_wiol($img, $IO), "write low level")
+ or print "# ", Imager->_error_as_msg, "\n";
close(FH);
open(FH,"testout/t106.tiff") or die "cannot open testout/t106.tiff\n";
# paletted reads
my $img4 = Imager->new;
- ok($img4->read(file=>'testimg/comp4.tif'), "reading 4-bit paletted");
+ ok($img4->read(file=>'testimg/comp4.tif'), "reading 4-bit paletted")
+ or print "# ", $img4->errstr, "\n";
is($img4->type, 'paletted', "image isn't paletted");
print "# colors: ", $img4->colorcount,"\n";
cmp_ok($img4->colorcount, '<=', 16, "more than 16 colors!");
my $diff = i_img_diff($img4->{IMG}, $bmp4->{IMG});
print "# diff $diff\n";
ok($diff == 0, "image mismatch");
+ my $img4t = Imager->new;
+ ok($img4t->read(file => 'testimg/comp4t.tif'), "read 4-bit paletted, tiled")
+ or print "# ", $img4t->errstr, "\n";
+ is_image($bmp4, $img4t, "check tiled version matches");
my $img8 = Imager->new;
ok($img8->read(file=>'testimg/comp8.tif'), "reading 8-bit paletted");
is($img8->type, 'paletted', "image isn't paletted");
my $photo_cielab = 8;
my @alpha_images =
(
- [ 'srgb.tif', 3, $photo_rgb ],
- [ 'srgba.tif', 4, $photo_rgb ],
- [ 'srgbaa.tif', 4, $photo_rgb ],
- [ 'scmyk.tif', 3, $photo_cmyk ],
- [ 'scmyka.tif', 4, $photo_cmyk ],
- [ 'scmykaa.tif', 4, $photo_cmyk ],
- [ 'slab.tif', 3, $photo_cielab ],
+ [ 'srgb.tif', 3, $photo_rgb, '003005005' ],
+ [ 'srgba.tif', 4, $photo_rgb, '003005005' ],
+ [ 'srgbaa.tif', 4, $photo_rgb, '003005005' ],
+ [ 'scmyk.tif', 3, $photo_cmyk, '003005005' ],
+ [ 'scmyka.tif', 4, $photo_cmyk, '003005005' ],
+ [ 'scmykaa.tif', 4, $photo_cmyk, '003005005' ],
+ [ 'slab.tif', 3, $photo_cielab, '003006001' ],
);
+
for my $test (@alpha_images) {
- my $im = Imager->new;
- ok($im->read(file => "testimg/$test->[0]"),
- "read alpha test $test->[0]")
+ my ($input, $channels, $photo, $need_ver) = @$test;
+
+ SKIP: {
+ my $skipped = $channels == 4 ? 4 : 3;
+ $need_ver le $cmp_ver
+ or skip("Your ancient tifflib is buggy/limited for this test", $skipped);
+ my $im = Imager->new;
+ ok($im->read(file => "testimg/$input"),
+ "read alpha test $input")
or print "# ", $im->errstr, "\n";
- is($im->getchannels, $test->[1], "channels for $test->[0] match");
- is($im->tags(name=>'tiff_photometric'), $test->[2],
- "photometric for $test->[0] match");
+ is($im->getchannels, $channels, "channels for $input match");
+ is($im->tags(name=>'tiff_photometric'), $photo,
+ "photometric for $input match");
+ $channels == 4
+ or next;
+ my $c = $im->getpixel(x => 0, 'y' => 7);
+ is(($c->rgba)[3], 0, "bottom row should have 0 alpha");
+ }
}
}
ok(grep($_ eq 'tiff', Imager->read_types), "check tiff in read types");
ok(grep($_ eq 'tiff', Imager->write_types), "check tiff in write types");
}
+
+ { # reading tile based images
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/pengtile.tif'), "read tiled image");
+ # compare it
+ my $comp = Imager->new;
+ ok($comp->read(file => 'testimg/penguin-base.ppm'), 'read comparison image');
+ is_image($im, $comp, 'compare them');
+ }
+
+ SKIP:
+ { # failing to read tile based images
+ # we grab our tiled image and patch a tile offset to nowhere
+ ok(open(TIFF, '< testimg/pengtile.tif'), 'open pengtile.tif')
+ or skip 'cannot open testimg/pengtile.tif', 4;
+
+ $cmp_ver ge '0030057'
+ or skip("Your ancient tifflib has bad error handling", 4);
+ binmode TIFF;
+ my $data = do { local $/; <TIFF>; };
+
+ # patch a tile offset
+ substr($data, 0x5AFE, 4) = pack("H*", "1F5C0000");
+
+ #open PIPE, "| bytedump -a | less" or die;
+ #print PIPE $data;
+ #close PIPE;
+
+ my $allow = Imager->new;
+ ok($allow->read(data => $data, allow_incomplete => 1),
+ "read incomplete tiled");
+ ok($allow->tags(name => 'i_incomplete'), 'i_incomplete set');
+ is($allow->tags(name => 'i_lines_read'), 173,
+ 'check i_lines_read set appropriately');
+
+ my $fail = Imager->new;
+ ok(!$fail->read(data => $data), "read fail tiled");
+ }
+
+ { # read 16-bit/sample
+ my $im16 = Imager->new;
+ ok($im16->read(file => 'testimg/rgb16.tif'), "read 16-bit rgb");
+ is($im16->bits, 16, 'got a 16-bit image');
+ my $im16t = Imager->new;
+ ok($im16t->read(file => 'testimg/rgb16t.tif'), "ready 16-bit rgb tiled");
+ is($im16t->bits, 16, 'got a 16-bit image');
+ is_image($im16, $im16t, 'check they match');
+
+ my $grey16 = Imager->new;
+ ok($grey16->read(file => 'testimg/grey16.tif'), "read 16-bit grey")
+ or print "# ", $grey16->errstr, "\n";
+ is($grey16->bits, 16, 'got a 16-bit image');
+ is($grey16->getchannels, 1, 'and its grey');
+ my $comp16 = $im16->convert(matrix => [ [ 0.299, 0.587, 0.114 ] ]);
+ is_image($grey16, $comp16, 'compare grey to converted');
+
+ my $grey32 = Imager->new;
+ ok($grey32->read(file => 'testimg/grey32.tif'), "read 32-bit grey")
+ or print "# ", $grey32->errstr, "\n";
+ is($grey32->bits, 'double', 'got a double image');
+ is($grey32->getchannels, 2, 'and its grey + alpha');
+ is($grey32->tags(name => 'tiff_bitspersample'), 32,
+ "check bits per sample");
+ my $base = test_image_double->convert(preset =>'grey')
+ ->convert(preset => 'addalpha');
+ is_image($grey32, $base, 'compare to original');
+ }
+
+ { # read 16, 32-bit/sample and compare to the original
+ my $rgba = Imager->new;
+ ok($rgba->read(file => 'testimg/srgba.tif'),
+ "read base rgba image");
+ my $rgba16 = Imager->new;
+ ok($rgba16->read(file => 'testimg/srgba16.tif'),
+ "read 16-bit/sample rgba image");
+ is_image($rgba, $rgba16, "check they match");
+ is($rgba16->bits, 16, 'check we got the right type');
+
+ my $rgba32 = Imager->new;
+ ok($rgba32->read(file => 'testimg/srgba32.tif'),
+ "read 32-bit/sample rgba image");
+ is_image($rgba, $rgba32, "check they match");
+ is($rgba32->bits, 'double', 'check we got the right type');
+
+ my $cmyka16 = Imager->new;
+ ok($cmyka16->read(file => 'testimg/scmyka16.tif'),
+ "read cmyk 16-bit")
+ or print "# ", $cmyka16->errstr, "\n";
+ is($cmyka16->bits, 16, "check we got the right type");
+ is_image_similar($rgba, $cmyka16, 10, "check image data");
+ }
+ { # read bi-level
+ my $pbm = Imager->new;
+ ok($pbm->read(file => 'testimg/imager.pbm'), "read original pbm");
+ my $tif = Imager->new;
+ ok($tif->read(file => 'testimg/imager.tif'), "read mono tif");
+ is_image($pbm, $tif, "compare them");
+ is($tif->type, 'paletted', 'check image type');
+ is($tif->colorcount, 2, 'check we got a "mono" image');
+ }
+
+ { # check alpha channels scaled correctly for fallback handler
+ my $im = Imager->new;
+ ok($im->read(file=>'testimg/alpha.tif'), 'read alpha check image');
+ my @colors =
+ (
+ [ 0, 0, 0 ],
+ [ 255, 255, 255 ],
+ [ 127, 0, 127 ],
+ [ 127, 127, 0 ],
+ );
+ my @alphas = ( 255, 191, 127, 63 );
+ my $ok = 1;
+ my $msg = 'alpha check ok';
+ CHECKER:
+ for my $y (0 .. 3) {
+ for my $x (0 .. 3) {
+ my $c = $im->getpixel(x => $x, 'y' => $y);
+ my @c = $c->rgba;
+ my $alpha = pop @c;
+ if ($alpha != $alphas[$y]) {
+ $ok = 0;
+ $msg = "($x,$y) alpha mismatch $alpha vs $alphas[$y]";
+ last CHECKER;
+ }
+ my $expect = $colors[$x];
+ for my $ch (0 .. 2) {
+ if (abs($expect->[$ch]-$c[$ch]) > 3) {
+ $ok = 0;
+ $msg = "($x,$y)[$ch] color mismatch got $c[$ch] vs expected $expect->[$ch]";
+ last CHECKER;
+ }
+ }
+ }
+ }
+ ok($ok, $msg);
+ }
+
+ { # check alpha channels scaled correctly for greyscale
+ my $im = Imager->new;
+ ok($im->read(file=>'testimg/gralpha.tif'), 'read alpha check grey image');
+ my @greys = ( 0, 255, 52, 112 );
+ my @alphas = ( 255, 191, 127, 63 );
+ my $ok = 1;
+ my $msg = 'alpha check ok';
+ CHECKER:
+ for my $y (0 .. 3) {
+ for my $x (0 .. 3) {
+ my $c = $im->getpixel(x => $x, 'y' => $y);
+ my ($grey, $alpha) = $c->rgba;
+ if ($alpha != $alphas[$y]) {
+ $ok = 0;
+ $msg = "($x,$y) alpha mismatch $alpha vs $alphas[$y]";
+ last CHECKER;
+ }
+ if (abs($greys[$x] - $grey) > 3) {
+ $ok = 0;
+ $msg = "($x,$y) grey mismatch $grey vs $greys[$x]";
+ last CHECKER;
+ }
+ }
+ }
+ ok($ok, $msg);
+ }
+
+ { # 16-bit writes
+ my $orig = test_image_16();
+ my $data;
+ ok($orig->write(data => \$data, type => 'tiff',
+ tiff_compression => 'none'), "write 16-bit/sample");
+ my $im = Imager->new;
+ ok($im->read(data => $data), "read it back");
+ is_image($im, $orig, "check read data matches");
+ is($im->tags(name => 'tiff_bitspersample'), 16, "correct bits");
+ is($im->bits, 16, 'check image bits');
+ is($im->tags(name => 'tiff_photometric'), 2, "correct photometric");
+ is($im->tags(name => 'tiff_compression'), 'none', "no compression");
+ is($im->getchannels, 3, 'correct channels');
+ }
+
+ { # 8-bit writes
+ # and check compression
+ my $compress = Imager::i_tiff_has_compression('lzw') ? 'lzw' : 'packbits';
+ my $orig = test_image()->convert(preset=>'grey')
+ ->convert(preset => 'addalpha');
+ my $data;
+ ok($orig->write(data => \$data, type => 'tiff',
+ tiff_compression=> $compress),
+ "write 8 bit");
+ my $im = Imager->new;
+ ok($im->read(data => $data), "read it back");
+ is_image($im, $orig, "check read data matches");
+ is($im->tags(name => 'tiff_bitspersample'), 8, 'correct bits');
+ is($im->bits, 8, 'check image bits');
+ is($im->tags(name => 'tiff_photometric'), 1, 'correct photometric');
+ is($im->tags(name => 'tiff_compression'), $compress,
+ "$compress compression");
+ is($im->getchannels, 2, 'correct channels');
+ }
+
+ { # double writes
+ my $orig = test_image_double()->convert(preset=>'addalpha');
+ my $data;
+ ok($orig->write(data => \$data, type => 'tiff',
+ tiff_compression => 'none'),
+ "write 32-bit/sample from double")
+ or print "# ", $orig->errstr, "\n";
+ my $im = Imager->new;
+ ok($im->read(data => $data), "read it back");
+ is_image($im, $orig, "check read data matches");
+ is($im->tags(name => 'tiff_bitspersample'), 32, "correct bits");
+ is($im->bits, 'double', 'check image bits');
+ is($im->tags(name => 'tiff_photometric'), 2, "correct photometric");
+ is($im->tags(name => 'tiff_compression'), 'none', "no compression");
+ is($im->getchannels, 4, 'correct channels');
+ }
+
+ { # bilevel
+ my $im = test_image()->convert(preset => 'grey')
+ ->to_paletted(make_colors => 'mono',
+ translate => 'errdiff');
+ my $faxdata;
+
+ # fax compression is written as miniswhite
+ ok($im->write(data => \$faxdata, type => 'tiff',
+ tiff_compression => 'fax3'),
+ "write bilevel fax compressed");
+ my $fax = Imager->new;
+ ok($fax->read(data => $faxdata), "read it back");
+ ok($fax->is_bilevel, "got a bi-level image back");
+ is($fax->tags(name => 'tiff_compression'), 'fax3',
+ "check fax compression used");
+ is_image($fax, $im, "compare to original");
+
+ # other compresion written as minisblack
+ my $packdata;
+ ok($im->write(data => \$packdata, type => 'tiff',
+ tiff_compression => 'jpeg'),
+ "write bilevel packbits compressed");
+ my $packim = Imager->new;
+ ok($packim->read(data => $packdata), "read it back");
+ ok($packim->is_bilevel, "got a bi-level image back");
+ is($packim->tags(name => 'tiff_compression'), 'packbits',
+ "check fallback compression used");
+ is_image($packim, $im, "compare to original");
+ }
+
+ { # fallback handling of tiff
+ is(Imager::i_tiff_has_compression('none'), 1, "can always do uncompresed");
+ is(Imager::i_tiff_has_compression('xxx'), '', "can't do xxx compression");
+ }
}
my $imbase16 = $imbase->to_rgb16;
my $gauss16 = test($imbase16, {type=>'gaussian', stddev=>5 },
'testout/t61_gaussian16.ppm');
- is_image_similar($gauss, $gauss16, 200000, "8 and 16 gaussian match");
+ is_image_similar($gauss, $gauss16, 250000, "8 and 16 gaussian match");
}
}
-sub NCF {
- return Imager::Color::Float->new(@_);
-}
#include "imager.h"
-#include "tiffio.h"
+#include <tiffio.h>
#include "iolayer.h"
#include "imageri.h"
+/* needed to implement our substitute TIFFIsCODECConfigured */
+#if TIFFLIB_VERSION < 20031121
+#include "tiffconf.h"
+static int TIFFIsCODECConfigured(uint16 scheme);
+#endif
+
/*
=head1 NAME
((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
+#define CLAMP8(x) ((x) < 0 ? 0 : (x) > 255 ? 255 : (x))
+#define CLAMP16(x) ((x) < 0 ? 0 : (x) > 65535 ? 65535 : (x))
+
struct tag_name {
char *name;
uint32 tag;
};
+static i_img *read_one_rgb_tiled(TIFF *tif, int width, int height, int allow_incomplete);
+static i_img *read_one_rgb_lines(TIFF *tif, int width, int height, int allow_incomplete);
+
static struct tag_name text_tag_names[] =
{
{ "tiff_documentname", TIFFTAG_DOCUMENTNAME, },
{ "tiff_hostcomputer", TIFFTAG_HOSTCOMPUTER, },
};
+static struct tag_name
+compress_values[] =
+ {
+ { "none", COMPRESSION_NONE },
+ { "ccittrle", COMPRESSION_CCITTRLE },
+ { "fax3", COMPRESSION_CCITTFAX3 },
+ { "t4", COMPRESSION_CCITT_T4 },
+ { "fax4", COMPRESSION_CCITTFAX4 },
+ { "t6", COMPRESSION_CCITT_T6 },
+ { "lzw", COMPRESSION_LZW },
+ { "jpeg", COMPRESSION_JPEG },
+ { "packbits", COMPRESSION_PACKBITS },
+ { "deflate", COMPRESSION_ADOBE_DEFLATE },
+ { "zip", COMPRESSION_ADOBE_DEFLATE },
+ { "oldzip", COMPRESSION_DEFLATE },
+ { "ccittrlew", COMPRESSION_CCITTRLEW },
+ };
+
+static const int compress_value_count =
+ sizeof(compress_values) / sizeof(*compress_values);
+
+typedef struct read_state_tag read_state_t;
+/* the setup function creates the image object, allocates the line buffer */
+typedef int (*read_setup_t)(read_state_t *state);
+
+/* the putter writes the image data provided by the getter to the
+ image, x, y, width, height describe the target area of the image,
+ extras is the extra number of pixels stored for each scanline in
+ the raster buffer, (for tiles against the right side of the
+ image) */
+
+typedef int (*read_putter_t)(read_state_t *state, int x, int y, int width,
+ int height, int extras);
+
+/* reads from a tiled or strip image and calls the putter.
+ This may need a second type for handling non-contiguous images
+ at some point */
+typedef int (*read_getter_t)(read_state_t *state, read_putter_t putter);
+
+struct read_state_tag {
+ TIFF *tif;
+ i_img *img;
+ void *raster;
+ unsigned long pixels_read;
+ int allow_incomplete;
+ void *line_buf;
+ uint32 width, height;
+ uint16 bits_per_sample;
+ uint16 photometric;
+
+ /* the total number of channels (samples per pixel) */
+ int samples_per_pixel;
+
+ /* if non-zero, which channel is the alpha channel, typically 3 for rgb */
+ int alpha_chan;
+
+ /* whether or not to scale the color channels based on the alpha
+ channel. TIFF has 2 types of alpha channel, if the alpha channel
+ we use is EXTRASAMPLE_ASSOCALPHA then the color data will need to
+ be scaled to match Imager's conventions */
+ int scale_alpha;
+};
+
+static int tile_contig_getter(read_state_t *state, read_putter_t putter);
+static int strip_contig_getter(read_state_t *state, read_putter_t putter);
+
+static int setup_paletted(read_state_t *state);
+static int paletted_putter8(read_state_t *, int, int, int, int, int);
+static int paletted_putter4(read_state_t *, int, int, int, int, int);
+
+static int setup_16_rgb(read_state_t *state);
+static int setup_16_grey(read_state_t *state);
+static int putter_16(read_state_t *, int, int, int, int, int);
+
+static int setup_8_rgb(read_state_t *state);
+static int setup_8_grey(read_state_t *state);
+static int putter_8(read_state_t *, int, int, int, int, int);
+
+static int setup_32_rgb(read_state_t *state);
+static int setup_32_grey(read_state_t *state);
+static int putter_32(read_state_t *, int, int, int, int, int);
+
+static int setup_bilevel(read_state_t *state);
+static int putter_bilevel(read_state_t *, int, int, int, int, int);
+
+static int setup_cmyk8(read_state_t *state);
+static int putter_cmyk8(read_state_t *, int, int, int, int, int);
+
+static int setup_cmyk16(read_state_t *state);
+static int putter_cmyk16(read_state_t *, int, int, int, int, int);
+
static const int text_tag_count =
sizeof(text_tag_names) / sizeof(*text_tag_names);
static void error_handler(char const *module, char const *fmt, va_list ap) {
+ mm_log((1, "tiff error fmt %s\n", fmt));
i_push_errorvf(0, fmt, ap);
}
#else
vsprintf(buf, fmt, ap);
#endif
+ mm_log((1, "tiff warning %s\n", buf));
+
if (!warn_buffer || strlen(warn_buffer)+strlen(buf)+2 > warn_buffer_size) {
int new_size = warn_buffer_size + strlen(buf) + 2;
char *old_buffer = warn_buffer;
static int save_tiff_tags(TIFF *tif, i_img *im);
-static void expand_4bit_hl(unsigned char *buf, int count);
-
-static void pack_4bit_hl(unsigned char *buf, int count);
+static void
+pack_4bit_to(unsigned char *dest, const unsigned char *src, int count);
static toff_t sizeproc(thandle_t x) {
static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
i_img *im;
uint32 width, height;
- uint16 channels;
- uint32* raster = NULL;
+ uint16 samples_per_pixel;
int tiled, error;
float xres, yres;
uint16 resunit;
int gotXres, gotYres;
uint16 photometric;
uint16 bits_per_sample;
+ uint16 planar_config;
+ uint16 inkset;
+ uint16 compress;
int i;
- int ch;
+ read_state_t state;
+ read_setup_t setupf = NULL;
+ read_getter_t getterf = NULL;
+ read_putter_t putterf = NULL;
error = 0;
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
- TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &channels);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
tiled = TIFFIsTiled(tif);
TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric);
TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset);
- mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
+ mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, samples_per_pixel));
mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not "));
mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not "));
- /* separated defaults to CMYK, but if the user is using some strange
- ink system we can't work out the color anyway */
- if (photometric == PHOTOMETRIC_SEPARATED && channels >= 4) {
- /* TIFF can have more than one alpha channel on an image,
- but Imager can't, only store the first one */
-
- channels = channels == 4 ? 3 : 4;
-
- /* unfortunately the RGBA functions don't try to deal with the alpha
- channel on CMYK images, at some point I'm planning on expanding
- TIFF support to handle 16-bit/sample images and I'll deal with
- it then */
+ /* yes, this if() is horrible */
+ if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) {
+ setupf = setup_paletted;
+ if (bits_per_sample == 8)
+ putterf = paletted_putter8;
+ else if (bits_per_sample == 4)
+ putterf = paletted_putter4;
+ else
+ mm_log((1, "unsupported paletted bits_per_sample %d\n", bits_per_sample));
}
+ else if (bits_per_sample == 16
+ && photometric == PHOTOMETRIC_RGB
+ && samples_per_pixel >= 3) {
+ setupf = setup_16_rgb;
+ putterf = putter_16;
+ }
+ else if (bits_per_sample == 16
+ && photometric == PHOTOMETRIC_MINISBLACK) {
+ setupf = setup_16_grey;
+ putterf = putter_16;
+ }
+ else if (bits_per_sample == 8
+ && photometric == PHOTOMETRIC_MINISBLACK) {
+ setupf = setup_8_grey;
+ putterf = putter_8;
+ }
+ else if (bits_per_sample == 8
+ && photometric == PHOTOMETRIC_RGB) {
+ setupf = setup_8_rgb;
+ putterf = putter_8;
+ }
+ else if (bits_per_sample == 32
+ && photometric == PHOTOMETRIC_RGB
+ && samples_per_pixel >= 3) {
+ setupf = setup_32_rgb;
+ putterf = putter_32;
+ }
+ else if (bits_per_sample == 32
+ && photometric == PHOTOMETRIC_MINISBLACK) {
+ setupf = setup_32_grey;
+ putterf = putter_32;
+ }
+ else if (bits_per_sample == 1
+ && (photometric == PHOTOMETRIC_MINISBLACK
+ || photometric == PHOTOMETRIC_MINISWHITE)
+ && samples_per_pixel == 1) {
+ setupf = setup_bilevel;
+ putterf = putter_bilevel;
+ }
+ else if (bits_per_sample == 8
+ && photometric == PHOTOMETRIC_SEPARATED
+ && inkset == INKSET_CMYK
+ && samples_per_pixel >= 4) {
+ setupf = setup_cmyk8;
+ putterf = putter_cmyk8;
+ }
+ else if (bits_per_sample == 16
+ && photometric == PHOTOMETRIC_SEPARATED
+ && inkset == INKSET_CMYK
+ && samples_per_pixel >= 4) {
+ setupf = setup_cmyk16;
+ putterf = putter_cmyk16;
+ }
+ if (tiled) {
+ if (planar_config == PLANARCONFIG_CONTIG)
+ getterf = tile_contig_getter;
+ }
+ else {
+ if (planar_config == PLANARCONFIG_CONTIG)
+ getterf = strip_contig_getter;
+ }
+ if (setupf && getterf && putterf) {
+ unsigned long total_pixels = (unsigned long)width * height;
+ memset(&state, 0, sizeof(state));
+ state.tif = tif;
+ state.allow_incomplete = allow_incomplete;
+ state.width = width;
+ state.height = height;
+ state.bits_per_sample = bits_per_sample;
+ state.samples_per_pixel = samples_per_pixel;
+ state.photometric = photometric;
+
+ if (!setupf(&state))
+ return NULL;
+ if (!getterf(&state, putterf) || !state.pixels_read) {
+ if (state.img)
+ i_img_destroy(state.img);
+ if (state.raster)
+ _TIFFfree(state.raster);
+ if (state.line_buf)
+ myfree(state.line_buf);
+
+ return NULL;
+ }
- /* TIFF images can have more than one alpha channel, but Imager can't
- this ignores the possibility of 2 channel images with 2 alpha,
- but there's not much I can do about that */
- if (channels > 4)
- channels = 4;
-
- if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) {
- channels = 3;
- im = i_img_pal_new(width, height, channels, 256);
+ if (allow_incomplete && state.pixels_read < total_pixels) {
+ i_tags_setn(&(state.img->tags), "i_incomplete", 1);
+ i_tags_setn(&(state.img->tags), "i_lines_read",
+ state.pixels_read / width);
+ }
+ im = state.img;
+
+ if (state.raster)
+ _TIFFfree(state.raster);
+ if (state.line_buf)
+ myfree(state.line_buf);
}
else {
- im = i_img_empty_ch(NULL, width, height, channels);
+ if (tiled) {
+ im = read_one_rgb_tiled(tif, width, height, allow_incomplete);
+ }
+ else {
+ im = read_one_rgb_lines(tif, width, height, allow_incomplete);
+ }
}
if (!im)
/* general metadata */
i_tags_addn(&im->tags, "tiff_bitspersample", 0, bits_per_sample);
i_tags_addn(&im->tags, "tiff_photometric", 0, photometric);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compress);
/* resolution tags */
TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
i_tags_add(&im->tags, "i_warning", 0, warn_buffer, -1, 0);
*warn_buffer = '\0';
}
-
- /* TIFFPrintDirectory(tif, stdout, 0); good for debugging */
-
- if (photometric == PHOTOMETRIC_PALETTE &&
- (bits_per_sample == 4 || bits_per_sample == 8)) {
- uint16 *maps[3];
- char used[256];
- int maxused;
- uint32 row, col;
- unsigned char *buffer;
-
- if (!TIFFGetField(tif, TIFFTAG_COLORMAP, maps+0, maps+1, maps+2)) {
- i_push_error(0, "Cannot get colormap for paletted image");
- i_img_destroy(im);
- return NULL;
- }
- buffer = (unsigned char *)_TIFFmalloc(width+2);
- if (!buffer) {
- i_push_error(0, "out of memory");
- i_img_destroy(im);
- return NULL;
- }
- row = 0;
- memset(used, 0, sizeof(used));
- while (row < height && TIFFReadScanline(tif, buffer, row, 0) > 0) {
- if (bits_per_sample == 4)
- expand_4bit_hl(buffer, (width+1)/2);
- for (col = 0; col < width; ++col) {
- used[buffer[col]] = 1;
- }
- i_ppal(im, 0, width, row, buffer);
- ++row;
- }
- if (row < height) {
- if (allow_incomplete) {
- i_tags_setn(&im->tags, "i_lines_read", row);
- }
- else {
- i_img_destroy(im);
- _TIFFfree(buffer);
- return NULL;
- }
- error = 1;
- }
- /* Ideally we'd optimize the palette, but that could be expensive
- since we'd have to re-index every pixel.
-
- Optimizing the palette (even at this level) might not
- be what the user wants, so I don't do it.
-
- We'll add a function to optimize a paletted image instead.
- */
- maxused = (1 << bits_per_sample)-1;
- if (!error) {
- while (maxused >= 0 && !used[maxused])
- --maxused;
- }
- for (i = 0; i < 1 << bits_per_sample; ++i) {
- i_color c;
- for (ch = 0; ch < 3; ++ch) {
- c.channel[ch] = Sample16To8(maps[ch][i]);
- }
- i_addcolors(im, &c, 1);
+
+ for (i = 0; i < compress_value_count; ++i) {
+ if (compress_values[i].tag == compress) {
+ i_tags_add(&im->tags, "tiff_compression", 0, compress_values[i].name, -1, 0);
+ break;
}
- _TIFFfree(buffer);
}
- else {
- if (tiled) {
- int ok = 1;
- uint32 row, col;
- uint32 tile_width, tile_height;
-
- TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width);
- TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
- mm_log((1, "i_readtiff_wiol: tile_width=%d, tile_height=%d\n", tile_width, tile_height));
-
- raster = (uint32*)_TIFFmalloc(tile_width * tile_height * sizeof (uint32));
- if (!raster) {
- i_img_destroy(im);
- i_push_error(0, "No space for raster buffer");
- return NULL;
- }
-
- for( row = 0; row < height; row += tile_height ) {
- for( col = 0; ok && col < width; col += tile_width ) {
- uint32 i_row, x, newrows, newcols;
-
- /* Read the tile into an RGBA array */
- if (!TIFFReadRGBATile(tif, col, row, raster)) {
- ok = 0;
- break;
- }
- newrows = (row+tile_height > height) ? height-row : tile_height;
- mm_log((1, "i_readtiff_wiol: newrows=%d\n", newrows));
- newcols = (col+tile_width > width ) ? width-row : tile_width;
- for( i_row = 0; i_row < tile_height; i_row++ ) {
- for(x = 0; x < newcols; x++) {
- i_color val;
- uint32 temp = raster[x+tile_width*(tile_height-i_row-1)];
- val.rgba.r = TIFFGetR(temp);
- val.rgba.g = TIFFGetG(temp);
- val.rgba.b = TIFFGetB(temp);
- val.rgba.a = TIFFGetA(temp);
- i_ppix(im, col+x, row+i_row, &val);
- }
- }
- }
- }
- } else {
- uint32 rowsperstrip, row;
- int rc = TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
- mm_log((1, "i_readtiff_wiol: rowsperstrip=%d rc = %d\n", rowsperstrip, rc));
- if (rc != 1 || rowsperstrip==-1) {
- rowsperstrip = height;
- }
-
- raster = (uint32*)_TIFFmalloc(width * rowsperstrip * sizeof (uint32));
- if (!raster) {
- i_img_destroy(im);
- i_push_error(0, "No space for raster buffer");
- return NULL;
- }
-
- for( row = 0; row < height; row += rowsperstrip ) {
- uint32 newrows, i_row;
-
- if (!TIFFReadRGBAStrip(tif, row, raster)) {
- if (allow_incomplete) {
- i_tags_setn(&im->tags, "i_lines_read", row);
- error++;
- break;
- }
- else {
- i_push_error(0, "could not read TIFF image strip");
- _TIFFfree(raster);
- i_img_destroy(im);
- return NULL;
- }
- }
-
- newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip;
- mm_log((1, "newrows=%d\n", newrows));
-
- for( i_row = 0; i_row < newrows; i_row++ ) {
- uint32 x;
- for(x = 0; x<width; x++) {
- i_color val;
- uint32 temp = raster[x+width*(newrows-i_row-1)];
- val.rgba.r = TIFFGetR(temp);
- val.rgba.g = TIFFGetG(temp);
- val.rgba.b = TIFFGetB(temp);
- val.rgba.a = TIFFGetA(temp);
- i_ppix(im, x, i_row+row, &val);
- }
- }
- }
- }
- }
- if (error) {
- mm_log((1, "i_readtiff_wiol: error during reading\n"));
- i_tags_setn(&im->tags, "i_incomplete", 1);
- }
- if (raster)
- _TIFFfree( raster );
-
return im;
}
return 1;
}
-undef_int
-i_writetiff_low(TIFF *tif, i_img *im) {
- uint32 width, height;
- uint16 channels;
- uint16 predictor = 0;
- int quality = 75;
- int jpegcolormode = JPEGCOLORMODE_RGB;
- uint16 compression = COMPRESSION_PACKBITS;
- i_color val;
- uint16 photometric;
- uint32 rowsperstrip = (uint32) -1; /* Let library pick default */
- unsigned char *linebuf = NULL;
- uint32 y;
- tsize_t linebytes;
- int ch, ci, rc;
- uint32 x;
- int got_xres, got_yres, aspect_only, resunit;
- double xres, yres;
- uint16 bitspersample = 8;
- uint16 samplesperpixel;
- uint16 *colors = NULL;
-
- width = im->xsize;
- height = im->ysize;
- channels = im->channels;
+static uint16
+find_compression(char const *name, uint16 *compress) {
+ int i;
- switch (channels) {
- case 1:
- photometric = PHOTOMETRIC_MINISBLACK;
- break;
- case 3:
- photometric = PHOTOMETRIC_RGB;
- if (compression == COMPRESSION_JPEG && jpegcolormode == JPEGCOLORMODE_RGB) photometric = PHOTOMETRIC_YCBCR;
- else if (im->type == i_palette_type) {
- photometric = PHOTOMETRIC_PALETTE;
+ for (i = 0; i < compress_value_count; ++i) {
+ if (strcmp(compress_values[i].name, name) == 0) {
+ *compress = (uint16)compress_values[i].tag;
+ return 1;
}
- break;
- default:
- /* This means a colorspace we don't handle yet */
- mm_log((1, "i_writetiff_wiol: don't handle %d channel images.\n", channels));
- return 0;
}
+ *compress = COMPRESSION_NONE;
- /* 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));*/
+ return 0;
+}
- mm_log((1, "i_writetiff_low: 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;
+static uint16
+get_compression(i_img *im, uint16 def_compress) {
+ int entry;
+ int value;
+
+ if (i_tags_find(&im->tags, "tiff_compression", 0, &entry)
+ && im->tags.tags[entry].data) {
+ uint16 compress;
+ if (find_compression(im->tags.tags[entry].data, &compress)
+ && TIFFIsCODECConfigured(compress))
+ return compress;
}
- if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) ) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height));
- return 0;
+ if (i_tags_get_int(&im->tags, "tiff_compression", 0, &value)) {
+ if ((uint16)value == value
+ && TIFFIsCODECConfigured((uint16)value))
+ return (uint16)value;
}
- if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n"));
- return 0;
+
+ return def_compress;
+}
+
+int
+i_tiff_has_compression(const char *name) {
+ uint16 compress;
+
+ if (!find_compression(name, &compress))
+ return 0;
+
+ return TIFFIsCODECConfigured(compress);
+}
+
+static int
+set_base_tags(TIFF *tif, i_img *im, uint16 compress, uint16 photometric,
+ uint16 bits_per_sample, uint16 samples_per_pixel) {
+ double xres, yres;
+ int resunit;
+ int got_xres, got_yres;
+ int aspect_only;
+
+ if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, im->xsize)) {
+ i_push_error(0, "write TIFF: setting width tag");
+ return 0;
}
- if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n"));
- return 0;
+ if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, im->ysize)) {
+ i_push_error(0, "write TIFF: setting length tag");
+ 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_ORIENTATION, ORIENTATION_TOPLEFT)) {
+ i_push_error(0, "write TIFF: setting orientation tag");
+ return 0;
}
- if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, compression)) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression));
- return 0;
+ if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) {
+ i_push_error(0, "write TIFF: setting planar configuration tag");
+ return 0;
}
- samplesperpixel = channels;
- if (photometric == PHOTOMETRIC_PALETTE) {
- uint16 *out[3];
- i_color c;
- int count = i_colorcount(im);
- int size;
- int ch, i;
-
- samplesperpixel = 1;
- if (count > 16)
- bitspersample = 8;
- else
- bitspersample = 4;
- size = 1 << bitspersample;
- colors = (uint16 *)_TIFFmalloc(sizeof(uint16) * 3 * size);
- out[0] = colors;
- out[1] = colors + size;
- out[2] = colors + 2 * size;
-
- for (i = 0; i < count; ++i) {
- i_getcolors(im, i, &c, 1);
- for (ch = 0; ch < 3; ++ch)
- out[ch][i] = c.channel[ch] * 257;
- }
- for (; i < size; ++i) {
- for (ch = 0; ch < 3; ++ch)
- out[ch][i] = 0;
- }
- if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample)) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=%d\n",
- bitspersample));
- return 0;
- }
- if (!TIFFSetField(tif, TIFFTAG_COLORMAP, out[0], out[1], out[2])) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField colormap\n"));
- return 0;
- }
+ if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric)) {
+ i_push_error(0, "write TIFF: setting photometric tag");
+ return 0;
}
- else {
- if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample)) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=%d\n",
- bitspersample));
- return 0;
- }
+ if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, compress)) {
+ i_push_error(0, "write TIFF: setting compression tag");
+ return 0;
}
- if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel)) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField samplesperpixel=%d failed\n", samplesperpixel));
+ if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample)) {
+ i_push_error(0, "write TIFF: setting bits per sample tag");
return 0;
}
-
- 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;
- }
- break;
- case COMPRESSION_LZW:
- mm_log((1, "i_writetiff_wiol: lzw compression\n"));
- break;
- 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;
- }
- break;
- case COMPRESSION_PACKBITS:
- mm_log((1, "i_writetiff_wiol: packbits compression\n"));
- break;
- default:
- mm_log((1, "i_writetiff_wiol: unknown compression %d\n", compression));
+ if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel)) {
+ i_push_error(0, "write TIFF: setting samples per pixel tag");
return 0;
}
-
- linebytes = channels * width;
- linebytes = TIFFScanlineSize(tif) > linebytes ? linebytes
- : TIFFScanlineSize(tif);
- /* working space for the scanlines - we go from 8-bit/pixel to 4 */
- if (photometric == PHOTOMETRIC_PALETTE && bitspersample == 4)
- linebytes += linebytes + 1;
- linebuf = (unsigned char *)_TIFFmalloc(linebytes);
-
- if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, rowsperstrip))) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField rowsperstrip=%d\n", rowsperstrip)); return 0; }
-
- TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
- TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
-
- mm_log((1, "i_writetiff_wiol: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
- mm_log((1, "i_writetiff_wiol: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
- mm_log((1, "i_writetiff_wiol: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
- mm_log((1, "i_writetiff_wiol: bitspersample = %d\n", bitspersample));
got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
}
}
if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres)) {
- i_push_error(0, "cannot set TIFFTAG_XRESOLUTION tag");
+ i_push_error(0, "write TIFF: setting xresolution tag");
return 0;
}
if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres)) {
- i_push_error(0, "cannot set TIFFTAG_YRESOLUTION tag");
+ i_push_error(0, "write TIFF: setting yresolution tag");
return 0;
}
if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16)resunit)) {
- i_push_error(0, "cannot set TIFFTAG_RESOLUTIONUNIT tag");
+ i_push_error(0, "write TIFF: setting resolutionunit tag");
return 0;
}
}
- if (!save_tiff_tags(tif, im)) {
- return 0;
+ return 1;
+}
+
+static int
+write_one_bilevel(TIFF *tif, i_img *im, int zero_is_white) {
+ uint16 compress = get_compression(im, COMPRESSION_PACKBITS);
+ uint16 photometric;
+ unsigned char *in_row;
+ unsigned char *out_row;
+ unsigned out_size;
+ int x, y;
+ int invert;
+
+ mm_log((1, "tiff - write_one_bilevel(tif %p, im %p, zero_is_white %d)\n",
+ tif, im, zero_is_white));
+
+ /* ignore a silly choice */
+ if (compress == COMPRESSION_JPEG)
+ compress = COMPRESSION_PACKBITS;
+
+ switch (compress) {
+ case COMPRESSION_CCITTRLE:
+ case COMPRESSION_CCITTFAX3:
+ case COMPRESSION_CCITTFAX4:
+ /* natural fax photometric */
+ photometric = PHOTOMETRIC_MINISWHITE;
+ break;
+
+ default:
+ /* natural for most computer images */
+ photometric = PHOTOMETRIC_MINISBLACK;
+ break;
}
- if (photometric == PHOTOMETRIC_PALETTE) {
- for (y = 0; y < height; ++y) {
- i_gpal(im, 0, width, y, linebuf);
- if (bitspersample == 4)
- pack_4bit_hl(linebuf, width);
- if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
- mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
- if (linebuf) _TIFFfree(linebuf);
- if (colors) _TIFFfree(colors);
- return 0;
- }
- }
+ if (!set_base_tags(tif, im, compress, photometric, 1, 1))
+ return 0;
+
+ if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) {
+ i_push_error(0, "write TIFF: setting rows per strip tag");
+ return 0;
}
- else {
- for (y=0; y<height; y++) {
- ci = 0;
- for(x=0; x<width; x++) {
- (void) i_gpix(im, x, y,&val);
- for(ch=0; ch<channels; ch++)
- linebuf[ci++] = val.channel[ch];
+
+ out_size = TIFFScanlineSize(tif);
+ out_row = (unsigned char *)_TIFFmalloc( out_size );
+ in_row = mymalloc(im->xsize);
+
+ invert = (photometric == PHOTOMETRIC_MINISWHITE) != (zero_is_white != 0);
+
+ for (y = 0; y < im->ysize; ++y) {
+ int mask = 0x80;
+ unsigned char *outp = out_row;
+ memset(out_row, 0, out_size);
+ i_gpal(im, 0, im->xsize, y, in_row);
+ for (x = 0; x < im->xsize; ++x) {
+ if (invert ? !in_row[x] : in_row[x]) {
+ *outp |= mask;
}
- if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
- mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
- if (linebuf) _TIFFfree(linebuf);
- if (colors) _TIFFfree(colors);
- return 0;
+ mask >>= 1;
+ if (!mask) {
+ ++outp;
+ mask = 0x80;
}
}
+ if (TIFFWriteScanline(tif, out_row, y, 0) < 0) {
+ _TIFFfree(out_row);
+ myfree(in_row);
+ i_push_error(0, "write TIFF: write scan line failed");
+ return 0;
+ }
}
- if (linebuf) _TIFFfree(linebuf);
- if (colors) _TIFFfree(colors);
- return 1;
-}
-
-/*
-=item i_writetiff_multi_wiol(ig, imgs, count, fine_mode)
-Stores an image in the iolayer object.
+ _TIFFfree(out_row);
+ myfree(in_row);
- ig - io_object that defines source to write to
- imgs,count - the images to write
+ return 1;
+}
-=cut
-*/
+static int
+set_palette(TIFF *tif, i_img *im, int size) {
+ int count;
+ uint16 *colors;
+ uint16 *out[3];
+ i_color c;
+ int i, ch;
+
+ colors = (uint16 *)_TIFFmalloc(sizeof(uint16) * 3 * size);
+ out[0] = colors;
+ out[1] = colors + size;
+ out[2] = colors + 2 * size;
+
+ count = i_colorcount(im);
+ for (i = 0; i < count; ++i) {
+ i_getcolors(im, i, &c, 1);
+ for (ch = 0; ch < 3; ++ch)
+ out[ch][i] = c.channel[ch] * 257;
+ }
+ for (; i < size; ++i) {
+ for (ch = 0; ch < 3; ++ch)
+ out[ch][i] = 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_COLORMAP, out[0], out[1], out[2])) {
+ _TIFFfree(colors);
+ i_push_error(0, "write TIFF: setting color map");
+ return 0;
+ }
+ _TIFFfree(colors);
+
+ return 1;
+}
-undef_int
-i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
- TIFF* tif;
- TIFFErrorHandler old_handler;
- int i;
+static int
+write_one_paletted8(TIFF *tif, i_img *im) {
+ uint16 compress = get_compression(im, COMPRESSION_PACKBITS);
+ unsigned char *out_row;
+ unsigned out_size;
+ int y;
- old_handler = TIFFSetErrorHandler(error_handler);
+ mm_log((1, "tiff - write_one_paletted8(tif %p, im %p)\n", tif, im));
- 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));
+ /* ignore a silly choice */
+ if (compress == COMPRESSION_JPEG ||
+ compress == COMPRESSION_CCITTRLE ||
+ compress == COMPRESSION_CCITTFAX3 ||
+ compress == COMPRESSION_CCITTFAX4)
+ compress = COMPRESSION_PACKBITS;
- /* 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,
- ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
- (TIFFMapFileProc) comp_mmap,
- (TIFFUnmapFileProc) comp_munmap);
-
+ if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) {
+ i_push_error(0, "write TIFF: setting rows per strip tag");
+ return 0;
+ }
+ if (!set_base_tags(tif, im, compress, PHOTOMETRIC_PALETTE, 8, 1))
+ return 0;
- if (!tif) {
- mm_log((1, "i_writetiff_multi_wiol: Unable to open tif file for writing\n"));
- i_push_error(0, "Could not create TIFF object");
- TIFFSetErrorHandler(old_handler);
+ if (!set_palette(tif, im, 256))
return 0;
- }
- for (i = 0; i < count; ++i) {
- if (!i_writetiff_low(tif, imgs[i])) {
- TIFFClose(tif);
- TIFFSetErrorHandler(old_handler);
+ out_size = TIFFScanlineSize(tif);
+ out_row = (unsigned char *)_TIFFmalloc( out_size );
+
+ for (y = 0; y < im->ysize; ++y) {
+ i_gpal(im, 0, im->xsize, y, out_row);
+ if (TIFFWriteScanline(tif, out_row, y, 0) < 0) {
+ _TIFFfree(out_row);
+ i_push_error(0, "write TIFF: write scan line failed");
return 0;
}
+ }
- if (!TIFFWriteDirectory(tif)) {
- i_push_error(0, "Cannot write TIFF directory");
- TIFFClose(tif);
+ _TIFFfree(out_row);
+
+ return 1;
+}
+
+static int
+write_one_paletted4(TIFF *tif, i_img *im) {
+ uint16 compress = get_compression(im, COMPRESSION_PACKBITS);
+ unsigned char *in_row;
+ unsigned char *out_row;
+ unsigned out_size;
+ int y;
+
+ mm_log((1, "tiff - write_one_paletted4(tif %p, im %p)\n", tif, im));
+
+ /* ignore a silly choice */
+ if (compress == COMPRESSION_JPEG ||
+ compress == COMPRESSION_CCITTRLE ||
+ compress == COMPRESSION_CCITTFAX3 ||
+ compress == COMPRESSION_CCITTFAX4)
+ compress = COMPRESSION_PACKBITS;
+
+ if (!set_base_tags(tif, im, compress, PHOTOMETRIC_PALETTE, 4, 1))
+ return 0;
+
+ if (!set_palette(tif, im, 16))
+ return 0;
+
+ if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) {
+ i_push_error(0, "write TIFF: setting rows per strip tag");
+ return 0;
+ }
+
+ in_row = mymalloc(im->xsize);
+ out_size = TIFFScanlineSize(tif);
+ out_row = (unsigned char *)_TIFFmalloc( out_size );
+
+ for (y = 0; y < im->ysize; ++y) {
+ i_gpal(im, 0, im->xsize, y, in_row);
+ memset(out_row, 0, out_size);
+ pack_4bit_to(out_row, in_row, im->xsize);
+ if (TIFFWriteScanline(tif, out_row, y, 0) < 0) {
+ _TIFFfree(out_row);
+ i_push_error(0, "write TIFF: write scan line failed");
+ return 0;
+ }
+ }
+
+ myfree(in_row);
+ _TIFFfree(out_row);
+
+ return 1;
+}
+
+static int
+set_direct_tags(TIFF *tif, i_img *im, uint16 compress,
+ uint16 bits_per_sample) {
+ uint16 extras = EXTRASAMPLE_ASSOCALPHA;
+ uint16 extra_count = im->channels == 2 || im->channels == 4;
+ uint16 photometric = im->channels >= 3 ?
+ PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK;
+
+ if (!set_base_tags(tif, im, compress, photometric, bits_per_sample,
+ im->channels)) {
+ return 0;
+ }
+
+ if (extra_count) {
+ if (!TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, extra_count, &extras)) {
+ i_push_error(0, "write TIFF: setting extra samples tag");
+ return 0;
+ }
+ }
+
+ if (compress == COMPRESSION_JPEG) {
+ int jpeg_quality;
+ if (i_tags_get_int(&im->tags, "tiff_jpegquality", 0, &jpeg_quality)
+ && jpeg_quality >= 0 && jpeg_quality <= 100) {
+ if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, jpeg_quality)) {
+ i_push_error(0, "write TIFF: setting jpeg quality pseudo-tag");
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int
+write_one_32(TIFF *tif, i_img *im) {
+ uint16 compress = get_compression(im, COMPRESSION_PACKBITS);
+ unsigned *in_row;
+ size_t out_size;
+ uint32 *out_row;
+ int y;
+ size_t sample_count = im->xsize * im->channels;
+ size_t sample_index;
+
+ mm_log((1, "tiff - write_one_32(tif %p, im %p)\n", tif, im));
+
+ /* only 8 and 12 bit samples are supported by jpeg compression */
+ if (compress == COMPRESSION_JPEG)
+ compress = COMPRESSION_PACKBITS;
+
+ if (!set_direct_tags(tif, im, compress, 32))
+ return 0;
+
+ in_row = mymalloc(sample_count * sizeof(unsigned));
+ out_size = TIFFScanlineSize(tif);
+ out_row = (uint32 *)_TIFFmalloc( out_size );
+
+ for (y = 0; y < im->ysize; ++y) {
+ if (i_gsamp_bits(im, 0, im->xsize, y, in_row, NULL, im->channels, 32) <= 0) {
+ i_push_error(0, "Cannot read 32-bit samples");
+ return 0;
+ }
+ for (sample_index = 0; sample_index < sample_count; ++sample_index)
+ out_row[sample_index] = in_row[sample_index];
+ if (TIFFWriteScanline(tif, out_row, y, 0) < 0) {
+ myfree(in_row);
+ _TIFFfree(out_row);
+ i_push_error(0, "write TIFF: write scan line failed");
+ return 0;
+ }
+ }
+
+ myfree(in_row);
+ _TIFFfree(out_row);
+
+ return 1;
+}
+
+static int
+write_one_16(TIFF *tif, i_img *im) {
+ uint16 compress = get_compression(im, COMPRESSION_PACKBITS);
+ unsigned *in_row;
+ size_t out_size;
+ uint16 *out_row;
+ int y;
+ size_t sample_count = im->xsize * im->channels;
+ size_t sample_index;
+
+ mm_log((1, "tiff - write_one_16(tif %p, im %p)\n", tif, im));
+
+ /* only 8 and 12 bit samples are supported by jpeg compression */
+ if (compress == COMPRESSION_JPEG)
+ compress = COMPRESSION_PACKBITS;
+
+ if (!set_direct_tags(tif, im, compress, 16))
+ return 0;
+
+ in_row = mymalloc(sample_count * sizeof(unsigned));
+ out_size = TIFFScanlineSize(tif);
+ out_row = (uint16 *)_TIFFmalloc( out_size );
+
+ for (y = 0; y < im->ysize; ++y) {
+ if (i_gsamp_bits(im, 0, im->xsize, y, in_row, NULL, im->channels, 16) <= 0) {
+ i_push_error(0, "Cannot read 16-bit samples");
+ return 0;
+ }
+ for (sample_index = 0; sample_index < sample_count; ++sample_index)
+ out_row[sample_index] = in_row[sample_index];
+ if (TIFFWriteScanline(tif, out_row, y, 0) < 0) {
+ myfree(in_row);
+ _TIFFfree(out_row);
+ i_push_error(0, "write TIFF: write scan line failed");
+ return 0;
+ }
+ }
+
+ myfree(in_row);
+ _TIFFfree(out_row);
+
+ return 1;
+}
+
+static int
+write_one_8(TIFF *tif, i_img *im) {
+ uint16 compress = get_compression(im, COMPRESSION_PACKBITS);
+ size_t out_size;
+ unsigned char *out_row;
+ int y;
+ size_t sample_count = im->xsize * im->channels;
+
+ mm_log((1, "tiff - write_one_8(tif %p, im %p)\n", tif, im));
+
+ if (!set_direct_tags(tif, im, compress, 8))
+ return 0;
+
+ out_size = TIFFScanlineSize(tif);
+ if (out_size < sample_count)
+ out_size = sample_count;
+ out_row = (unsigned char *)_TIFFmalloc( out_size );
+
+ for (y = 0; y < im->ysize; ++y) {
+ if (i_gsamp(im, 0, im->xsize, y, out_row, NULL, im->channels) <= 0) {
+ i_push_error(0, "Cannot read 8-bit samples");
+ return 0;
+ }
+ if (TIFFWriteScanline(tif, out_row, y, 0) < 0) {
+ _TIFFfree(out_row);
+ i_push_error(0, "write TIFF: write scan line failed");
+ return 0;
+ }
+ }
+ _TIFFfree(out_row);
+
+ return 1;
+}
+
+static int
+i_writetiff_low(TIFF *tif, i_img *im) {
+ uint32 width, height;
+ uint16 channels;
+ int zero_is_white;
+
+ width = im->xsize;
+ height = im->ysize;
+ channels = im->channels;
+
+ mm_log((1, "i_writetiff_low: width=%d, height=%d, channels=%d, bits=%d\n", width, height, channels, im->bits));
+ if (im->type == i_palette_type) {
+ mm_log((1, "i_writetiff_low: paletted, colors=%d\n", i_colorcount(im)));
+ }
+
+ if (i_img_is_monochrome(im, &zero_is_white)) {
+ if (!write_one_bilevel(tif, im, zero_is_white))
+ return 0;
+ }
+ else if (im->type == i_palette_type) {
+ if (i_colorcount(im) <= 16) {
+ if (!write_one_paletted4(tif, im))
+ return 0;
+ }
+ else {
+ if (!write_one_paletted8(tif, im))
+ return 0;
+ }
+ }
+ else if (im->bits > 16) {
+ if (!write_one_32(tif, im))
+ return 0;
+ }
+ else if (im->bits > 8) {
+ if (!write_one_16(tif, im))
+ return 0;
+ }
+ else {
+ if (!write_one_8(tif, im))
+ return 0;
+ }
+
+ if (!save_tiff_tags(tif, im))
+ return 0;
+
+ return 1;
+}
+
+/*
+=item i_writetiff_multi_wiol(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
+
+=cut
+*/
+
+undef_int
+i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
+ TIFF* tif;
+ TIFFErrorHandler old_handler;
+ int i;
+
+ old_handler = TIFFSetErrorHandler(error_handler);
+
+ 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));
+
+ /* 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,
+ ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+ (TIFFMapFileProc) comp_mmap,
+ (TIFFUnmapFileProc) comp_munmap);
+
+
+
+ if (!tif) {
+ mm_log((1, "i_writetiff_multi_wiol: Unable to open tif file for writing\n"));
+ i_push_error(0, "Could not create TIFF object");
+ TIFFSetErrorHandler(old_handler);
+ return 0;
+ }
+
+ for (i = 0; i < count; ++i) {
+ if (!i_writetiff_low(tif, imgs[i])) {
+ TIFFClose(tif);
+ TIFFSetErrorHandler(old_handler);
+ return 0;
+ }
+
+ if (!TIFFWriteDirectory(tif)) {
+ i_push_error(0, "Cannot write TIFF directory");
+ TIFFClose(tif);
TIFFSetErrorHandler(old_handler);
return 0;
}
}
-/*
-=item expand_4bit_hl(buf, count)
+static void
+unpack_4bit_to(unsigned char *dest, const unsigned char *src,
+ int src_byte_count) {
+ while (src_byte_count > 0) {
+ *dest++ = *src >> 4;
+ *dest++ = *src++ & 0xf;
+ --src_byte_count;
+ }
+}
-Expands 4-bit/entry packed data into 1 byte/entry.
+static void pack_4bit_to(unsigned char *dest, const unsigned char *src,
+ int pixel_count) {
+ int i = 0;
+ while (i < pixel_count) {
+ if ((i & 1) == 0) {
+ *dest = *src++ << 4;
+ }
+ else {
+ *dest++ |= *src++;
+ }
+ ++i;
+ }
+}
-buf must contain count bytes to be expanded and have 2*count bytes total
-space.
+static i_img *
+make_rgb(TIFF *tif, int width, int height, int *alpha_chan) {
+ uint16 photometric;
+ uint16 channels, in_channels;
+ uint16 extra_count;
+ uint16 *extras;
-The data is expanded in place.
+ TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &in_channels);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric);
-=cut
-*/
+ switch (photometric) {
+ case PHOTOMETRIC_SEPARATED:
+ channels = 3;
+ break;
+
+ case PHOTOMETRIC_MINISWHITE:
+ case PHOTOMETRIC_MINISBLACK:
+ /* the TIFF RGBA functions expand single channel grey into RGB,
+ so reduce it, we move the alpha channel into the right place
+ if needed */
+ channels = 1;
+ break;
-static void expand_4bit_hl(unsigned char *buf, int count) {
- while (--count >= 0) {
- buf[count*2+1] = buf[count] & 0xF;
- buf[count*2] = buf[count] >> 4;
+ default:
+ channels = 3;
+ break;
+ }
+ /* TIFF images can have more than one alpha channel, but Imager can't
+ this ignores the possibility of 2 channel images with 2 alpha,
+ but there's not much I can do about that */
+ *alpha_chan = 0;
+ if (TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra_count, &extras)
+ && extra_count) {
+ *alpha_chan = channels++;
}
+
+ return i_img_8_new(width, height, channels);
}
-static void pack_4bit_hl(unsigned char *buf, int count) {
- int i = 0;
- while (i < count) {
- buf[i/2] = (buf[i] << 4) + buf[i+1];
- i += 2;
+static i_img *
+read_one_rgb_lines(TIFF *tif, int width, int height, int allow_incomplete) {
+ i_img *im;
+ uint32* raster = NULL;
+ uint32 rowsperstrip, row;
+ i_color *line_buf;
+ int alpha_chan;
+
+ im = make_rgb(tif, width, height, &alpha_chan);
+ if (!im)
+ return NULL;
+
+ int rc = TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+ mm_log((1, "i_readtiff_wiol: rowsperstrip=%d rc = %d\n", rowsperstrip, rc));
+
+ if (rc != 1 || rowsperstrip==-1) {
+ rowsperstrip = height;
+ }
+
+ raster = (uint32*)_TIFFmalloc(width * rowsperstrip * sizeof (uint32));
+ if (!raster) {
+ i_img_destroy(im);
+ i_push_error(0, "No space for raster buffer");
+ return NULL;
+ }
+
+ line_buf = mymalloc(sizeof(i_color) * width);
+
+ for( row = 0; row < height; row += rowsperstrip ) {
+ uint32 newrows, i_row;
+
+ if (!TIFFReadRGBAStrip(tif, row, raster)) {
+ if (allow_incomplete) {
+ i_tags_setn(&im->tags, "i_lines_read", row);
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ break;
+ }
+ else {
+ i_push_error(0, "could not read TIFF image strip");
+ _TIFFfree(raster);
+ i_img_destroy(im);
+ return NULL;
+ }
+ }
+
+ newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip;
+ mm_log((1, "newrows=%d\n", newrows));
+
+ for( i_row = 0; i_row < newrows; i_row++ ) {
+ uint32 x;
+ i_color *outp = line_buf;
+
+ for(x = 0; x<width; x++) {
+ uint32 temp = raster[x+width*(newrows-i_row-1)];
+ outp->rgba.r = TIFFGetR(temp);
+ outp->rgba.g = TIFFGetG(temp);
+ outp->rgba.b = TIFFGetB(temp);
+
+ if (alpha_chan) {
+ /* the libtiff RGBA code expands greyscale into RGBA, so put the
+ alpha in the right place and scale it */
+ int ch;
+ outp->channel[alpha_chan] = TIFFGetA(temp);
+ if (outp->channel[alpha_chan]) {
+ for (ch = 0; ch < alpha_chan; ++ch) {
+ outp->channel[ch] = outp->channel[ch] * 255 / outp->channel[alpha_chan];
+ }
+ }
+ }
+
+ outp++;
+ }
+ i_plin(im, 0, width, i_row+row, line_buf);
+ }
}
+
+ myfree(line_buf);
+ _TIFFfree(raster);
+
+ return im;
+}
+
+/* adapted from libtiff
+
+ libtiff's TIFFReadRGBATile succeeds even when asked to read an
+ invalid tile, which means we have no way of knowing whether the data
+ we received from it is valid or not.
+
+ So the caller here has set stoponerror to 1 so that
+ TIFFRGBAImageGet() will fail.
+
+ read_one_rgb_tiled() then takes that into account for i_incomplete
+ or failure.
+ */
+static int
+myTIFFReadRGBATile(TIFFRGBAImage *img, uint32 col, uint32 row, uint32 * raster)
+
+{
+ int ok;
+ uint32 tile_xsize, tile_ysize;
+ uint32 read_xsize, read_ysize;
+ uint32 i_row;
+
+ /*
+ * Verify that our request is legal - on a tile file, and on a
+ * tile boundary.
+ */
+
+ TIFFGetFieldDefaulted(img->tif, TIFFTAG_TILEWIDTH, &tile_xsize);
+ TIFFGetFieldDefaulted(img->tif, TIFFTAG_TILELENGTH, &tile_ysize);
+ if( (col % tile_xsize) != 0 || (row % tile_ysize) != 0 )
+ {
+ i_push_errorf(0, "Row/col passed to myTIFFReadRGBATile() must be top"
+ "left corner of a tile.");
+ return 0;
+ }
+
+ /*
+ * The TIFFRGBAImageGet() function doesn't allow us to get off the
+ * edge of the image, even to fill an otherwise valid tile. So we
+ * figure out how much we can read, and fix up the tile buffer to
+ * a full tile configuration afterwards.
+ */
+
+ if( row + tile_ysize > img->height )
+ read_ysize = img->height - row;
+ else
+ read_ysize = tile_ysize;
+
+ if( col + tile_xsize > img->width )
+ read_xsize = img->width - col;
+ else
+ read_xsize = tile_xsize;
+
+ /*
+ * Read the chunk of imagery.
+ */
+
+ img->row_offset = row;
+ img->col_offset = col;
+
+ ok = TIFFRGBAImageGet(img, raster, read_xsize, read_ysize );
+
+ /*
+ * If our read was incomplete we will need to fix up the tile by
+ * shifting the data around as if a full tile of data is being returned.
+ *
+ * This is all the more complicated because the image is organized in
+ * bottom to top format.
+ */
+
+ if( read_xsize == tile_xsize && read_ysize == tile_ysize )
+ return( ok );
+
+ for( i_row = 0; i_row < read_ysize; i_row++ ) {
+ memmove( raster + (tile_ysize - i_row - 1) * tile_xsize,
+ raster + (read_ysize - i_row - 1) * read_xsize,
+ read_xsize * sizeof(uint32) );
+ _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize+read_xsize,
+ 0, sizeof(uint32) * (tile_xsize - read_xsize) );
+ }
+
+ for( i_row = read_ysize; i_row < tile_ysize; i_row++ ) {
+ _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize,
+ 0, sizeof(uint32) * tile_xsize );
+ }
+
+ return (ok);
}
+static i_img *
+read_one_rgb_tiled(TIFF *tif, int width, int height, int allow_incomplete) {
+ i_img *im;
+ uint32* raster = NULL;
+ int ok = 1;
+ uint32 row, col;
+ uint32 tile_width, tile_height;
+ unsigned long pixels = 0;
+ char emsg[1024] = "";
+ TIFFRGBAImage img;
+ i_color *line;
+ int alpha_chan;
+
+ im = make_rgb(tif, width, height, &alpha_chan);
+ if (!im)
+ return NULL;
+
+ if (!TIFFRGBAImageOK(tif, emsg)
+ || !TIFFRGBAImageBegin(&img, tif, 1, emsg)) {
+ i_push_error(0, emsg);
+ i_img_destroy(im);
+ return( 0 );
+ }
+
+ TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width);
+ TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
+ mm_log((1, "i_readtiff_wiol: tile_width=%d, tile_height=%d\n", tile_width, tile_height));
+
+ raster = (uint32*)_TIFFmalloc(tile_width * tile_height * sizeof (uint32));
+ if (!raster) {
+ i_img_destroy(im);
+ i_push_error(0, "No space for raster buffer");
+ TIFFRGBAImageEnd(&img);
+ return NULL;
+ }
+ line = mymalloc(tile_width * sizeof(i_color));
+
+ for( row = 0; row < height; row += tile_height ) {
+ for( col = 0; col < width; col += tile_width ) {
+
+ /* Read the tile into an RGBA array */
+ if (myTIFFReadRGBATile(&img, col, row, raster)) {
+ uint32 i_row, x;
+ uint32 newrows = (row+tile_height > height) ? height-row : tile_height;
+ uint32 newcols = (col+tile_width > width ) ? width-col : tile_width;
+
+ mm_log((1, "i_readtiff_wiol: tile(%d, %d) newcols=%d newrows=%d\n", col, row, newcols, newrows));
+ for( i_row = 0; i_row < newrows; i_row++ ) {
+ i_color *outp = line;
+ for(x = 0; x < newcols; x++) {
+ uint32 temp = raster[x+tile_width*(tile_height-i_row-1)];
+ outp->rgba.r = TIFFGetR(temp);
+ outp->rgba.g = TIFFGetG(temp);
+ outp->rgba.b = TIFFGetB(temp);
+ outp->rgba.a = TIFFGetA(temp);
+
+ if (alpha_chan) {
+ /* the libtiff RGBA code expands greyscale into RGBA, so put the
+ alpha in the right place and scale it */
+ int ch;
+ outp->channel[alpha_chan] = TIFFGetA(temp);
+
+ if (outp->channel[alpha_chan]) {
+ for (ch = 0; ch < alpha_chan; ++ch) {
+ outp->channel[ch] = outp->channel[ch] * 255 / outp->channel[alpha_chan];
+ }
+ }
+ }
+
+ ++outp;
+ }
+ i_plin(im, col, col+newcols, row+i_row, line);
+ }
+ pixels += newrows * newcols;
+ }
+ else {
+ if (allow_incomplete) {
+ ok = 0;
+ }
+ else {
+ goto error;
+ }
+ }
+ }
+ }
+
+ if (!ok) {
+ if (pixels == 0) {
+ i_push_error(0, "TIFF: No image data could be read from the image");
+ goto error;
+ }
+
+ /* incomplete image */
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", pixels / width);
+ }
+
+ myfree(line);
+ TIFFRGBAImageEnd(&img);
+ _TIFFfree(raster);
+
+ return im;
+
+ error:
+ myfree(line);
+ _TIFFfree(raster);
+ TIFFRGBAImageEnd(&img);
+ i_img_destroy(im);
+ return NULL;
+}
+
+char const *
+i_tiff_libversion(void) {
+ return TIFFGetVersion();
+}
+
+static int
+setup_paletted(read_state_t *state) {
+ uint16 *maps[3];
+ int i, ch;
+ int color_count = 1 << state->bits_per_sample;
+
+ state->img = i_img_pal_new(state->width, state->height, 3, 256);
+ if (!state->img)
+ return 0;
+
+ /* setup the color map */
+ if (!TIFFGetField(state->tif, TIFFTAG_COLORMAP, maps+0, maps+1, maps+2)) {
+ i_push_error(0, "Cannot get colormap for paletted image");
+ i_img_destroy(state->img);
+ return 0;
+ }
+ for (i = 0; i < color_count; ++i) {
+ i_color c;
+ for (ch = 0; ch < 3; ++ch) {
+ c.channel[ch] = Sample16To8(maps[ch][i]);
+ }
+ i_addcolors(state->img, &c, 1);
+ }
+
+ return 1;
+}
+
+static int
+tile_contig_getter(read_state_t *state, read_putter_t putter) {
+ uint32 tile_width, tile_height;
+ uint32 this_tile_height, this_tile_width;
+ uint32 rows_left, cols_left;
+ uint32 x, y;
+
+ state->raster = _TIFFmalloc(TIFFTileSize(state->tif));
+ if (!state->raster) {
+ i_push_error(0, "tiff: Out of memory allocating tile buffer");
+ return 0;
+ }
+
+ TIFFGetField(state->tif, TIFFTAG_TILEWIDTH, &tile_width);
+ TIFFGetField(state->tif, TIFFTAG_TILELENGTH, &tile_height);
+ rows_left = state->height;
+ for (y = 0; y < state->height; y += this_tile_height) {
+ this_tile_height = rows_left > tile_height ? tile_height : rows_left;
+
+ cols_left = state->width;
+ for (x = 0; x < state->width; x += this_tile_width) {
+ this_tile_width = cols_left > tile_width ? tile_width : cols_left;
+
+ if (TIFFReadTile(state->tif,
+ state->raster,
+ x, y, 0, 0) < 0) {
+ if (!state->allow_incomplete) {
+ return 0;
+ }
+ }
+ else {
+ putter(state, x, y, this_tile_width, this_tile_height, tile_width - this_tile_width);
+ }
+
+ cols_left -= this_tile_width;
+ }
+
+ rows_left -= this_tile_height;
+ }
+
+ return 1;
+}
+
+static int
+strip_contig_getter(read_state_t *state, read_putter_t putter) {
+ uint32 rows_per_strip;
+ tsize_t strip_size = TIFFStripSize(state->tif);
+ uint32 y, strip_rows, rows_left;
+
+ state->raster = _TIFFmalloc(strip_size);
+ if (!state->raster) {
+ i_push_error(0, "tiff: Out of memory allocating strip buffer");
+ return 0;
+ }
+
+ TIFFGetFieldDefaulted(state->tif, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
+ rows_left = state->height;
+ for (y = 0; y < state->height; y += strip_rows) {
+ strip_rows = rows_left > rows_per_strip ? rows_per_strip : rows_left;
+ if (TIFFReadEncodedStrip(state->tif,
+ TIFFComputeStrip(state->tif, y, 0),
+ state->raster,
+ strip_size) < 0) {
+ if (!state->allow_incomplete)
+ return 0;
+ }
+ else {
+ putter(state, 0, y, state->width, strip_rows, 0);
+ }
+ rows_left -= strip_rows;
+ }
+
+ return 1;
+}
+
+static int
+paletted_putter8(read_state_t *state, int x, int y, int width, int height, int extras) {
+ unsigned char *p = state->raster;
+
+ state->pixels_read += (unsigned long) width * height;
+ while (height > 0) {
+ i_ppal(state->img, x, x + width, y, p);
+ p += width + extras;
+ --height;
+ ++y;
+ }
+
+ return 1;
+}
+
+static int
+paletted_putter4(read_state_t *state, int x, int y, int width, int height, int extras) {
+ uint32 img_line_size = (width + 1) / 2;
+ uint32 skip_line_size = (width + extras + 1) / 2;
+ unsigned char *p = state->raster;
+
+ if (!state->line_buf)
+ state->line_buf = mymalloc(state->width);
+
+ state->pixels_read += (unsigned long) width * height;
+ while (height > 0) {
+ unpack_4bit_to(state->line_buf, p, img_line_size);
+ i_ppal(state->img, x, x + width, y, state->line_buf);
+ p += skip_line_size;
+ --height;
+ ++y;
+ }
+
+ return 1;
+}
+
+static void
+rgb_channels(read_state_t *state, int *out_channels) {
+ uint16 extra_count;
+ uint16 *extras;
+
+ /* safe defaults */
+ *out_channels = 3;
+ state->alpha_chan = 0;
+ state->scale_alpha = 0;
+
+ /* plain RGB */
+ if (state->samples_per_pixel == 3)
+ return;
+
+ if (!TIFFGetField(state->tif, TIFFTAG_EXTRASAMPLES, &extra_count, &extras)) {
+ mm_log((1, "tiff: samples != 3 but no extra samples tag\n"));
+ return;
+ }
+
+ if (!extra_count) {
+ mm_log((1, "tiff: samples != 3 but no extra samples listed"));
+ return;
+ }
+
+ ++*out_channels;
+ state->alpha_chan = 3;
+ switch (*extras) {
+ case EXTRASAMPLE_UNSPECIFIED:
+ case EXTRASAMPLE_ASSOCALPHA:
+ state->scale_alpha = 1;
+ break;
+
+ case EXTRASAMPLE_UNASSALPHA:
+ state->scale_alpha = 0;
+ break;
+
+ default:
+ mm_log((1, "tiff: unknown extra sample type %d, treating as assoc alpha\n",
+ *extras));
+ state->scale_alpha = 1;
+ break;
+ }
+ mm_log((1, "tiff alpha channel %d scale %d\n", state->alpha_chan, state->scale_alpha));
+}
+
+static void
+grey_channels(read_state_t *state, int *out_channels) {
+ uint16 extra_count;
+ uint16 *extras;
+
+ /* safe defaults */
+ *out_channels = 1;
+ state->alpha_chan = 0;
+ state->scale_alpha = 0;
+
+ /* plain grey */
+ if (state->samples_per_pixel == 1)
+ return;
+
+ if (!TIFFGetField(state->tif, TIFFTAG_EXTRASAMPLES, &extra_count, &extras)) {
+ mm_log((1, "tiff: samples != 1 but no extra samples tag\n"));
+ return;
+ }
+
+ if (!extra_count) {
+ mm_log((1, "tiff: samples != 1 but no extra samples listed"));
+ return;
+ }
+
+ ++*out_channels;
+ state->alpha_chan = 1;
+ switch (*extras) {
+ case EXTRASAMPLE_UNSPECIFIED:
+ case EXTRASAMPLE_ASSOCALPHA:
+ state->scale_alpha = 1;
+ break;
+
+ case EXTRASAMPLE_UNASSALPHA:
+ state->scale_alpha = 0;
+ break;
+
+ default:
+ mm_log((1, "tiff: unknown extra sample type %d, treating as assoc alpha\n",
+ *extras));
+ state->scale_alpha = 1;
+ break;
+ }
+}
+
+static int
+setup_16_rgb(read_state_t *state) {
+ int out_channels;
+
+ rgb_channels(state, &out_channels);
+
+ state->img = i_img_16_new(state->width, state->height, out_channels);
+ if (!state->img)
+ return 0;
+ state->line_buf = mymalloc(sizeof(unsigned) * state->width * out_channels);
+
+ return 1;
+}
+
+static int
+setup_16_grey(read_state_t *state) {
+ int out_channels;
+
+ grey_channels(state, &out_channels);
+
+ state->img = i_img_16_new(state->width, state->height, out_channels);
+ if (!state->img)
+ return 0;
+ state->line_buf = mymalloc(sizeof(unsigned) * state->width * out_channels);
+
+ return 1;
+}
+
+static int
+putter_16(read_state_t *state, int x, int y, int width, int height,
+ int row_extras) {
+ uint16 *p = state->raster;
+ int out_chan = state->img->channels;
+
+ state->pixels_read += (unsigned long) width * height;
+ while (height > 0) {
+ int i;
+ int ch;
+ unsigned *outp = state->line_buf;
+
+ for (i = 0; i < width; ++i) {
+ for (ch = 0; ch < out_chan; ++ch) {
+ outp[ch] = p[ch];
+ }
+ if (state->alpha_chan && state->scale_alpha && outp[state->alpha_chan]) {
+ for (ch = 0; ch < state->alpha_chan; ++ch) {
+ int result = 0.5 + (outp[ch] * 65535.0 / outp[state->alpha_chan]);
+ outp[ch] = CLAMP16(result);
+ }
+ }
+ p += state->samples_per_pixel;
+ outp += out_chan;
+ }
+
+ i_psamp_bits(state->img, x, x + width, y, state->line_buf, NULL, out_chan, 16);
+
+ p += row_extras * state->samples_per_pixel;
+ --height;
+ ++y;
+ }
+
+ return 1;
+}
+
+static int
+setup_8_rgb(read_state_t *state) {
+ int out_channels;
+
+ rgb_channels(state, &out_channels);
+
+ state->img = i_img_8_new(state->width, state->height, out_channels);
+ if (!state->img)
+ return 0;
+ state->line_buf = mymalloc(sizeof(unsigned) * state->width * out_channels);
+
+ return 1;
+}
+
+static int
+setup_8_grey(read_state_t *state) {
+ int out_channels;
+
+ grey_channels(state, &out_channels);
+
+ state->img = i_img_8_new(state->width, state->height, out_channels);
+ if (!state->img)
+ return 0;
+ state->line_buf = mymalloc(sizeof(i_color) * state->width * out_channels);
+
+ return 1;
+}
+
+static int
+putter_8(read_state_t *state, int x, int y, int width, int height,
+ int row_extras) {
+ unsigned char *p = state->raster;
+ int out_chan = state->img->channels;
+
+ state->pixels_read += (unsigned long) width * height;
+ while (height > 0) {
+ int i;
+ int ch;
+ i_color *outp = state->line_buf;
+
+ for (i = 0; i < width; ++i) {
+ for (ch = 0; ch < out_chan; ++ch) {
+ outp->channel[ch] = p[ch];
+ }
+ if (state->alpha_chan && state->scale_alpha
+ && outp->channel[state->alpha_chan]) {
+ for (ch = 0; ch < state->alpha_chan; ++ch) {
+ int result = (outp->channel[ch] * 255 + 127) / outp->channel[state->alpha_chan];
+
+ outp->channel[ch] = CLAMP8(result);
+ }
+ }
+ p += state->samples_per_pixel;
+ outp++;
+ }
+
+ i_plin(state->img, x, x + width, y, state->line_buf);
+
+ p += row_extras * state->samples_per_pixel;
+ --height;
+ ++y;
+ }
+
+ return 1;
+}
+
+static int
+setup_32_rgb(read_state_t *state) {
+ int out_channels;
+
+ rgb_channels(state, &out_channels);
+
+ state->img = i_img_double_new(state->width, state->height, out_channels);
+ if (!state->img)
+ return 0;
+ state->line_buf = mymalloc(sizeof(i_fcolor) * state->width);
+
+ return 1;
+}
+
+static int
+setup_32_grey(read_state_t *state) {
+ int out_channels;
+
+ grey_channels(state, &out_channels);
+
+ state->img = i_img_double_new(state->width, state->height, out_channels);
+ if (!state->img)
+ return 0;
+ state->line_buf = mymalloc(sizeof(i_fcolor) * state->width);
+
+ return 1;
+}
+
+static int
+putter_32(read_state_t *state, int x, int y, int width, int height,
+ int row_extras) {
+ uint32 *p = state->raster;
+ int out_chan = state->img->channels;
+
+ state->pixels_read += (unsigned long) width * height;
+ while (height > 0) {
+ int i;
+ int ch;
+ i_fcolor *outp = state->line_buf;
+
+ for (i = 0; i < width; ++i) {
+ for (ch = 0; ch < out_chan; ++ch) {
+ outp->channel[ch] = p[ch] / 4294967295.0;
+ }
+ if (state->alpha_chan && state->scale_alpha && outp->channel[state->alpha_chan]) {
+ for (ch = 0; ch < state->alpha_chan; ++ch)
+ outp->channel[ch] /= outp->channel[state->alpha_chan];
+ }
+ p += state->samples_per_pixel;
+ outp++;
+ }
+
+ i_plinf(state->img, x, x + width, y, state->line_buf);
+
+ p += row_extras * state->samples_per_pixel;
+ --height;
+ ++y;
+ }
+
+ return 1;
+}
+
+static int
+setup_bilevel(read_state_t *state) {
+ i_color black, white;
+ state->img = i_img_pal_new(state->width, state->height, 1, 256);
+ if (!state->img)
+ return 0;
+ black.channel[0] = black.channel[1] = black.channel[2] =
+ black.channel[3] = 0;
+ white.channel[0] = white.channel[1] = white.channel[2] =
+ white.channel[3] = 255;
+ if (state->photometric == PHOTOMETRIC_MINISBLACK) {
+ i_addcolors(state->img, &black, 1);
+ i_addcolors(state->img, &white, 1);
+ }
+ else {
+ i_addcolors(state->img, &white, 1);
+ i_addcolors(state->img, &black, 1);
+ }
+ state->line_buf = mymalloc(state->width);
+
+ return 1;
+}
+
+static int
+putter_bilevel(read_state_t *state, int x, int y, int width, int height,
+ int row_extras) {
+ unsigned char *line_in = state->raster;
+ size_t line_size = (width + row_extras + 7) / 8;
+
+ /* tifflib returns the bits in MSB2LSB order even when the file is
+ in LSB2MSB, so we only need to handle MSB2LSB */
+ state->pixels_read += (unsigned long) width * height;
+ while (height > 0) {
+ int i;
+ unsigned char *outp = state->line_buf;
+ unsigned char *inp = line_in;
+ unsigned mask = 0x80;
+
+ for (i = 0; i < width; ++i) {
+ *outp++ = *inp & mask ? 1 : 0;
+ mask >>= 1;
+ if (!mask) {
+ ++inp;
+ mask = 0x80;
+ }
+ }
+
+ i_ppal(state->img, x, x + width, y, state->line_buf);
+
+ line_in += line_size;
+ --height;
+ ++y;
+ }
+
+ return 1;
+}
+
+static void
+cmyk_channels(read_state_t *state, int *out_channels) {
+ uint16 extra_count;
+ uint16 *extras;
+
+ /* safe defaults */
+ *out_channels = 3;
+ state->alpha_chan = 0;
+ state->scale_alpha = 0;
+
+ /* plain CMYK */
+ if (state->samples_per_pixel == 4)
+ return;
+
+ if (!TIFFGetField(state->tif, TIFFTAG_EXTRASAMPLES, &extra_count, &extras)) {
+ mm_log((1, "tiff: CMYK samples != 4 but no extra samples tag\n"));
+ return;
+ }
+
+ if (!extra_count) {
+ mm_log((1, "tiff: CMYK samples != 4 but no extra samples listed"));
+ return;
+ }
+
+ ++*out_channels;
+ state->alpha_chan = 4;
+ switch (*extras) {
+ case EXTRASAMPLE_UNSPECIFIED:
+ case EXTRASAMPLE_ASSOCALPHA:
+ state->scale_alpha = 1;
+ break;
+
+ case EXTRASAMPLE_UNASSALPHA:
+ state->scale_alpha = 0;
+ break;
+
+ default:
+ mm_log((1, "tiff: unknown extra sample type %d, treating as assoc alpha\n",
+ *extras));
+ state->scale_alpha = 1;
+ break;
+ }
+}
+
+static int
+setup_cmyk8(read_state_t *state) {
+ int channels;
+
+ cmyk_channels(state, &channels);
+ state->img = i_img_8_new(state->width, state->height, channels);
+
+ state->line_buf = mymalloc(sizeof(i_color) * state->width);
+
+ return 1;
+}
+
+static int
+putter_cmyk8(read_state_t *state, int x, int y, int width, int height,
+ int row_extras) {
+ unsigned char *p = state->raster;
+
+ state->pixels_read += (unsigned long) width * height;
+ while (height > 0) {
+ int i;
+ int ch;
+ i_color *outp = state->line_buf;
+
+ for (i = 0; i < width; ++i) {
+ unsigned char c, m, y, k;
+ c = p[0];
+ m = p[1];
+ y = p[2];
+ k = 255 - p[3];
+ outp->rgba.r = (k * (255 - c)) / 255;
+ outp->rgba.g = (k * (255 - m)) / 255;
+ outp->rgba.b = (k * (255 - y)) / 255;
+ if (state->alpha_chan) {
+ outp->rgba.a = p[state->alpha_chan];
+ if (state->scale_alpha
+ && outp->rgba.a) {
+ for (ch = 0; ch < 3; ++ch) {
+ int result = (outp->channel[ch] * 255 + 127) / outp->rgba.a;
+ outp->channel[ch] = CLAMP8(result);
+ }
+ }
+ }
+ p += state->samples_per_pixel;
+ outp++;
+ }
+
+ i_plin(state->img, x, x + width, y, state->line_buf);
+
+ p += row_extras * state->samples_per_pixel;
+ --height;
+ ++y;
+ }
+
+ return 1;
+}
+
+static int
+setup_cmyk16(read_state_t *state) {
+ int channels;
+
+ cmyk_channels(state, &channels);
+ state->img = i_img_16_new(state->width, state->height, channels);
+
+ state->line_buf = mymalloc(sizeof(unsigned) * state->width * channels);
+
+ return 1;
+}
+
+static int
+putter_cmyk16(read_state_t *state, int x, int y, int width, int height,
+ int row_extras) {
+ uint16 *p = state->raster;
+ int out_chan = state->img->channels;
+
+ mm_log((4, "putter_cmyk16(%p, %d, %d, %d, %d, %d)\n", x, y, width, height, row_extras));
+
+ state->pixels_read += (unsigned long) width * height;
+ while (height > 0) {
+ int i;
+ int ch;
+ unsigned *outp = state->line_buf;
+
+ for (i = 0; i < width; ++i) {
+ unsigned c, m, y, k;
+ c = p[0];
+ m = p[1];
+ y = p[2];
+ k = 65535 - p[3];
+ outp[0] = (k * (65535U - c)) / 65535U;
+ outp[1] = (k * (65535U - m)) / 65535U;
+ outp[2] = (k * (65535U - y)) / 65535U;
+ if (state->alpha_chan) {
+ outp[3] = p[state->alpha_chan];
+ if (state->scale_alpha
+ && outp[3]) {
+ for (ch = 0; ch < 3; ++ch) {
+ int result = (outp[ch] * 65535 + 32767) / outp[3];
+ outp[3] = CLAMP16(result);
+ }
+ }
+ }
+ p += state->samples_per_pixel;
+ outp += out_chan;
+ }
+
+ i_psamp_bits(state->img, x, x + width, y, state->line_buf, NULL, out_chan, 16);
+
+ p += row_extras * state->samples_per_pixel;
+ --height;
+ ++y;
+ }
+
+ return 1;
+}
+
+/*
+
+ Older versions of tifflib we support don't define this, so define it
+ ourselves.
+
+ */
+#if TIFFLIB_VERSION < 20031121
+
+int TIFFIsCODECConfigured(uint16 scheme) {
+ switch (scheme) {
+ case COMPRESSION_NONE:
+#if PACKBITS_SUPPORT
+ case COMPRESSION_PACKBITS:
+#endif
+
+#if CCITT_SUPPORT
+ case COMPRESSION_CCITTRLE:
+ case COMPRESSION_CCITTRLEW:
+ case COMPRESSION_CCITTFAX3:
+ case COMPRESSION_CCITTFAX4:
+#endif
+
+#if JPEG_SUPPORT
+ case COMPRESSION_JPEG:
+#endif
+
+#if LZW_SUPPORT
+ case COMPRESSION_LZW:
+#endif
+
+#if ZIP_SUPPORT
+ case COMPRESSION_DEFLATE:
+ case COMPRESSION_ADOBE_DEFLATE:
+#endif
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+#endif
+
/*
=back
=head1 AUTHOR
-Arnar M. Hrafnkelsson <addi@umich.edu>
+Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook <tony@imager.perl.org>
=head1 SEE ALSO