4 // Copyright (C) 2006 Alexander Macdonald <alex@alexmac.cc>
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
34 namespace Beagle
.Filters
{
36 public class FilterGif
: FilterImage
{
40 ImageBlockSeparator
= 0x2c,
42 BlockIntroducer
= 0x21,
43 BlockTerminator
= 0x00,
45 GraphicControlExtension
= 0xf9,
46 CommentExtension
= 0xfe,
47 PlaintextExtension
= 0x01,
48 ApplicationExtension
= 0xff
51 static public bool Debug
= false;
53 public FilterGif () : base ()
55 AddSupportedFlavor (FilterFlavor
.NewFromMimeType ("image/gif"));
58 protected override void PullImageProperties ()
61 /* Parse the GIF Header to get the version and overall size */
62 byte [] data
= new byte [13];
63 Stream
.Read (data
, 0, data
.Length
);
65 if (data
[0] != 'G' || data
[1] != 'I' || data
[2] != 'F') {
66 Logger
.Log
.Debug ("File is not a GIF file!");
71 char [] gif_version
= { (char) data [3], (char) data [4], (char) data [5] }
;
73 AddProperty (Beagle
.Property
.NewUnsearched ("gif:version", new string (gif_version
)));
75 ushort width
= EndianConverter
.ToUInt16 (data
, 6, true);
76 ushort height
= EndianConverter
.ToUInt16 (data
, 8, true);
82 * Everything from here onwards is for parsing the GIF Stream
83 * and extracting comments and plaintext and working out how
84 * many frames there are and how many times they are supposed
89 int b
, num_frames
= 0, ct_size
;
91 if ((data
[10] & 0x80) == 0x80) {
92 /* A Global Color Table exists */
93 ct_size
= (int) (3 * Math
.Pow(2, (data
[10] & 0x07) + 1));
96 Logger
.Log
.Debug ("ct_size: " + ct_size
);
98 Stream
.Seek (ct_size
, SeekOrigin
.Current
);
101 while ((gb
= (GifBytes
) Stream
.ReadByte ()) != GifBytes
.Trailer
105 case GifBytes
.ImageBlockSeparator
:
109 Logger
.Log
.Debug ("Start of Image Block : " + Stream
.Position
);
111 Stream
.Seek (8, SeekOrigin
.Current
);
112 b
= Stream
.ReadByte ();
114 if ((b
& 0x80) == 0x80) {
115 /* A Local Color Table exists */
117 ct_size
= (int) (3 * Math
.Pow(2, (b
& 0x07) + 1));
120 Logger
.Log
.Debug ("-- Image Block has local color table");
121 Logger
.Log
.Debug ("ct_size: " + ct_size
);
124 Stream
.Seek (ct_size
, SeekOrigin
.Current
);
128 while ((b
= Stream
.ReadByte ()) != 0x0) {
130 Logger
.Log
.Debug ("-- Image Data Block size: " + b
+ " pos: " + Stream
.Position
);
133 Logger
.Log
.Warn ("Invalid Data Block size");
138 Stream
.Seek (b
, SeekOrigin
.Current
);
142 Logger
.Log
.Debug ("-- Image Block end: " + Stream
.Position
);
145 case GifBytes
.BlockIntroducer
:
147 Logger
.Log
.Debug ("Start of Extension : " + Stream
.Position
);
150 case GifBytes
.GraphicControlExtension
:
152 Logger
.Log
.Debug ("-- Graphic Control Extension : " + Stream
.Position
);
154 Stream
.Seek (6, SeekOrigin
.Current
);
157 case GifBytes
.PlaintextExtension
:
159 Logger
.Log
.Debug ("Plaintext Extension: " + Stream
.Position
);
161 Stream
.Seek (13, SeekOrigin
.Current
);
163 while ((b
= Stream
.ReadByte ()) != 0x0) {
165 Logger
.Log
.Debug ("-- Plaintext Data Block size: " + b
+ " pos: " + Stream
.Position
);
168 Logger
.Log
.Warn ("Invalid Plaintext Data Block size!");
173 char [] cbuffer
= new char [b
];
175 for (int i
= 0; i
< b
; i
++)
176 cbuffer
[i
] = (char) Stream
.ReadByte ();
178 AppendText (new string (cbuffer
));
181 Logger
.Log
.Debug ("-- Plaintext Data: " + new string(cbuffer
));
185 Logger
.Log
.Debug ("-- Plaintext Extension End: " + Stream
.Position
);
188 case GifBytes
.CommentExtension
:
190 Logger
.Log
.Debug ("Comment Extension: " + Stream
.Position
);
192 while ((b
= Stream
.ReadByte ()) != 0x0) {
194 Logger
.Log
.Debug ("-- Comment Data Block size: " + b
+ " pos: " + Stream
.Position
);
197 Logger
.Log
.Warn ("Invalid Comment Data Block size!");
202 char [] cbuffer
= new char [b
];
204 for (int i
= 0; i
< b
; i
++) {
205 cbuffer
[i
] = (char) Stream
.ReadByte ();
208 AppendText (new string (cbuffer
));
211 Logger
.Log
.Debug ("-- Comment Data: " + new string(cbuffer
));
215 Logger
.Log
.Debug ("-- Comment Extension End: " + Stream
.Position
);
218 case GifBytes
.ApplicationExtension
:
220 Logger
.Log
.Debug ("Application Extension: " + Stream
.Position
);
224 char [] cbuffer
= new char [11];
225 for (int i
= 0; i
< 11; i
++) {
226 cbuffer
[i
] = (char) Stream
.ReadByte ();
229 string application
= new string (cbuffer
);
231 if (application
== "NETSCAPE2.0") {
233 Logger
.Log
.Debug ("-- Application: 'NETSCAPE2.0'>");
238 b
= Stream
.ReadByte();
241 AddProperty (Beagle
.Property
.NewUnsearched ("gif:loopcount", "infinite"));
243 AddProperty (Beagle
.Property
.NewUnsearched ("gif:loopcount", b
));
247 //unknown extension...
248 while ((b
= Stream
.ReadByte ()) != 0x0) {
250 Logger
.Log
.Debug ("-- Application Data Block size: " + b
+ " pos: " + Stream
.Position
);
253 Logger
.Log
.Warn ("Invalid Application Data Block size!");
258 Stream
.Seek (b
, SeekOrigin
.Current
);
263 Logger
.Log
.Debug ("-- Application Extension End: " + Stream
.Position
);
271 if (Debug
&& gb
== GifBytes
.Trailer
)
272 Logger
.Log
.Debug ("Trailer marker: " + Stream
.Position
);
274 // We got zero frames, this isn't actually a valid GIF file.
275 if (num_frames
== 0) {
276 Logger
.Log
.Debug ("File doesn't contain any GIF frames");
281 AddProperty (Beagle
.Property
.NewUnsearched ("gif:numframes", num_frames
));
282 } catch (Exception e
) {
284 Logger
.Log
.Debug ("-- Exception: {0} - {1}", Stream
.Position
, e
);