File Coverage

lib/Image/Info/GIF.pm
Criterion Covered Total %
statement 78 100 78.0
branch 31 54 57.4
condition 2 9 22.2
subroutine 5 6 83.3
pod 1 5 20.0
total 117 174 67.2


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   13 use strict;
  2         4  
  2         2474  
26              
27             sub my_read
28             {
29 425     425 0 658 my($source, $len) = @_;
30 425         385 my $buf;
31 425         2124 my $n = read($source, $buf, $len);
32 425 50       819 die "read failed: $!" unless defined $n;
33 425 50       558 die "short read ($len/$n) at pos " . tell($source) unless $n == $len;
34 425         1306 $buf;
35             }
36              
37             sub read_data_blocks
38             {
39 7     7 0 9 my $source = shift;
40 7         8 my @data;
41 7         8 while (my $len = ord(my_read($source, 1))) {
42 7         14 push(@data, my_read($source, $len));
43             }
44 7         21 join("", @data);
45             }
46              
47             sub seek_data_blocks
48             {
49 7     7 0 9 my $source = shift;
50 7         9 while (my $len = ord(my_read($source, 1))) {
51 343         2769 seek($source, $len, 1);
52             }
53             }
54              
55             sub process_file
56             {
57 7     7 1 11 my($info, $fh) = @_;
58              
59 7         13 my $header = my_read($fh, 13);
60 7 50       38 die "Bad GIF signature"
61             unless $header =~ s/^GIF(8[79]a)//;
62 7         20 my $version = $1;
63 7         23 $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         26 $info->push_info(0, "ScreenWidth" => $sw);
68 7         16 $info->push_info(0, "ScreenHeight" => $sh);
69              
70 7         16 my $color_table_size = 1 << (($packed & 0x07) + 1);
71 7         13 $info->push_info(0, "ColorTableSize" => $color_table_size);
72              
73 7 50       28 $info->push_info(0, "SortedColors" => ($packed & 0x08) ? 1 : 0)
    50          
74             if $version eq "89a";
75              
76 7         18 $info->push_info(0, "ColorResolution", (($packed & 0x70) >> 4) + 1);
77              
78 7         9 my $global_color_table = $packed & 0x80;
79 7 50       22 $info->push_info(0, "GlobalColorTableFlag" => $global_color_table ? 1 : 0);
80 7 50       14 if ($global_color_table) {
81 7         11 $info->push_info(0, "BackgroundColor", $bg);
82             }
83              
84 7 50       13 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         12 $info->push_info(0, "resolution", "1/1");
93             }
94              
95 7         18 $info->push_info(0, "file_media_type" => "image/gif");
96 7         15 $info->push_info(0, "file_ext" => "gif");
97              
98             # more??
99 7 50       15 if ($global_color_table) {
100 7         13 my $color_table = my_read($fh, $color_table_size * 3);
101             #$info->push_info(0, "GlobalColorTable", color_table($color_table));
102             }
103              
104 7         10 my $img_no = 0;
105 7         12 my @comments;
106             my @warnings;
107              
108 7         7 while (1) {
109 21 100       56 last if eof($fh); # EOF
110 19         23 my $intro = ord(my_read($fh, 1));
111 19 100       44 if ($intro == 0x3B) { # trailer (end of image)
    100          
    100          
112 3         8 last;
113             }
114             elsif ($intro == 0x2C) { # new image
115              
116              
117 7 100       12 if (@comments) {
118 3         7 for (@comments) {
119 3         7 $info->push_info(0, "Comment", $_);
120             }
121 3         6 @comments = ();
122             }
123              
124 7         16 $info->push_info($img_no, "color_type" => "Indexed-RGB");
125              
126 7         14 my($x_pos, $y_pos, $w, $h, $packed) =
127             unpack("vvvvC", my_read($fh, 9));
128 7         17 $info->push_info($img_no, "XPosition", $x_pos);
129 7         14 $info->push_info($img_no, "YPosition", $y_pos);
130 7         15 $info->push_info($img_no, "width", $w);
131 7         15 $info->push_info($img_no, "height", $h);
132              
133 7 50       17 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       14 $info->push_info($img_no, "Interlace" => "GIF")
141             if $packed & 0x40;
142              
143 7         11 my $lzw_code_size = ord(my_read($fh, 1));
144             #$info->push_info($img_no, "LZW_MininmCodeSize", $lzw_code_size);
145 7         15 seek_data_blocks($fh); # skip image data
146 7         13 $img_no++;
147             }
148             elsif ($intro == 0x21) { # GIF89a extension
149 7 50       17 push(@warnings, "GIF 89a extensions in 87a")
150             if $version eq "87a";
151              
152 7         12 my $label = ord(my_read($fh, 1));
153 7         15 my $data = read_data_blocks($fh);
154 7 100 66     29 if ($label == 0xF9 && length($data) == 4) { # Graphic Control
    50          
    0          
155 4         10 my($packed, $delay, $trans_color) = unpack("CvC", $data);
156 4         7 my $disposal_method = ($packed >> 2) & 0x07;
157 4 50       14 $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       10 $info->push_info($img_no, "TransparencyIndex" => $trans_color)
163             if $packed & 0x01;
164             }
165             elsif ($label == 0xFE) { # Comment
166 3         11 $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         15 for (@comments) {
192 0         0 $info->push_info(0, "Comment", $_);
193             }
194              
195 7         17 for (@warnings) {
196 2         6 $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__