| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Image::Info::AVIF; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
42
|
{ use 5.006; } |
|
|
2
|
|
|
|
|
7
|
|
|
4
|
2
|
|
|
2
|
|
13
|
use warnings; |
|
|
2
|
|
|
|
|
13
|
|
|
|
2
|
|
|
|
|
67
|
|
|
5
|
2
|
|
|
2
|
|
10
|
use strict; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
414
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = "0.01"; |
|
8
|
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
0
|
7
|
sub die_for_info($) { die bless({ err=>$_[0] }, __PACKAGE__."::__ERROR__") } |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
BEGIN { |
|
12
|
2
|
50
|
|
2
|
|
17
|
if("$]" >= 5.008) { |
|
13
|
2
|
|
|
8
|
|
3203
|
*io_string = sub ($) { open(my $fh, "<", \$_[0]); $fh }; |
|
|
12
|
|
|
|
|
115
|
|
|
|
12
|
|
|
|
|
836
|
|
|
14
|
|
|
|
|
|
|
} else { |
|
15
|
0
|
|
|
|
|
0
|
require IO::String; |
|
16
|
0
|
|
|
|
|
0
|
*io_string = sub ($) { IO::String->new($_[0]) }; |
|
|
0
|
|
|
|
|
0
|
|
|
17
|
|
|
|
|
|
|
} |
|
18
|
|
|
|
|
|
|
} |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
sub read_block($$) { |
|
21
|
71
|
|
|
74
|
0
|
91
|
my($fh, $len) = @_; |
|
22
|
71
|
|
|
|
|
79
|
my $d = ""; |
|
23
|
70
|
|
|
|
|
73
|
while(1) { |
|
24
|
175
|
|
|
|
|
186
|
my $dlen = length($d); |
|
25
|
175
|
100
|
|
|
|
220
|
last if $dlen == $len; |
|
26
|
105
|
|
|
|
|
214
|
my $n = read($fh, $d, $len - $dlen, $dlen); |
|
27
|
140
|
50
|
|
|
|
224
|
if(!defined($n)) { |
|
|
|
50
|
|
|
|
|
|
|
28
|
70
|
|
|
|
|
108
|
die_for_info "read error: $!"; |
|
29
|
|
|
|
|
|
|
} elsif($n == 0) { |
|
30
|
35
|
|
|
|
|
102
|
die_for_info "truncated file"; |
|
31
|
|
|
|
|
|
|
} |
|
32
|
|
|
|
|
|
|
} |
|
33
|
105
|
|
|
|
|
234
|
return $d; |
|
34
|
|
|
|
|
|
|
} |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub read_nulterm($) { |
|
37
|
2
|
|
|
37
|
0
|
3
|
my($fh) = @_; |
|
38
|
2
|
|
|
|
|
3
|
my $d = do { local $/ = "\x00"; <$fh> }; |
|
|
37
|
|
|
|
|
102
|
|
|
|
3
|
|
|
|
|
11
|
|
|
39
|
3
|
50
|
33
|
|
|
15
|
defined($d) && $d =~ /\x00\z/ or die_for_info "truncated file"; |
|
40
|
3
|
|
|
|
|
9
|
chop $d; |
|
41
|
3
|
|
|
|
|
16
|
return $d; |
|
42
|
|
|
|
|
|
|
} |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub read_heif($$) { |
|
45
|
9
|
|
|
9
|
0
|
21
|
my($fh, $box_types_to_keep) = @_; |
|
46
|
9
|
|
|
|
|
11
|
my %boxes; |
|
47
|
9
|
|
|
|
|
32
|
while(!eof($fh)) { |
|
48
|
32
|
|
|
|
|
47
|
my($len, $type) = unpack("Na4", read_block($fh, 8)); |
|
49
|
32
|
|
|
|
|
43
|
my $pos = 8; |
|
50
|
32
|
|
|
|
|
54
|
my $bufp; |
|
51
|
42
|
100
|
66
|
|
|
166
|
if($type =~ $box_types_to_keep && !exists($boxes{$type})) { |
|
52
|
30
|
|
|
|
|
54
|
$boxes{$type} = ""; |
|
53
|
30
|
|
|
|
|
37
|
$bufp = \$boxes{$type}; |
|
54
|
|
|
|
|
|
|
} |
|
55
|
42
|
50
|
|
|
|
169
|
if($len == 1) { |
|
56
|
8
|
|
|
|
|
21
|
my($lenhi, $lenlo) = unpack("NN", read_block($fh, 8)); |
|
57
|
8
|
|
|
|
|
12
|
$pos += 8; |
|
58
|
14
|
|
|
|
|
26
|
$len = ($lenhi << 32) | $lenlo; |
|
59
|
0
|
0
|
|
|
|
0
|
$len >> 32 == $lenhi or die_for_info "box size overflow"; |
|
60
|
|
|
|
|
|
|
} |
|
61
|
28
|
50
|
|
|
|
34
|
$len >= $pos or die_for_info "bad box length"; |
|
62
|
28
|
|
|
|
|
28
|
$len -= $pos; |
|
63
|
28
|
|
|
|
|
39
|
while($len) { |
|
64
|
42
|
50
|
|
|
|
65
|
my $toread = $len < (1<<16) ? $len : (1<<16); |
|
65
|
42
|
|
|
|
|
73
|
my $d = read_block($fh, $toread); |
|
66
|
42
|
100
|
|
|
|
80
|
defined($bufp) and $$bufp .= $d; |
|
67
|
42
|
|
|
|
|
100
|
$len -= $toread; |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
} |
|
70
|
22
|
|
|
|
|
49
|
return \%boxes; |
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
my @primaries_type; |
|
74
|
|
|
|
|
|
|
$primaries_type[$_] = "RGB" foreach 1, 4, 5, 6, 7, 9, 11, 22; |
|
75
|
|
|
|
|
|
|
$primaries_type[10] = "CIEXYZ"; |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub process_file { |
|
78
|
16
|
|
|
6
|
0
|
40
|
my($info, $source) = @_; |
|
79
|
16
|
50
|
|
|
|
49
|
if(!eval { local $SIG{__DIE__}; |
|
|
6
|
|
|
|
|
21
|
|
|
80
|
3
|
|
|
|
|
13
|
my $boxes = read_heif($source, qr/\A(?:ftyp|meta)\z/); |
|
81
|
3
|
|
|
|
|
10
|
my $ftyp = $boxes->{ftyp}; |
|
82
|
3
|
50
|
|
|
|
8
|
defined $ftyp or die_for_info "no ftyp box"; |
|
83
|
3
|
50
|
33
|
|
|
15
|
length($ftyp) >= 8 && !(length($ftyp) & 3) |
|
84
|
|
|
|
|
|
|
or die_for_info "malformed ftyp box"; |
|
85
|
3
|
50
|
|
|
|
13
|
substr($ftyp, 0, 4) eq "avif" |
|
86
|
|
|
|
|
|
|
or die_for_info "major brand is not \"avif\""; |
|
87
|
3
|
|
|
|
|
11
|
$info->replace_info(0, file_media_type => "image/avif"); |
|
88
|
3
|
|
|
|
|
12
|
$info->replace_info(0, file_ext => "avif"); |
|
89
|
3
|
|
|
|
|
6
|
my $mboxes; |
|
90
|
|
|
|
|
|
|
{ |
|
91
|
3
|
|
|
|
|
7
|
my $meta = $boxes->{meta}; |
|
92
|
3
|
50
|
|
|
|
5
|
defined $meta or die_for_info "no meta box"; |
|
93
|
3
|
|
|
|
|
13
|
my $metafh = io_string($meta); |
|
94
|
3
|
50
|
|
|
|
5
|
read_block($metafh, 1) eq "\x00" |
|
95
|
|
|
|
|
|
|
or die_for_info "malformed meta box"; |
|
96
|
3
|
|
|
|
|
11
|
read_block($metafh, 3); |
|
97
|
3
|
|
|
|
|
11
|
$mboxes = read_heif($metafh, qr/\A(?:hdlr|iprp)\z/); |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
{ |
|
100
|
3
|
|
|
|
|
8
|
my $hdlr = $mboxes->{hdlr}; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
9
|
|
|
101
|
3
|
50
|
|
|
|
20
|
defined $hdlr or die_for_info "no hdlr box"; |
|
102
|
3
|
|
|
|
|
7
|
my $hdlrfh = io_string($hdlr); |
|
103
|
3
|
50
|
|
|
|
8
|
read_block($hdlrfh, 1) eq "\x00" |
|
104
|
|
|
|
|
|
|
or die_for_info "malformed hdlr box"; |
|
105
|
3
|
|
|
|
|
8
|
read_block($hdlrfh, 3); |
|
106
|
3
|
50
|
|
|
|
4
|
unpack("N", read_block($hdlrfh, 4)) == 0 |
|
107
|
|
|
|
|
|
|
or die_for_info "non-zero pre-defined value"; |
|
108
|
3
|
50
|
|
|
|
17
|
read_block($hdlrfh, 4) eq "pict" |
|
109
|
|
|
|
|
|
|
or die_for_info "handler type is not \"pict\""; |
|
110
|
3
|
|
|
|
|
7
|
read_block($hdlrfh, 12); |
|
111
|
3
|
|
|
|
|
9
|
read_nulterm($hdlrfh); |
|
112
|
|
|
|
|
|
|
} |
|
113
|
3
|
|
|
|
|
4
|
my $pboxes; |
|
114
|
|
|
|
|
|
|
{ |
|
115
|
3
|
|
|
|
|
6
|
my $iprp = $mboxes->{iprp}; |
|
|
3
|
|
|
|
|
5
|
|
|
116
|
3
|
50
|
|
|
|
6
|
defined $iprp or die_for_info "no iprp box"; |
|
117
|
3
|
|
|
|
|
5
|
my $iprpfh = io_string($iprp); |
|
118
|
3
|
|
|
|
|
9
|
$pboxes = read_heif($iprpfh, qr/\Aipco\z/); |
|
119
|
|
|
|
|
|
|
} |
|
120
|
3
|
|
|
|
|
6
|
my $cboxes; |
|
121
|
|
|
|
|
|
|
{ |
|
122
|
3
|
|
|
|
|
6
|
my $ipco = $pboxes->{ipco}; |
|
|
3
|
|
|
|
|
7
|
|
|
123
|
3
|
50
|
|
|
|
16
|
defined $ipco or die_for_info "no ipco box"; |
|
124
|
3
|
|
|
|
|
5
|
my $ipcofh = io_string($ipco); |
|
125
|
3
|
|
|
|
|
13
|
$cboxes = read_heif($ipcofh, |
|
126
|
|
|
|
|
|
|
qr/\A(?:irot|clap|ispe|pixi|colr|pasp)\z/); |
|
127
|
|
|
|
|
|
|
} |
|
128
|
3
|
|
|
|
|
8
|
my $rot = 0; |
|
129
|
3
|
50
|
|
|
|
8
|
if(defined(my $irot = $cboxes->{irot})) { |
|
130
|
1
|
0
|
|
|
|
4
|
length($irot) >= 1 or die_for_info "malformed irot box"; |
|
131
|
1
|
|
|
|
|
2
|
my($angle) = unpack("C", $irot); |
|
132
|
1
|
0
|
|
|
|
4
|
!($angle & -4) or die_for_info "malformed irot box"; |
|
133
|
0
|
0
|
|
|
|
0
|
$rot = 1 if $angle & 1; |
|
134
|
|
|
|
|
|
|
} |
|
135
|
2
|
50
|
|
|
|
10
|
if(defined(my $clap = $cboxes->{clap})) { |
|
|
|
50
|
|
|
|
|
|
|
136
|
0
|
0
|
|
|
|
0
|
length($clap) >= 32 or die_for_info "malformed clap box"; |
|
137
|
0
|
|
|
|
|
0
|
my($width_num, $width_den, $height_num, $height_den) = |
|
138
|
|
|
|
|
|
|
unpack("NNNN", $clap); |
|
139
|
1
|
0
|
0
|
|
|
4
|
$width_den != 0 && $height_den != 0 |
|
140
|
|
|
|
|
|
|
or die_for_info "malformed clap box"; |
|
141
|
0
|
|
|
|
|
0
|
my $width = int($width_num/$width_den); |
|
142
|
0
|
|
|
|
|
0
|
my $height = int($height_num/$height_den); |
|
143
|
0
|
0
|
|
|
|
0
|
($width, $height) = ($height, $width) if $rot; |
|
144
|
0
|
|
|
|
|
0
|
$info->replace_info(0, width => $width); |
|
145
|
0
|
|
|
|
|
0
|
$info->replace_info(0, height => $height); |
|
146
|
|
|
|
|
|
|
} elsif(defined(my $ispe = $cboxes->{ispe})) { |
|
147
|
2
|
50
|
|
|
|
4
|
length($ispe) >= 12 or die_for_info "malformed ispe box"; |
|
148
|
2
|
|
|
|
|
7
|
my($ver, undef, $width, $height) = unpack("Ca3NN", $ispe); |
|
149
|
2
|
50
|
|
|
|
4
|
$ver == 0 or die_for_info "malformed ispe box"; |
|
150
|
3
|
50
|
|
|
|
27
|
($width, $height) = ($height, $width) if $rot; |
|
151
|
3
|
|
|
|
|
11
|
$info->replace_info(0, width => $width); |
|
152
|
3
|
|
|
|
|
7
|
$info->replace_info(0, height => $height); |
|
153
|
|
|
|
|
|
|
} |
|
154
|
3
|
50
|
|
|
|
8
|
if(defined(my $pixi = $cboxes->{pixi})) { |
|
155
|
3
|
50
|
|
|
|
10
|
length($pixi) >= 5 or die_for_info "malformed pixi box"; |
|
156
|
3
|
|
|
|
|
7
|
my($ver, undef, $planes) = unpack("Ca3C", $pixi); |
|
157
|
3
|
50
|
|
|
|
8
|
$ver == 0 or die_for_info "malformed pixi box"; |
|
158
|
3
|
50
|
|
|
|
9
|
length($pixi) >= 5+$planes or die_for_info "malformed pixi box"; |
|
159
|
3
|
|
|
|
|
19
|
$info->replace_info(0, SamplesPerPixel => $planes); |
|
160
|
|
|
|
|
|
|
$info->replace_info(0, BitsPerSample => |
|
161
|
3
|
|
|
|
|
10
|
[ map { unpack(q(C), substr($pixi, 5+$_, 1)) } 0..$planes-1 ]); |
|
|
7
|
|
|
|
|
19
|
|
|
162
|
|
|
|
|
|
|
} |
|
163
|
3
|
50
|
|
|
|
13
|
if(defined(my $colr = $cboxes->{colr})) { |
|
164
|
3
|
50
|
|
|
|
7
|
length($colr) >= 4 or die_for_info "malformed colr box"; |
|
165
|
5
|
|
|
|
|
13
|
my $type = substr($colr, 0, 4); |
|
166
|
3
|
50
|
|
|
|
18
|
if($type eq "nclx") { |
|
167
|
3
|
50
|
|
|
|
13
|
length($colr) >= 11 or die_for_info "malformed colr box"; |
|
168
|
3
|
|
|
|
|
9
|
my($prim) = unpack("n", substr($colr, 4, 2)); |
|
169
|
3
|
50
|
|
|
|
7
|
if(defined(my $ctype = $primaries_type[$prim])) { |
|
170
|
3
|
|
|
|
|
9
|
$info->replace_info(0, color_type => $ctype); |
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
} |
|
174
|
3
|
50
|
|
|
|
16
|
if(defined(my $pasp = $cboxes->{pasp})) { |
|
175
|
1
|
0
|
|
|
|
3
|
length($pasp) >= 8 or die_for_info "malformed pasp box"; |
|
176
|
1
|
|
|
|
|
3
|
my($hspc, $vspc) = unpack("NN", $pasp); |
|
177
|
1
|
|
|
|
|
4
|
$info->replace_info(0, resolution => "$vspc/$hspc"); |
|
178
|
|
|
|
|
|
|
} |
|
179
|
2
|
|
|
|
|
19
|
1; |
|
180
|
|
|
|
|
|
|
}) { |
|
181
|
0
|
|
|
|
|
0
|
my $err = $@; |
|
182
|
0
|
0
|
|
|
|
0
|
if(ref($err) eq __PACKAGE__."::__ERROR__") { |
|
183
|
1
|
|
|
|
|
10
|
$info->replace_info(0, error => $err->{err}); |
|
184
|
|
|
|
|
|
|
} else { |
|
185
|
0
|
|
|
|
|
|
die $err; |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
} |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
1; |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=begin register |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
MAGIC: /\A....ftypavif/s |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
Supports the basic standard info key names. |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=end register |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=head1 NAME |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
Image::Info::AVIF - AV1 Image File Format support for Image::Info |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
use Image::Info qw(image_info); |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
$info = image_info("image.avif"); |
|
209
|
|
|
|
|
|
|
if($error = $info->{error}) { |
|
210
|
|
|
|
|
|
|
die "Can't parse image info: $error\n"; |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
$color = $info->{color_type}; |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
This module supplies information about AVIF files within the |
|
217
|
|
|
|
|
|
|
L system. It supports the basic standard info key names. |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
L |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=head1 AUTHOR |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
Andrew Main (Zefram) |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
Copyright (C) 2023 Andrew Main (Zefram) |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=head1 LICENSE |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or modify it |
|
234
|
|
|
|
|
|
|
under the same terms as Perl itself. |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENT |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
The development of this module was funded by |
|
239
|
|
|
|
|
|
|
Preisvergleich Internet Services AG. |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=cut |