2 # Make a signed or floating point version of an uncompressed TIFF
7 GetOptions("m|mode=s" => \$mode);
9 use constant TIFFTAG_BITSPERSAMPLE => 258;
10 use constant TIFFTAG_SAMPLEFORMAT => 339;
11 use constant SAMPLEFORMAT_UINT => 1;
12 use constant SAMPLEFORMAT_INT => 2;
13 use constant SAMPLEFORMAT_IEEEFP => 3;
14 use constant SAMPLEFORMAT_VOID => 4;
15 use constant TIFFTAG_COMPRESSION => 259;
16 use constant COMPRESSION_NONE => 1;
17 use constant TIFFTAG_SAMPLESPERPIXEL => 277;
22 Usage: $0 [-m mode] input output
29 open my $fh, "<", $inname
30 or die "Cannot open $inname: $!\n";
33 my $data = do { local $/; <$fh> };
37 my $tiff = TIFFPP->new($data);
39 $tiff->compression == COMPRESSION_NONE
40 or die "TIFF must be uncompressed\n";
42 my $sample_count = $tiff->samples_per_pixel;
48 my ($data, $tiff) = @_;
49 my ($values, $bits, $format) = $tiff->unpack_samples($data);
51 for my $value (@$values) {
52 my $limit= 1 << ($bits->[$i++] - 1);
54 $i == @$bits and $i = 0;
56 return $tiff->pack_samples($values, undef, [ (SAMPLEFORMAT_INT) x $sample_count ]);
61 tag => TIFFTAG_SAMPLEFORMAT,
63 value => [ (SAMPLEFORMAT_INT) x $sample_count ],
66 elsif ($mode eq "float") {
70 my ($data, $tiff) = @_;
71 my ($values, $bits, $format) = $tiff->unpack_samples($data);
73 for my $value (@$values) {
74 my $limit = 2 ** ($bits->[$i++]) - 1;
76 $i == @$bits and $i = 0;
78 return $tiff->pack_samples($values, [ (32) x $sample_count ], [ (SAMPLEFORMAT_IEEEFP) x $sample_count ]);
83 tag => TIFFTAG_SAMPLEFORMAT,
85 value => [ (SAMPLEFORMAT_IEEEFP) x $sample_count ],
89 tag => TIFFTAG_BITSPERSAMPLE,
91 value => [ ( 32 ) x $sample_count ]
94 elsif ($mode eq "double") {
98 my ($data, $tiff) = @_;
99 my ($values, $bits, $format) = $tiff->unpack_samples($data);
101 for my $value (@$values) {
102 my $limit= 2 ** ($bits->[$i++] - 1) - 1;
104 $i == @$bits and $i = 0;
106 return $tiff->pack_samples($values, [ (64) x $sample_count ], [ (SAMPLEFORMAT_IEEEFP) x $sample_count ]);
111 tag => TIFFTAG_SAMPLEFORMAT,
113 value => [ (SAMPLEFORMAT_IEEEFP) x $sample_count ],
117 tag => TIFFTAG_BITSPERSAMPLE,
119 value => [ ( 64 ) x $sample_count ]
125 open my $ofh, ">", $outname;
128 print $ofh $tiff->data;
133 use constant TIFFTAG_STRIPOFFSETS => 273;
134 use constant TIFFTAG_STRIPBYTECOUNTS => 279;
135 use constant TIFFTAG_SAMPLESPERPIXEL => 277;
136 use constant TIFFTAG_BITSPERSAMPLE => 258;
137 use constant TIFFTAG_SAMPLEFORMAT => 339;
138 use constant TIFFTAG_COMPRESSION => 259;
139 use constant COMPRESSION_NONE => 1;
141 use constant TYPE_SHORT => 3;
142 use constant TYPE_LONG => 4;
144 use constant SAMPLEFORMAT_UINT => 1;
145 use constant SAMPLEFORMAT_INT => 2;
146 use constant SAMPLEFORMAT_IEEEFP => 3;
147 use constant SAMPLEFORMAT_VOID => 4;
161 pack => sub { pack("C*", @{$_[0]}), scalar @{$_[0]} },
162 unpack => sub { [ unpack "C*", $_[0] ] },
173 pack => sub { pack("$_[1]{SHORT}*", @{$_[0]}), scalar @{$_[0]} },
174 unpack => sub { [ unpack "$_[1]{SHORT}*", $_[0] ] },
180 pack => sub { pack("$_[1]{LONG}*", @{$_[0]}), scalar @{$_[0]} },
181 unpack => sub { [ unpack "$_[1]{LONG}*", $_[0] ] },
187 pack => sub { pack("$_[1]{LONG}*", map @$_, @{$_[0]}), scalar @{$_[0]} },
189 my @raw = unpack("$_[1]{LONG}*", $_[0]);
190 return [ map [ @raw[$_*2, $_*2+1] ], 0 .. $#raw/2 ];
197 pack => sub { pack("c*", @{$_[0]}), scalar @{$_[0]} },
198 unpack => sub { [ unpack "c*", $_[0] ] },
204 pack => sub { $_[0], length $_[0] },
205 unpack => sub { $_[0] },
211 pack => sub { pack("$_[1]{SSHORT}*", @{$_[0]}), scalar @{$_[0]} },
212 unpack => sub { [ unpack "$_[1]{SSHORT}*", $_[0] ] },
218 pack => sub { pack("$_[1]{SLONG}*", @{$_[0]}), scalar @{$_[0]} },
219 unpack => sub { [ unpack "$_[1]{SLONG}*", $_[0] ] },
225 pack => sub { pack("($_[1]{SLONG}$_[1]{LONG})*", map @$_, @{$_[0]}), scalar @{$_[0]} },
227 my @raw = unpack("($_[1]{SLONG}$_[1]{LONG})*", $_[0]);
228 return [ map [ @raw[$_*2, $_*2+1] ], 0 .. $#raw/2 ];
235 pack => sub { pack("$_[1]{FLOAT}*", @{$_[0]}), scalar @{$_[0]} },
236 unpack => sub { [ unpack "$_[1]{FLOAT}*", $_[0] ] },
242 pack => sub { pack("$_[1]{DOUBLE}*", @{$_[0]}), scalar @{$_[0]} },
243 unpack => sub { [ unpack "$_[1]{DOUBLE}*", $_[0] ] },
247 %type_names = map { $types{$_}->{name} => $_ } keys %types;
258 my ($class, $data) = @_;
265 if (substr($data, 0, 2) eq "II") {
269 $opts{SSHORT} = "s<";
271 $opts{DOUBLE} = "d<";
273 elsif (substr($data, 0, 2) eq "MM") {
277 $opts{SSHORT} = "s>";
279 $opts{DOUBLE} = "d>";
282 die "Not a TIFF file (bad byte-order)\n";
284 substr($data, 2, 2) eq "\x2A\0"
285 or die "Not a TIFF file (bad TIFF marker)\n";
286 my $ifd_off = unpack($opts{LONG}, substr($data, 4, 4));
287 $ifd_off < length $data
288 or die "Invalid TIFF - IFD offset too long\n";
290 my $self = bless \%opts, $class;
291 $self->_load_ifd(4, $ifd_off);
301 my ($self, $off_ptr, $off) = @_;
303 $self->{off_ptr} = $off_ptr;
304 $self->{ifd_off} = $off;
305 my $count = unpack($self->{SHORT}, substr($self->{data}, $off, 2));
306 $self->{ifd_size} = $count;
309 my ($short, $long) = ($self->{SHORT}, $self->{LONG});
310 for my $index (1 .. $count) {
311 my ($tag, $type, $count, $value) =
312 unpack("$short$short${long}a4", substr($self->{data}, $off, 12));
314 or die "Unknown type $type in IFD\n";
315 my $size = $types{$type}{size} * $count;
317 my $item_off = $size > 4 ? unpack($long, $value) : $off + 8;
318 my $data = substr($self->{data}, $item_off, $size);
330 my %ifd = map { $_->{tag} => $_ } @ifds;
331 $self->{ifd} = \@ifds;
332 $self->{ifdh} = \%ifd;
334 $self->{next_ifd} = unpack($long, substr($self->{data}, $off, 4));
340 my @ifd = sort { $a->{tag} <=> $b->{tag} } @{$self->{ifd}};
341 my $ifd = pack($self->{SHORT}, scalar(@ifd));
342 my ($short, $long) = ($self->{SHORT}, $self->{LONG});
343 for my $entry (@ifd) {
345 if (!$entry{original} && length $entry{data} > 4) {
346 $entry{offset} = length $self->{data};
347 $self->{data} .= $entry{data};
349 if (length $entry{data} > 4) {
350 $ifd .= pack("$short$short$long$long", @entry{qw(tag type count offset)});
353 $ifd .= pack("$short$short${long}a4", @entry{qw(tag type count data)});
356 $ifd .= pack($long, $self->{next_ifd});
357 if (scalar(@ifd) <= $self->{ifd_size}) {
358 substr($self->{data}, $self->{ifd_off}, length $ifd, $ifd);
361 $self->{ifd_off} = length $self->{data};
362 $self->{data} .= $ifd;
363 substr($self->{data}, $self->{off_ptr}, 4, pack($long, $self->{ifd_off}));
368 my ($self, $tag) = @_;
370 if (delete $self->{ifdh}{$tag}) {
371 $self->{ifd} = [ grep $_->{tag} != $tag, @{$self->{ifd}} ];
376 my ($self, %opts) = @_;
378 unless ($opts{type} =~ /[0-9]/) {
379 $opts{type} = $type_names{$opts{type}}
380 or die "add_tag: Invalid type\n";
383 if ($opts{value} && !exists $opts{data}) {
384 @opts{qw(data count)} = $types{$opts{type}}{pack}->($opts{value}, $self);
387 if ($self->{ifdh}{$opts{tag}}) {
388 $self->remove_tag($opts{tag});
390 push @{$self->{ifd}}, \%opts;
391 $self->{ifdh}{$opts{tag}} = \%opts;
395 my ($self, $tag) = @_;
397 my $val = $self->{ifdh}{$tag}
400 return $types{$val->{type}}{unpack}->($val->{data}, $self);
404 my ($self, $cb) = @_;
406 my $offsets = $self->tag_value(TIFFTAG_STRIPOFFSETS);
407 my $sizes = $self->tag_value(TIFFTAG_STRIPBYTECOUNTS);
409 or die "Strip offset and byte counts do not match\n";
410 for my $i (0 .. $#$offsets) {
411 my $bytes = substr($self->{data}, $offsets->[$i], $sizes->[$i]);
412 $bytes = $cb->($bytes, $self);
413 if (length $bytes > $sizes->[$i]) {
414 $offsets->[$i] = length $self->{data};
415 $self->{data} .= $bytes;
418 substr($self->{data}, $offsets->[$i], length $bytes, $bytes);
420 $sizes->[$i] = length $bytes;
422 my $off_type = TYPE_SHORT;
423 my $count_type = TYPE_SHORT;
424 $_ > 0xFFFF and $off_type = TYPE_LONG for @$offsets;
425 $_ > 0xFFFF and $count_type = TYPE_LONG for @$sizes;
429 tag => TIFFTAG_STRIPOFFSETS,
435 tag => TIFFTAG_STRIPBYTECOUNTS,
442 my ($self, $bits, $formats) = @_;
444 $bits ||= $self->_bitspersample;
445 $formats ||= $self->_sampleformat;
447 or die "Mismatch between bitsperlsample and sampleformat counts\n";
449 for my $i (0 .. $#$bits) {
451 if ($formats->[$i] == SAMPLEFORMAT_IEEEFP) {
452 if ($bits->[$i] == 32) {
455 elsif ($bits->[$i] == 64) {
459 die "No IEEEFP format for bits $bits->[$i]\n";
463 $type = $bit_types{$bits->[$i]}
464 or die "Can't pack $bits->[$i] bits\n";
465 $type = "S$type" if $formats->[$i] == SAMPLEFORMAT_INT;
467 $pack .= $self->{$type};
470 wantarray ? ($pack, $bits, $formats) : $pack;
473 sub samples_per_pixel {
476 my $spp = $self->tag_value(TIFFTAG_SAMPLESPERPIXEL);
485 my $comp = $self->tag_value(TIFFTAG_COMPRESSION);
486 $comp or return COMPRESSION_NONE;
494 my $bps = $self->tag_value(TIFFTAG_BITSPERSAMPLE);
495 $bps or $bps = [ ( 1 ) x $self->samples_per_pixel ];
503 my $formats = $self->tag_value(TIFFTAG_SAMPLEFORMAT);
505 $formats = [ ( SAMPLEFORMAT_UINT ) x $self->samples_per_pixel ];
512 my ($self, $data, $bits, $formats) = @_;
514 my ($pack, $rbits, $rformats) = $self->_pack_format($bits, $formats);
516 my $values = [ unpack "($pack)*", $data ];
518 wantarray ? ( $values, $rbits, $rformats) : $values;
522 my ($self, $data, $bits, $formats) = @_;
524 my $pack = $self->_pack_format($bits, $formats);
526 pack "($pack)*", @$data;