context: #region Java Info
  /**
   * Class GifDecoder - Decodes a GIF file into one or more frames.
   * <br><pre>
   * Example:
   *    GifDecoder d = new GifDecoder();
   *    d.read("sample.gif");
   *    int n = d.getFrameCount();
   *    for (int i = 0; i < n; i++) {
   *       BufferedImage frame = d.getFrame(i);  // frame i
   *       int t = d.getDelay(i);  // display duration of frame in milliseconds
   *       // do something with frame
   *    }
   * </pre>
   * No copyright asserted on the source code of this class.  May be used for
   * any purpose, however, refer to the Unisys LZW patent for any additional
   * restrictions.  Please forward any corrections to kweiner@fmsware.com.
   *
   * @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy's ImageMagick.
   * @version 1.03 November 2003
   *
   */
  #endregion
  
  #pragma warning disable 0675
  
  using System;
  using System.Collections;
  using System.Drawing;
  using System.Drawing.Imaging;
  using System.IO;
  
  namespace Aga.Controls
  {
  	public class GifFrame
  	{
  		private Image _image;
  		public Image Image
  		{
  			get { return _image; }
  		}
  
  		private int _delay;
  		public int Delay
  		{
  			get { return _delay; }
  		}
  
  		public GifFrame(Image im, int del)
  		{
  			_image = im;
  			_delay = del;
  		}
  	}
  
  	public class GifDecoder 
  	{
  		public const int StatusOK = 0;//File read status: No errors.
  		public const int StatusFormatError = 1; //File read status: Error decoding file (may be partially decoded)
  		public const int StatusOpenError = 2; //Unable to open source.
  
  		private Stream inStream;
  		private int status;
  
  		private int width; // full image width
  		private int height; // full image height
  		private bool gctFlag; // global color table used
  		private int gctSize; // size of global color table
  		private int loopCount = 1; // iterations; 0 = repeat forever
  
  		private int[] gct; // global color table
  		private int[] lct; // local color table
  		private int[] act; // active color table
  
  		private int bgIndex; // background color index
  		private int bgColor; // background color
  		private int lastBgColor; // previous bg color
  		private int pixelAspect; // pixel aspect ratio
  
  		private bool lctFlag; // local color table flag
  		private bool interlace; // interlace flag
  		private int lctSize; // local color table size
  
  		private int ix, iy, iw, ih; // current image rectangle
  		private Rectangle lastRect; // last image rect
  		private Image image; // current frame
  		private Bitmap bitmap;
  		private Image lastImage; // previous frame
  
  		private byte[] block = new byte[256]; // current data block
  		private int blockSize = 0; // block size
  
  		// last graphic control extension info
  		private int dispose = 0;
  		// 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
  		private int lastDispose = 0;
  		private bool transparency = false; // use transparent color
  		private int delay = 0; // delay in milliseconds
  		private int transIndex; // transparent color index
  
  		private const int MaxStackSize = 4096;
  		// max decoder pixel stack size
  
  		// LZW decoder working arrays
  		private short[] prefix;
  		private byte[] suffix;
  		private byte[] pixelStack;
  		private byte[] pixels;
  
  		private ArrayList frames; // frames read from current file
  		private int frameCount;
  		private bool _makeTransparent;
  
  		/**
  		 * Gets the number of frames read from file.
  		 * @return frame count
  		 */
  		public int FrameCount
  		{
  			get
  			{
  				return frameCount;
  			}
  		}
  
  		/**
  		 * Gets the first (or only) image read.
  		 *
  		 * @return BufferedImage containing first frame, or null if none.
  		 */
  		public Image Image
  		{
  			get
  			{
  				return GetFrame(0).Image;
  			}
  		}
  
  		/**
  		 * Gets the "Netscape" iteration count, if any.
  		 * A count of 0 means repeat indefinitiely.
  		 *
  		 * @return iteration count if one was specified, else 1.
  		 */
  		public int LoopCount
  		{
  			get
  			{
  				return loopCount;
  			}
  		}
  
  		public GifDecoder(Stream stream, bool makeTransparent)
  		{
  			_makeTransparent = makeTransparent;
  			if (Read(stream) != 0)
  				throw new InvalidOperationException();
  		}
  
  		/**
  		 * Creates new frame image from current data (and previous
  		 * frames as specified by their disposition codes).
  		 */
  		private int[] GetPixels(Bitmap bitmap)
  		{
  			int [] pixels = new int [ 3 * image.Width * image.Height ];
  			int count = 0;
  			for (int th = 0; th < image.Height; th++)
  			{
  				for (int tw = 0; tw < image.Width; tw++)
  				{
  					Color color = bitmap.GetPixel(tw, th);
  					pixels[count] = color.R;
  					count++;
  					pixels[count] = color.G;
  					count++;
  					pixels[count] = color.B;
  					count++;
  				}
  			}
  			return pixels;
  		}
  
  		private void SetPixels(int[] pixels)
  		{
  			int count = 0;
  			for (int th = 0; th < image.Height; th++)
  			{
  				for (int tw = 0; tw < image.Width; tw++)
  				{
  					Color color = Color.FromArgb( pixels[count++] );
  					bitmap.SetPixel( tw, th, color );
  				}
  			}
  			if (_makeTransparent)
  				bitmap.MakeTransparent(bitmap.GetPixel(0, 0));
  		}
  
  		private void SetPixels() 
  		{
  			// expose destination image's pixels as int array
  			//		int[] dest =
  			//			(( int ) image.getRaster().getDataBuffer()).getData();
  			int[] dest = GetPixels( bitmap );
  
  			// fill in starting image contents based on last image's dispose code
  			if (lastDispose > 0) 
  			{
  				if (lastDispose == 3) 
  				{
  					// use image before last
  					int n = frameCount - 2;
  					if (n > 0) 
  					{
  						lastImage = GetFrame(n - 1).Image;
  					} 
  					else 
  					{
  						lastImage = null;
  					}
  				}
  
  				if (lastImage != null) 
  				{
  					//				int[] prev =
  					//					((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();
  					int[] prev = GetPixels( new Bitmap( lastImage ) );
  					Array.Copy(prev, 0, dest, 0, width * height);
  					// copy pixels
  
  					if (lastDispose == 2) 
  					{
  						// fill last image rect area with background color
  						Graphics g = Graphics.FromImage( image );
  						Color c = Color.Empty;
  						if (transparency) 
  						{
  							c = Color.FromArgb( 0, 0, 0, 0 ); 	// assume background is transparent
  						} 
  						else 
  						{
  							c = Color.FromArgb( lastBgColor ) ;
  							//						c = new Color(lastBgColor); // use given background color
  						}
  						Brush brush = new SolidBrush( c );
  						g.FillRectangle( brush, lastRect );
  						brush.Dispose();
  						g.Dispose();
  					}
  				}
  			}
  
  			// copy each source line to the appropriate place in the destination
  			int pass = 1;
  			int inc = 8;
  			int iline = 0;
  			for (int i = 0; i < ih; i++) 
  			{
  				int line = i;
  				if (interlace) 
  				{
  					if (iline >= ih) 
  					{
  						pass++;
  						switch (pass) 
  						{
  							case 2 :
  								iline = 4;
  								break;
  							case 3 :
  								iline = 2;
  								inc = 4;
  								break;
  							case 4 :
  								iline = 1;
  								inc = 2;
  								break;
  						}
  					}
  					line = iline;
  					iline += inc;
  				}
  				line += iy;
  				if (line < height) 
  				{
  					int k = line * width;
  					int dx = k + ix; // start of line in dest
  					int dlim = dx + iw; // end of dest line
  					if ((k + width) < dlim) 
  					{
  						dlim = k + width; // past dest edge
  					}
  					int sx = i * iw; // start of line in source
  					while (dx < dlim) 
  					{
  						// map color and insert in destination
  						int index = ((int) pixels[sx++]) & 0xff;
  						int c = act[index];
  						if (c != 0) 
  						{
  							dest[dx] = c;
  						}
  						dx++;
  					}
  				}
  			}
  			SetPixels( dest );
  		}
  
  		/**
  		 * Gets the image contents of frame n.
  		 *
  		 * @return BufferedImage representation of frame.
  		 */
  		public GifFrame GetFrame(int n) 
  		{
  			if ((n >= 0) && (n < frameCount))
  				return (GifFrame)frames[n];
  			else
  				throw new ArgumentOutOfRangeException();
  		}
  
  		/**
  		 * Gets image size.
  		 *
  		 * @return GIF image dimensions
  		 */
  		public Size FrameSize
  		{
  			get
  			{
  				return new Size(width, height);
  			}
  		}
  
  		/**
  		 * Reads GIF image from stream
  		 *
  		 * @param BufferedInputStream containing GIF file.
  		 * @return read status code (0 = no errors)
  		 */
  		private int Read( Stream inStream ) 
  		{
  			Init();
  			if ( inStream != null) 
  			{
  				this.inStream = inStream;
  				ReadHeader();
  				if (!Error()) 
  				{
  					ReadContents();
  					if (frameCount < 0) 
  					{
  						status = StatusFormatError;
  					}
  				}
  				inStream.Close();
  			} 
  			else 
  			{
  				status = StatusOpenError;
  			}
  			return status;
  		}
  
  
  		/**
  		 * Decodes LZW image data into pixel array.
  		 * Adapted from John Cristy's ImageMagick.
  		 */
  		private void DecodeImageData() 
  		{
  			int NullCode = -1;
  			int npix = iw * ih;
  			int available, 
  				clear,
  				code_mask,
  				code_size,
  				end_of_information,
  				in_code,
  				old_code,
  				bits,
  				code,
  				count,
  				i,
  				datum,
  				data_size,
  				first,
  				top,
  				bi,
  				pi;
  
  			if ((pixels == null) || (pixels.Length < npix)) 
  			{
  				pixels = new byte[npix]; // allocate new pixel array
  			}
  			if (prefix == null) prefix = new short[MaxStackSize];
  			if (suffix == null) suffix = new byte[MaxStackSize];
  			if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1];
  
  			//  Initialize GIF data stream decoder.
  
  			data_size = Read();
  			clear = 1 << data_size;
  			end_of_information = clear + 1;
  			available = clear + 2;
  			old_code = NullCode;
  			code_size = data_size + 1;
  			code_mask = (1 << code_size) - 1;
  			for (code = 0; code < clear; code++) 
  			{
  				prefix[code] = 0;
  				suffix[code] = (byte) code;
  			}
  
  			//  Decode GIF pixel stream.
  
  			datum = bits = count = first = top = pi = bi = 0;
  
  			for (i = 0; i < npix;) 
  			{
  				if (top == 0) 
  				{
  					if (bits < code_size) 
  					{
  						//  Load bytes until there are enough bits for a code.
  						if (count == 0) 
  						{
  							// Read a new data block.
  							count = ReadBlock();
  							if (count <= 0)
  								break;
  							bi = 0;
  						}
  
gt: 						datum += (((int) block[bi]) & 0xff) << bits;