Sync: Adding shamap and documentation to enable easier sync next time OHM is updated.
1 #pragma warning disable 675 // Bitwise-or operator used on a sign-extended operand
5 * Class GifDecoder - Decodes a GIF file into one or more frames.
8 * GifDecoder d = new GifDecoder();
9 * d.read("sample.gif");
10 * int n = d.getFrameCount();
11 * for (int i = 0; i < n; i++) {
12 * BufferedImage frame = d.getFrame(i); // frame i
13 * int t = d.getDelay(i); // display duration of frame in milliseconds
14 * // do something with frame
17 * No copyright asserted on the source code of this class. May be used for
18 * any purpose, however, refer to the Unisys LZW patent for any additional
19 * restrictions. Please forward any corrections to kweiner@fmsware.com.
21 * @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy's ImageMagick.
22 * @version 1.03 November 2003
28 using System.Collections;
30 using System.Drawing.Imaging;
33 namespace Aga.Controls
40 get { return _image; }
46 get { return _delay; }
49 public GifFrame(Image im, int del)
56 public class GifDecoder
58 public const int StatusOK = 0;//File read status: No errors.
59 public const int StatusFormatError = 1; //File read status: Error decoding file (may be partially decoded)
60 public const int StatusOpenError = 2; //Unable to open source.
62 private Stream inStream;
65 private int width; // full image width
66 private int height; // full image height
67 private bool gctFlag; // global color table used
68 private int gctSize; // size of global color table
69 private int loopCount = 1; // iterations; 0 = repeat forever
71 private int[] gct; // global color table
72 private int[] lct; // local color table
73 private int[] act; // active color table
75 private int bgIndex; // background color index
76 private int bgColor; // background color
77 private int lastBgColor; // previous bg color
78 private int pixelAspect; // pixel aspect ratio
80 private bool lctFlag; // local color table flag
81 private bool interlace; // interlace flag
82 private int lctSize; // local color table size
84 private int ix, iy, iw, ih; // current image rectangle
85 private Rectangle lastRect; // last image rect
86 private Image image; // current frame
87 private Bitmap bitmap;
88 private Image lastImage; // previous frame
90 private byte[] block = new byte[256]; // current data block
91 private int blockSize = 0; // block size
93 // last graphic control extension info
94 private int dispose = 0;
95 // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
96 private int lastDispose = 0;
97 private bool transparency = false; // use transparent color
98 private int delay = 0; // delay in milliseconds
99 private int transIndex; // transparent color index
101 private const int MaxStackSize = 4096;
102 // max decoder pixel stack size
104 // LZW decoder working arrays
105 private short[] prefix;
106 private byte[] suffix;
107 private byte[] pixelStack;
108 private byte[] pixels;
110 private ArrayList frames; // frames read from current file
111 private int frameCount;
112 private bool _makeTransparent;
115 * Gets the number of frames read from file.
116 * @return frame count
118 public int FrameCount
127 * Gets the first (or only) image read.
129 * @return BufferedImage containing first frame, or null if none.
135 return GetFrame(0).Image;
140 * Gets the "Netscape" iteration count, if any.
141 * A count of 0 means repeat indefinitiely.
143 * @return iteration count if one was specified, else 1.
153 public GifDecoder(Stream stream, bool makeTransparent)
155 _makeTransparent = makeTransparent;
156 if (Read(stream) != 0)
157 throw new InvalidOperationException();
161 * Creates new frame image from current data (and previous
162 * frames as specified by their disposition codes).
164 private int[] GetPixels(Bitmap bitmap)
166 int [] pixels = new int [ 3 * image.Width * image.Height ];
168 for (int th = 0; th < image.Height; th++)
170 for (int tw = 0; tw < image.Width; tw++)
172 Color color = bitmap.GetPixel(tw, th);
173 pixels[count] = color.R;
175 pixels[count] = color.G;
177 pixels[count] = color.B;
184 private void SetPixels(int[] pixels)
187 for (int th = 0; th < image.Height; th++)
189 for (int tw = 0; tw < image.Width; tw++)
191 Color color = Color.FromArgb( pixels[count++] );
192 bitmap.SetPixel( tw, th, color );
195 if (_makeTransparent)
196 bitmap.MakeTransparent(bitmap.GetPixel(0, 0));
199 private void SetPixels()
201 // expose destination image's pixels as int array
203 // (( int ) image.getRaster().getDataBuffer()).getData();
204 int[] dest = GetPixels( bitmap );
206 // fill in starting image contents based on last image's dispose code
209 if (lastDispose == 3)
211 // use image before last
212 int n = frameCount - 2;
215 lastImage = GetFrame(n - 1).Image;
223 if (lastImage != null)
226 // ((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();
227 int[] prev = GetPixels( new Bitmap( lastImage ) );
228 Array.Copy(prev, 0, dest, 0, width * height);
231 if (lastDispose == 2)
233 // fill last image rect area with background color
234 Graphics g = Graphics.FromImage( image );
235 Color c = Color.Empty;
238 c = Color.FromArgb( 0, 0, 0, 0 ); // assume background is transparent
242 c = Color.FromArgb( lastBgColor ) ;
243 // c = new Color(lastBgColor); // use given background color
245 Brush brush = new SolidBrush( c );
246 g.FillRectangle( brush, lastRect );
253 // copy each source line to the appropriate place in the destination
257 for (int i = 0; i < ih; i++)
286 int k = line * width;
287 int dx = k + ix; // start of line in dest
288 int dlim = dx + iw; // end of dest line
289 if ((k + width) < dlim)
291 dlim = k + width; // past dest edge
293 int sx = i * iw; // start of line in source
296 // map color and insert in destination
297 int index = ((int) pixels[sx++]) & 0xff;
311 * Gets the image contents of frame n.
313 * @return BufferedImage representation of frame.
315 public GifFrame GetFrame(int n)
317 if ((n >= 0) && (n < frameCount))
318 return (GifFrame)frames[n];
320 throw new ArgumentOutOfRangeException();
326 * @return GIF image dimensions
328 public Size FrameSize
332 return new Size(width, height);
337 * Reads GIF image from stream
339 * @param BufferedInputStream containing GIF file.
340 * @return read status code (0 = no errors)
342 private int Read( Stream inStream )
345 if ( inStream != null)
347 this.inStream = inStream;
354 status = StatusFormatError;
361 status = StatusOpenError;
368 * Decodes LZW image data into pixel array.
369 * Adapted from John Cristy's ImageMagick.
371 private void DecodeImageData()
393 if ((pixels == null) || (pixels.Length < npix))
395 pixels = new byte[npix]; // allocate new pixel array
397 if (prefix == null) prefix = new short[MaxStackSize];
398 if (suffix == null) suffix = new byte[MaxStackSize];
399 if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1];
401 // Initialize GIF data stream decoder.
404 clear = 1 << data_size;
405 end_of_information = clear + 1;
406 available = clear + 2;
408 code_size = data_size + 1;
409 code_mask = (1 << code_size) - 1;
410 for (code = 0; code < clear; code++)
413 suffix[code] = (byte) code;
416 // Decode GIF pixel stream.
418 datum = bits = count = first = top = pi = bi = 0;
420 for (i = 0; i < npix;)
424 if (bits < code_size)
426 // Load bytes until there are enough bits for a code.
429 // Read a new data block.
435 datum += (((int) block[bi]) & 0xff) << bits;
442 // Get the next code.
444 code = datum & code_mask;
448 // Interpret the code
450 if ((code > available) || (code == end_of_information))
455 code_size = data_size + 1;
456 code_mask = (1 << code_size) - 1;
457 available = clear + 2;
461 if (old_code == NullCode)
463 pixelStack[top++] = suffix[code];
469 if (code == available)
471 pixelStack[top++] = (byte) first;
476 pixelStack[top++] = suffix[code];
479 first = ((int) suffix[code]) & 0xff;
481 // Add a new string to the string table,
483 if (available >= MaxStackSize)
485 pixelStack[top++] = (byte) first;
486 prefix[available] = (short) old_code;
487 suffix[available] = (byte) first;
489 if (((available & code_mask) == 0)
490 && (available < MaxStackSize))
493 code_mask += available;
498 // Pop a pixel off the pixel stack.
501 pixels[pi++] = pixelStack[top];
505 for (i = pi; i < npix; i++)
507 pixels[i] = 0; // clear missing pixels
513 * Returns true if an error was encountered during reading/decoding
517 return status != StatusOK;
521 * Initializes or re-initializes reader
527 frames = new ArrayList();
533 * Reads a single byte from the input stream.
540 curByte = inStream.ReadByte();
544 status = StatusFormatError;
550 * Reads next variable length block from input.
552 * @return number of bytes stored in "buffer"
554 private int ReadBlock()
563 while (n < blockSize)
565 count = inStream.Read(block, n, blockSize - n);
577 status = StatusFormatError;
584 * Reads color table as 256 RGB integer values
586 * @param ncolors int number of colors to read
587 * @return int array containing 256 colors (packed ARGB with full alpha)
589 private int[] ReadColorTable(int ncolors)
591 int nbytes = 3 * ncolors;
593 byte[] c = new byte[nbytes];
597 n = inStream.Read(c, 0, c.Length );
604 status = StatusFormatError;
608 tab = new int[256]; // max size to avoid bounds checks
613 int r = ((int) c[j++]) & 0xff;
614 int g = ((int) c[j++]) & 0xff;
615 int b = ((int) c[j++]) & 0xff;
616 tab[i++] = ( int ) ( 0xff000000 | (r << 16) | (g << 8) | b );
623 * Main file parser. Reads GIF content blocks.
625 private void ReadContents()
627 // read GIF file content blocks
629 while (!(done || Error()))
635 case 0x2C : // image separator
639 case 0x21 : // extension
643 case 0xf9 : // graphics control extension
644 ReadGraphicControlExt();
647 case 0xff : // application extension
650 for (int i = 0; i < 11; i++)
652 app += (char) block[i];
654 if (app.Equals("NETSCAPE2.0"))
659 Skip(); // don't care
662 default : // uninteresting extension
668 case 0x3b : // terminator
672 case 0x00 : // bad byte, but keep going and see what happens
676 status = StatusFormatError;
683 * Reads Graphics Control Extension values
685 private void ReadGraphicControlExt()
687 Read(); // block size
688 int packed = Read(); // packed fields
689 dispose = (packed & 0x1c) >> 2; // disposal method
692 dispose = 1; // elect to keep old image if discretionary
694 transparency = (packed & 1) != 0;
695 delay = ReadShort() * 10; // delay in milliseconds
696 transIndex = Read(); // transparent color index
697 Read(); // block terminator
701 * Reads GIF file header information.
703 private void ReadHeader()
706 for (int i = 0; i < 6; i++)
710 if (!id.StartsWith("GIF"))
712 status = StatusFormatError;
717 if (gctFlag && !Error())
719 gct = ReadColorTable(gctSize);
720 bgColor = gct[bgIndex];
725 * Reads next frame image
727 private void ReadImage()
729 ix = ReadShort(); // (sub)image position & size
735 lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
736 interlace = (packed & 0x40) != 0; // 2 - interlace flag
739 lctSize = 2 << (packed & 7); // 6-8 - local color table size
743 lct = ReadColorTable(lctSize); // read table
744 act = lct; // make local table active
748 act = gct; // make global table active
749 if (bgIndex == transIndex)
755 save = act[transIndex];
756 act[transIndex] = 0; // set transparent color if specified
761 status = StatusFormatError; // no color table defined
766 DecodeImageData(); // decode pixel data
773 // create new image to receive frame data
775 // new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
777 bitmap = new Bitmap( width, height );
779 SetPixels(); // transfer pixel data to image
781 frames.Add(new GifFrame(bitmap, delay)); // add image to frame list
785 act[transIndex] = save;
792 * Reads Logical Screen Descriptor
794 private void ReadLSD()
797 // logical screen size
799 height = ReadShort();
803 gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
804 // 2-4 : color resolution
806 gctSize = 2 << (packed & 7); // 6-8 : gct size
808 bgIndex = Read(); // background color index
809 pixelAspect = Read(); // pixel aspect ratio
813 * Reads Netscape extenstion to obtain iteration count
815 private void ReadNetscapeExt()
822 // loop count sub-block
823 int b1 = ((int) block[1]) & 0xff;
824 int b2 = ((int) block[2]) & 0xff;
825 loopCount = (b2 << 8) | b1;
827 } while ((blockSize > 0) && !Error());
831 * Reads next 16-bit value, LSB first
833 private int ReadShort()
835 // read 16-bit value, LSB first
836 return Read() | (Read() << 8);
840 * Resets frame state for reading next image.
842 private void ResetFrame()
844 lastDispose = dispose;
845 lastRect = new Rectangle(ix, iy, iw, ih);
847 lastBgColor = bgColor;
853 * Skips variable length blocks up to and including
854 * next zero length block.
861 } while ((blockSize > 0) && !Error());