| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Image::Info::GIF; |
|
2
|
|
|
|
|
|
|
$VERSION = '1.02'; |
|
3
|
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
# Copyright 1999-2000, Gisle Aas. |
|
5
|
|
|
|
|
|
|
# |
|
6
|
|
|
|
|
|
|
# This library is free software; you can redistribute it and/or |
|
7
|
|
|
|
|
|
|
# modify it under the same terms as Perl itself. |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=begin register |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
MAGIC: /^GIF8[79]a/ |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Both GIF87a and GIF89a are supported and the version number is found |
|
14
|
|
|
|
|
|
|
as C for the first image. GIF files can contain multiple |
|
15
|
|
|
|
|
|
|
images, and information for all images will be returned if |
|
16
|
|
|
|
|
|
|
image_info() is called in list context. The Netscape-2.0 extension to |
|
17
|
|
|
|
|
|
|
loop animation sequences is represented by the C key for the |
|
18
|
|
|
|
|
|
|
first image. The value is either "forever" or a number indicating |
|
19
|
|
|
|
|
|
|
loop count. |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=end register |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=cut |
|
24
|
|
|
|
|
|
|
|
|
25
|
2
|
|
|
2
|
|
14
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
2511
|
|
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub my_read |
|
28
|
|
|
|
|
|
|
{ |
|
29
|
425
|
|
|
425
|
0
|
689
|
my($source, $len) = @_; |
|
30
|
425
|
|
|
|
|
407
|
my $buf; |
|
31
|
425
|
|
|
|
|
2129
|
my $n = read($source, $buf, $len); |
|
32
|
425
|
50
|
|
|
|
864
|
die "read failed: $!" unless defined $n; |
|
33
|
425
|
50
|
|
|
|
588
|
die "short read ($len/$n) at pos " . tell($source) unless $n == $len; |
|
34
|
425
|
|
|
|
|
943
|
$buf; |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub read_data_blocks |
|
38
|
|
|
|
|
|
|
{ |
|
39
|
7
|
|
|
7
|
0
|
11
|
my $source = shift; |
|
40
|
7
|
|
|
|
|
53
|
my @data; |
|
41
|
7
|
|
|
|
|
15
|
while (my $len = ord(my_read($source, 1))) { |
|
42
|
7
|
|
|
|
|
12
|
push(@data, my_read($source, $len)); |
|
43
|
|
|
|
|
|
|
} |
|
44
|
7
|
|
|
|
|
26
|
join("", @data); |
|
45
|
|
|
|
|
|
|
} |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub seek_data_blocks |
|
48
|
|
|
|
|
|
|
{ |
|
49
|
7
|
|
|
7
|
0
|
9
|
my $source = shift; |
|
50
|
7
|
|
|
|
|
13
|
while (my $len = ord(my_read($source, 1))) { |
|
51
|
343
|
|
|
|
|
2864
|
seek($source, $len, 1); |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub process_file |
|
56
|
|
|
|
|
|
|
{ |
|
57
|
7
|
|
|
7
|
1
|
16
|
my($info, $fh) = @_; |
|
58
|
|
|
|
|
|
|
|
|
59
|
7
|
|
|
|
|
15
|
my $header = my_read($fh, 13); |
|
60
|
7
|
50
|
|
|
|
41
|
die "Bad GIF signature" |
|
61
|
|
|
|
|
|
|
unless $header =~ s/^GIF(8[79]a)//; |
|
62
|
7
|
|
|
|
|
20
|
my $version = $1; |
|
63
|
7
|
|
|
|
|
27
|
$info->push_info(0, "GIF_Version" => $version); |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# process logical screen descriptor |
|
66
|
7
|
|
|
|
|
34
|
my($sw, $sh, $packed, $bg, $aspect) = unpack("vvCCC", $header); |
|
67
|
7
|
|
|
|
|
30
|
$info->push_info(0, "ScreenWidth" => $sw); |
|
68
|
7
|
|
|
|
|
18
|
$info->push_info(0, "ScreenHeight" => $sh); |
|
69
|
|
|
|
|
|
|
|
|
70
|
7
|
|
|
|
|
16
|
my $color_table_size = 1 << (($packed & 0x07) + 1); |
|
71
|
7
|
|
|
|
|
15
|
$info->push_info(0, "ColorTableSize" => $color_table_size); |
|
72
|
|
|
|
|
|
|
|
|
73
|
7
|
50
|
|
|
|
31
|
$info->push_info(0, "SortedColors" => ($packed & 0x08) ? 1 : 0) |
|
|
|
50
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
if $version eq "89a"; |
|
75
|
|
|
|
|
|
|
|
|
76
|
7
|
|
|
|
|
19
|
$info->push_info(0, "ColorResolution", (($packed & 0x70) >> 4) + 1); |
|
77
|
|
|
|
|
|
|
|
|
78
|
7
|
|
|
|
|
10
|
my $global_color_table = $packed & 0x80; |
|
79
|
7
|
50
|
|
|
|
24
|
$info->push_info(0, "GlobalColorTableFlag" => $global_color_table ? 1 : 0); |
|
80
|
7
|
50
|
|
|
|
14
|
if ($global_color_table) { |
|
81
|
7
|
|
|
|
|
13
|
$info->push_info(0, "BackgroundColor", $bg); |
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
|
|
84
|
7
|
50
|
|
|
|
14
|
if ($aspect) { |
|
85
|
0
|
|
|
|
|
0
|
$aspect = ($aspect + 15) / 64; |
|
86
|
0
|
|
|
|
|
0
|
$info->push_info(0, "PixelAspectRatio" => $aspect); |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# XXX is this correct???? |
|
89
|
0
|
|
|
|
|
0
|
$info->push_info(0, "resolution", "1/$aspect"); |
|
90
|
|
|
|
|
|
|
} |
|
91
|
|
|
|
|
|
|
else { |
|
92
|
7
|
|
|
|
|
14
|
$info->push_info(0, "resolution", "1/1"); |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
7
|
|
|
|
|
16
|
$info->push_info(0, "file_media_type" => "image/gif"); |
|
96
|
7
|
|
|
|
|
14
|
$info->push_info(0, "file_ext" => "gif"); |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# more?? |
|
99
|
7
|
50
|
|
|
|
15
|
if ($global_color_table) { |
|
100
|
7
|
|
|
|
|
17
|
my $color_table = my_read($fh, $color_table_size * 3); |
|
101
|
|
|
|
|
|
|
#$info->push_info(0, "GlobalColorTable", color_table($color_table)); |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
7
|
|
|
|
|
17
|
my $img_no = 0; |
|
105
|
7
|
|
|
|
|
11
|
my @comments; |
|
106
|
|
|
|
|
|
|
my @warnings; |
|
107
|
|
|
|
|
|
|
|
|
108
|
7
|
|
|
|
|
8
|
while (1) { |
|
109
|
21
|
100
|
|
|
|
52
|
last if eof($fh); # EOF |
|
110
|
19
|
|
|
|
|
28
|
my $intro = ord(my_read($fh, 1)); |
|
111
|
19
|
100
|
|
|
|
57
|
if ($intro == 0x3B) { # trailer (end of image) |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
112
|
3
|
|
|
|
|
11
|
last; |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
elsif ($intro == 0x2C) { # new image |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
|
|
117
|
7
|
100
|
|
|
|
17
|
if (@comments) { |
|
118
|
3
|
|
|
|
|
6
|
for (@comments) { |
|
119
|
3
|
|
|
|
|
9
|
$info->push_info(0, "Comment", $_); |
|
120
|
|
|
|
|
|
|
} |
|
121
|
3
|
|
|
|
|
15
|
@comments = (); |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
|
|
124
|
7
|
|
|
|
|
20
|
$info->push_info($img_no, "color_type" => "Indexed-RGB"); |
|
125
|
|
|
|
|
|
|
|
|
126
|
7
|
|
|
|
|
12
|
my($x_pos, $y_pos, $w, $h, $packed) = |
|
127
|
|
|
|
|
|
|
unpack("vvvvC", my_read($fh, 9)); |
|
128
|
7
|
|
|
|
|
21
|
$info->push_info($img_no, "XPosition", $x_pos); |
|
129
|
7
|
|
|
|
|
15
|
$info->push_info($img_no, "YPosition", $y_pos); |
|
130
|
7
|
|
|
|
|
13
|
$info->push_info($img_no, "width", $w); |
|
131
|
7
|
|
|
|
|
13
|
$info->push_info($img_no, "height", $h); |
|
132
|
|
|
|
|
|
|
|
|
133
|
7
|
50
|
|
|
|
23
|
if ($packed & 0x80) { |
|
134
|
|
|
|
|
|
|
# yes, we have a local color table |
|
135
|
0
|
|
|
|
|
0
|
my $ct_size = 1 << (($packed & 0x07) + 1); |
|
136
|
0
|
|
|
|
|
0
|
$info->push_info($img_no, "LColorTableSize" => $ct_size); |
|
137
|
0
|
|
|
|
|
0
|
my $color_table = my_read($fh, $ct_size * 3); |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
|
|
140
|
7
|
100
|
|
|
|
25
|
$info->push_info($img_no, "Interlace" => "GIF") |
|
141
|
|
|
|
|
|
|
if $packed & 0x40; |
|
142
|
|
|
|
|
|
|
|
|
143
|
7
|
|
|
|
|
12
|
my $lzw_code_size = ord(my_read($fh, 1)); |
|
144
|
|
|
|
|
|
|
#$info->push_info($img_no, "LZW_MininmCodeSize", $lzw_code_size); |
|
145
|
7
|
|
|
|
|
16
|
seek_data_blocks($fh); # skip image data |
|
146
|
7
|
|
|
|
|
14
|
$img_no++; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
elsif ($intro == 0x21) { # GIF89a extension |
|
149
|
7
|
50
|
|
|
|
18
|
push(@warnings, "GIF 89a extensions in 87a") |
|
150
|
|
|
|
|
|
|
if $version eq "87a"; |
|
151
|
|
|
|
|
|
|
|
|
152
|
7
|
|
|
|
|
11
|
my $label = ord(my_read($fh, 1)); |
|
153
|
7
|
|
|
|
|
29
|
my $data = read_data_blocks($fh); |
|
154
|
7
|
100
|
66
|
|
|
31
|
if ($label == 0xF9 && length($data) == 4) { # Graphic Control |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
155
|
4
|
|
|
|
|
10
|
my($packed, $delay, $trans_color) = unpack("CvC", $data); |
|
156
|
4
|
|
|
|
|
8
|
my $disposal_method = ($packed >> 2) & 0x07; |
|
157
|
4
|
50
|
|
|
|
9
|
$info->push_info($img_no, "DisposalMethod", $disposal_method) |
|
158
|
|
|
|
|
|
|
if $disposal_method; |
|
159
|
4
|
50
|
|
|
|
6
|
$info->push_info($img_no, "UserInput", 1) |
|
160
|
|
|
|
|
|
|
if $packed & 0x02; |
|
161
|
4
|
50
|
|
|
|
7
|
$info->push_info($img_no, "Delay" => $delay/100) if $delay; |
|
162
|
4
|
100
|
|
|
|
11
|
$info->push_info($img_no, "TransparencyIndex" => $trans_color) |
|
163
|
|
|
|
|
|
|
if $packed & 0x01; |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
elsif ($label == 0xFE) { # Comment |
|
166
|
3
|
|
|
|
|
8
|
$data =~ s/\0+$//; # is often NUL-terminated |
|
167
|
3
|
|
|
|
|
7
|
push(@comments, $data); |
|
168
|
|
|
|
|
|
|
} |
|
169
|
|
|
|
|
|
|
elsif ($label == 0xFF) { # Application |
|
170
|
0
|
|
|
|
|
0
|
my $app = substr($data, 0, 11, ""); |
|
171
|
0
|
|
|
|
|
0
|
my $auth = substr($app, -3, 3, ""); |
|
172
|
0
|
0
|
0
|
|
|
0
|
if ($app eq "NETSCAPE" && $auth eq "2.0" |
|
|
|
|
0
|
|
|
|
|
|
173
|
|
|
|
|
|
|
&& $data =~ /^\01/) { |
|
174
|
0
|
|
|
|
|
0
|
my $loop = unpack("xv", $data); |
|
175
|
0
|
0
|
|
|
|
0
|
$loop = "forever" unless $loop; |
|
176
|
0
|
|
|
|
|
0
|
$info->push_info(0, "GIF_Loop" => $loop); |
|
177
|
|
|
|
|
|
|
} else { |
|
178
|
0
|
|
|
|
|
0
|
$info->push_info(0, "APP-$app-$auth" => $data); |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
} |
|
181
|
|
|
|
|
|
|
else { |
|
182
|
0
|
|
|
|
|
0
|
$info->push_info($img_no, "GIF_Extension-$label" => $data); |
|
183
|
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
else { |
|
186
|
2
|
|
|
|
|
7
|
push @warnings, "Unknown introduced code $intro, ignoring following chunks"; |
|
187
|
2
|
|
|
|
|
4
|
last; |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
7
|
|
|
|
|
16
|
for (@comments) { |
|
192
|
0
|
|
|
|
|
0
|
$info->push_info(0, "Comment", $_); |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
|
|
195
|
7
|
|
|
|
|
20
|
for (@warnings) { |
|
196
|
2
|
|
|
|
|
5
|
$info->push_info(0, "Warn", $_); |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
} |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub color_table |
|
201
|
|
|
|
|
|
|
{ |
|
202
|
0
|
|
|
0
|
0
|
|
my @n = unpack("C*", shift); |
|
203
|
0
|
0
|
|
|
|
|
die "Color table not a multiple of 3" if @n % 3; |
|
204
|
0
|
|
|
|
|
|
my @table; |
|
205
|
0
|
|
|
|
|
|
while (@n) { |
|
206
|
0
|
|
|
|
|
|
my @triple = splice(@n, -3); |
|
207
|
0
|
|
|
|
|
|
push(@table, sprintf("#%02x%02x%02x", @triple)); |
|
208
|
|
|
|
|
|
|
} |
|
209
|
0
|
|
|
|
|
|
[reverse @table]; |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
1; |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
__END__ |