moel@345: #pragma warning disable 675 // Bitwise-or operator used on a sign-extended operand
moel@345: 
moel@345: #region Java Info
moel@345: /**
moel@345:  * Class GifDecoder - Decodes a GIF file into one or more frames.
moel@345:  * <br><pre>
moel@345:  * Example:
moel@345:  *    GifDecoder d = new GifDecoder();
moel@345:  *    d.read("sample.gif");
moel@345:  *    int n = d.getFrameCount();
moel@345:  *    for (int i = 0; i < n; i++) {
moel@345:  *       BufferedImage frame = d.getFrame(i);  // frame i
moel@345:  *       int t = d.getDelay(i);  // display duration of frame in milliseconds
moel@345:  *       // do something with frame
moel@345:  *    }
moel@345:  * </pre>
moel@345:  * No copyright asserted on the source code of this class.  May be used for
moel@345:  * any purpose, however, refer to the Unisys LZW patent for any additional
moel@345:  * restrictions.  Please forward any corrections to kweiner@fmsware.com.
moel@345:  *
moel@345:  * @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy's ImageMagick.
moel@345:  * @version 1.03 November 2003
moel@345:  *
moel@345:  */
moel@345: #endregion
moel@345: 
moel@345: using System;
moel@345: using System.Collections;
moel@345: using System.Drawing;
moel@345: using System.Drawing.Imaging;
moel@345: using System.IO;
moel@345: 
moel@345: namespace Aga.Controls
moel@345: {
moel@345: 	public class GifFrame
moel@345: 	{
moel@345: 		private Image _image;
moel@345: 		public Image Image
moel@345: 		{
moel@345: 			get { return _image; }
moel@345: 		}
moel@345: 
moel@345: 		private int _delay;
moel@345: 		public int Delay
moel@345: 		{
moel@345: 			get { return _delay; }
moel@345: 		}
moel@345: 
moel@345: 		public GifFrame(Image im, int del)
moel@345: 		{
moel@345: 			_image = im;
moel@345: 			_delay = del;
moel@345: 		}
moel@345: 	}
moel@345: 
moel@345: 	public class GifDecoder 
moel@345: 	{
moel@345: 		public const int StatusOK = 0;//File read status: No errors.
moel@345: 		public const int StatusFormatError = 1; //File read status: Error decoding file (may be partially decoded)
moel@345: 		public const int StatusOpenError = 2; //Unable to open source.
moel@345: 
moel@345: 		private Stream inStream;
moel@345: 		private int status;
moel@345: 
moel@345: 		private int width; // full image width
moel@345: 		private int height; // full image height
moel@345: 		private bool gctFlag; // global color table used
moel@345: 		private int gctSize; // size of global color table
moel@345: 		private int loopCount = 1; // iterations; 0 = repeat forever
moel@345: 
moel@345: 		private int[] gct; // global color table
moel@345: 		private int[] lct; // local color table
moel@345: 		private int[] act; // active color table
moel@345: 
moel@345: 		private int bgIndex; // background color index
moel@345: 		private int bgColor; // background color
moel@345: 		private int lastBgColor; // previous bg color
moel@345: 		private int pixelAspect; // pixel aspect ratio
moel@345: 
moel@345: 		private bool lctFlag; // local color table flag
moel@345: 		private bool interlace; // interlace flag
moel@345: 		private int lctSize; // local color table size
moel@345: 
moel@345: 		private int ix, iy, iw, ih; // current image rectangle
moel@345: 		private Rectangle lastRect; // last image rect
moel@345: 		private Image image; // current frame
moel@345: 		private Bitmap bitmap;
moel@345: 		private Image lastImage; // previous frame
moel@345: 
moel@345: 		private byte[] block = new byte[256]; // current data block
moel@345: 		private int blockSize = 0; // block size
moel@345: 
moel@345: 		// last graphic control extension info
moel@345: 		private int dispose = 0;
moel@345: 		// 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
moel@345: 		private int lastDispose = 0;
moel@345: 		private bool transparency = false; // use transparent color
moel@345: 		private int delay = 0; // delay in milliseconds
moel@345: 		private int transIndex; // transparent color index
moel@345: 
moel@345: 		private const int MaxStackSize = 4096;
moel@345: 		// max decoder pixel stack size
moel@345: 
moel@345: 		// LZW decoder working arrays
moel@345: 		private short[] prefix;
moel@345: 		private byte[] suffix;
moel@345: 		private byte[] pixelStack;
moel@345: 		private byte[] pixels;
moel@345: 
moel@345: 		private ArrayList frames; // frames read from current file
moel@345: 		private int frameCount;
moel@345: 		private bool _makeTransparent;
moel@345: 
moel@345: 		/**
moel@345: 		 * Gets the number of frames read from file.
moel@345: 		 * @return frame count
moel@345: 		 */
moel@345: 		public int FrameCount
moel@345: 		{
moel@345: 			get
moel@345: 			{
moel@345: 				return frameCount;
moel@345: 			}
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Gets the first (or only) image read.
moel@345: 		 *
moel@345: 		 * @return BufferedImage containing first frame, or null if none.
moel@345: 		 */
moel@345: 		public Image Image
moel@345: 		{
moel@345: 			get
moel@345: 			{
moel@345: 				return GetFrame(0).Image;
moel@345: 			}
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Gets the "Netscape" iteration count, if any.
moel@345: 		 * A count of 0 means repeat indefinitiely.
moel@345: 		 *
moel@345: 		 * @return iteration count if one was specified, else 1.
moel@345: 		 */
moel@345: 		public int LoopCount
moel@345: 		{
moel@345: 			get
moel@345: 			{
moel@345: 				return loopCount;
moel@345: 			}
moel@345: 		}
moel@345: 
moel@345: 		public GifDecoder(Stream stream, bool makeTransparent)
moel@345: 		{
moel@345: 			_makeTransparent = makeTransparent;
moel@345: 			if (Read(stream) != 0)
moel@345: 				throw new InvalidOperationException();
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Creates new frame image from current data (and previous
moel@345: 		 * frames as specified by their disposition codes).
moel@345: 		 */
moel@345: 		private int[] GetPixels(Bitmap bitmap)
moel@345: 		{
moel@345: 			int [] pixels = new int [ 3 * image.Width * image.Height ];
moel@345: 			int count = 0;
moel@345: 			for (int th = 0; th < image.Height; th++)
moel@345: 			{
moel@345: 				for (int tw = 0; tw < image.Width; tw++)
moel@345: 				{
moel@345: 					Color color = bitmap.GetPixel(tw, th);
moel@345: 					pixels[count] = color.R;
moel@345: 					count++;
moel@345: 					pixels[count] = color.G;
moel@345: 					count++;
moel@345: 					pixels[count] = color.B;
moel@345: 					count++;
moel@345: 				}
moel@345: 			}
moel@345: 			return pixels;
moel@345: 		}
moel@345: 
moel@345: 		private void SetPixels(int[] pixels)
moel@345: 		{
moel@345: 			int count = 0;
moel@345: 			for (int th = 0; th < image.Height; th++)
moel@345: 			{
moel@345: 				for (int tw = 0; tw < image.Width; tw++)
moel@345: 				{
moel@345: 					Color color = Color.FromArgb( pixels[count++] );
moel@345: 					bitmap.SetPixel( tw, th, color );
moel@345: 				}
moel@345: 			}
moel@345: 			if (_makeTransparent)
moel@345: 				bitmap.MakeTransparent(bitmap.GetPixel(0, 0));
moel@345: 		}
moel@345: 
moel@345: 		private void SetPixels() 
moel@345: 		{
moel@345: 			// expose destination image's pixels as int array
moel@345: 			//		int[] dest =
moel@345: 			//			(( int ) image.getRaster().getDataBuffer()).getData();
moel@345: 			int[] dest = GetPixels( bitmap );
moel@345: 
moel@345: 			// fill in starting image contents based on last image's dispose code
moel@345: 			if (lastDispose > 0) 
moel@345: 			{
moel@345: 				if (lastDispose == 3) 
moel@345: 				{
moel@345: 					// use image before last
moel@345: 					int n = frameCount - 2;
moel@345: 					if (n > 0) 
moel@345: 					{
moel@345: 						lastImage = GetFrame(n - 1).Image;
moel@345: 					} 
moel@345: 					else 
moel@345: 					{
moel@345: 						lastImage = null;
moel@345: 					}
moel@345: 				}
moel@345: 
moel@345: 				if (lastImage != null) 
moel@345: 				{
moel@345: 					//				int[] prev =
moel@345: 					//					((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();
moel@345: 					int[] prev = GetPixels( new Bitmap( lastImage ) );
moel@345: 					Array.Copy(prev, 0, dest, 0, width * height);
moel@345: 					// copy pixels
moel@345: 
moel@345: 					if (lastDispose == 2) 
moel@345: 					{
moel@345: 						// fill last image rect area with background color
moel@345: 						Graphics g = Graphics.FromImage( image );
moel@345: 						Color c = Color.Empty;
moel@345: 						if (transparency) 
moel@345: 						{
moel@345: 							c = Color.FromArgb( 0, 0, 0, 0 ); 	// assume background is transparent
moel@345: 						} 
moel@345: 						else 
moel@345: 						{
moel@345: 							c = Color.FromArgb( lastBgColor ) ;
moel@345: 							//						c = new Color(lastBgColor); // use given background color
moel@345: 						}
moel@345: 						Brush brush = new SolidBrush( c );
moel@345: 						g.FillRectangle( brush, lastRect );
moel@345: 						brush.Dispose();
moel@345: 						g.Dispose();
moel@345: 					}
moel@345: 				}
moel@345: 			}
moel@345: 
moel@345: 			// copy each source line to the appropriate place in the destination
moel@345: 			int pass = 1;
moel@345: 			int inc = 8;
moel@345: 			int iline = 0;
moel@345: 			for (int i = 0; i < ih; i++) 
moel@345: 			{
moel@345: 				int line = i;
moel@345: 				if (interlace) 
moel@345: 				{
moel@345: 					if (iline >= ih) 
moel@345: 					{
moel@345: 						pass++;
moel@345: 						switch (pass) 
moel@345: 						{
moel@345: 							case 2 :
moel@345: 								iline = 4;
moel@345: 								break;
moel@345: 							case 3 :
moel@345: 								iline = 2;
moel@345: 								inc = 4;
moel@345: 								break;
moel@345: 							case 4 :
moel@345: 								iline = 1;
moel@345: 								inc = 2;
moel@345: 								break;
moel@345: 						}
moel@345: 					}
moel@345: 					line = iline;
moel@345: 					iline += inc;
moel@345: 				}
moel@345: 				line += iy;
moel@345: 				if (line < height) 
moel@345: 				{
moel@345: 					int k = line * width;
moel@345: 					int dx = k + ix; // start of line in dest
moel@345: 					int dlim = dx + iw; // end of dest line
moel@345: 					if ((k + width) < dlim) 
moel@345: 					{
moel@345: 						dlim = k + width; // past dest edge
moel@345: 					}
moel@345: 					int sx = i * iw; // start of line in source
moel@345: 					while (dx < dlim) 
moel@345: 					{
moel@345: 						// map color and insert in destination
moel@345: 						int index = ((int) pixels[sx++]) & 0xff;
moel@345: 						int c = act[index];
moel@345: 						if (c != 0) 
moel@345: 						{
moel@345: 							dest[dx] = c;
moel@345: 						}
moel@345: 						dx++;
moel@345: 					}
moel@345: 				}
moel@345: 			}
moel@345: 			SetPixels( dest );
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Gets the image contents of frame n.
moel@345: 		 *
moel@345: 		 * @return BufferedImage representation of frame.
moel@345: 		 */
moel@345: 		public GifFrame GetFrame(int n) 
moel@345: 		{
moel@345: 			if ((n >= 0) && (n < frameCount))
moel@345: 				return (GifFrame)frames[n];
moel@345: 			else
moel@345: 				throw new ArgumentOutOfRangeException();
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Gets image size.
moel@345: 		 *
moel@345: 		 * @return GIF image dimensions
moel@345: 		 */
moel@345: 		public Size FrameSize
moel@345: 		{
moel@345: 			get
moel@345: 			{
moel@345: 				return new Size(width, height);
moel@345: 			}
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Reads GIF image from stream
moel@345: 		 *
moel@345: 		 * @param BufferedInputStream containing GIF file.
moel@345: 		 * @return read status code (0 = no errors)
moel@345: 		 */
moel@345: 		private int Read( Stream inStream ) 
moel@345: 		{
moel@345: 			Init();
moel@345: 			if ( inStream != null) 
moel@345: 			{
moel@345: 				this.inStream = inStream;
moel@345: 				ReadHeader();
moel@345: 				if (!Error()) 
moel@345: 				{
moel@345: 					ReadContents();
moel@345: 					if (frameCount < 0) 
moel@345: 					{
moel@345: 						status = StatusFormatError;
moel@345: 					}
moel@345: 				}
moel@345: 				inStream.Close();
moel@345: 			} 
moel@345: 			else 
moel@345: 			{
moel@345: 				status = StatusOpenError;
moel@345: 			}
moel@345: 			return status;
moel@345: 		}
moel@345: 
moel@345: 
moel@345: 		/**
moel@345: 		 * Decodes LZW image data into pixel array.
moel@345: 		 * Adapted from John Cristy's ImageMagick.
moel@345: 		 */
moel@345: 		private void DecodeImageData() 
moel@345: 		{
moel@345: 			int NullCode = -1;
moel@345: 			int npix = iw * ih;
moel@345: 			int available, 
moel@345: 				clear,
moel@345: 				code_mask,
moel@345: 				code_size,
moel@345: 				end_of_information,
moel@345: 				in_code,
moel@345: 				old_code,
moel@345: 				bits,
moel@345: 				code,
moel@345: 				count,
moel@345: 				i,
moel@345: 				datum,
moel@345: 				data_size,
moel@345: 				first,
moel@345: 				top,
moel@345: 				bi,
moel@345: 				pi;
moel@345: 
moel@345: 			if ((pixels == null) || (pixels.Length < npix)) 
moel@345: 			{
moel@345: 				pixels = new byte[npix]; // allocate new pixel array
moel@345: 			}
moel@345: 			if (prefix == null) prefix = new short[MaxStackSize];
moel@345: 			if (suffix == null) suffix = new byte[MaxStackSize];
moel@345: 			if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1];
moel@345: 
moel@345: 			//  Initialize GIF data stream decoder.
moel@345: 
moel@345: 			data_size = Read();
moel@345: 			clear = 1 << data_size;
moel@345: 			end_of_information = clear + 1;
moel@345: 			available = clear + 2;
moel@345: 			old_code = NullCode;
moel@345: 			code_size = data_size + 1;
moel@345: 			code_mask = (1 << code_size) - 1;
moel@345: 			for (code = 0; code < clear; code++) 
moel@345: 			{
moel@345: 				prefix[code] = 0;
moel@345: 				suffix[code] = (byte) code;
moel@345: 			}
moel@345: 
moel@345: 			//  Decode GIF pixel stream.
moel@345: 
moel@345: 			datum = bits = count = first = top = pi = bi = 0;
moel@345: 
moel@345: 			for (i = 0; i < npix;) 
moel@345: 			{
moel@345: 				if (top == 0) 
moel@345: 				{
moel@345: 					if (bits < code_size) 
moel@345: 					{
moel@345: 						//  Load bytes until there are enough bits for a code.
moel@345: 						if (count == 0) 
moel@345: 						{
moel@345: 							// Read a new data block.
moel@345: 							count = ReadBlock();
moel@345: 							if (count <= 0)
moel@345: 								break;
moel@345: 							bi = 0;
moel@345: 						}
moel@345: 						datum += (((int) block[bi]) & 0xff) << bits;
moel@345: 						bits += 8;
moel@345: 						bi++;
moel@345: 						count--;
moel@345: 						continue;
moel@345: 					}
moel@345: 
moel@345: 					//  Get the next code.
moel@345: 
moel@345: 					code = datum & code_mask;
moel@345: 					datum >>= code_size;
moel@345: 					bits -= code_size;
moel@345: 
moel@345: 					//  Interpret the code
moel@345: 
moel@345: 					if ((code > available) || (code == end_of_information))
moel@345: 						break;
moel@345: 					if (code == clear) 
moel@345: 					{
moel@345: 						//  Reset decoder.
moel@345: 						code_size = data_size + 1;
moel@345: 						code_mask = (1 << code_size) - 1;
moel@345: 						available = clear + 2;
moel@345: 						old_code = NullCode;
moel@345: 						continue;
moel@345: 					}
moel@345: 					if (old_code == NullCode) 
moel@345: 					{
moel@345: 						pixelStack[top++] = suffix[code];
moel@345: 						old_code = code;
moel@345: 						first = code;
moel@345: 						continue;
moel@345: 					}
moel@345: 					in_code = code;
moel@345: 					if (code == available) 
moel@345: 					{
moel@345: 						pixelStack[top++] = (byte) first;
moel@345: 						code = old_code;
moel@345: 					}
moel@345: 					while (code > clear) 
moel@345: 					{
moel@345: 						pixelStack[top++] = suffix[code];
moel@345: 						code = prefix[code];
moel@345: 					}
moel@345: 					first = ((int) suffix[code]) & 0xff;
moel@345: 
moel@345: 					//  Add a new string to the string table,
moel@345: 
moel@345: 					if (available >= MaxStackSize)
moel@345: 						break;
moel@345: 					pixelStack[top++] = (byte) first;
moel@345: 					prefix[available] = (short) old_code;
moel@345: 					suffix[available] = (byte) first;
moel@345: 					available++;
moel@345: 					if (((available & code_mask) == 0)
moel@345: 						&& (available < MaxStackSize)) 
moel@345: 					{
moel@345: 						code_size++;
moel@345: 						code_mask += available;
moel@345: 					}
moel@345: 					old_code = in_code;
moel@345: 				}
moel@345: 
moel@345: 				//  Pop a pixel off the pixel stack.
moel@345: 
moel@345: 				top--;
moel@345: 				pixels[pi++] = pixelStack[top];
moel@345: 				i++;
moel@345: 			}
moel@345: 
moel@345: 			for (i = pi; i < npix; i++) 
moel@345: 			{
moel@345: 				pixels[i] = 0; // clear missing pixels
moel@345: 			}
moel@345: 
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Returns true if an error was encountered during reading/decoding
moel@345: 		 */
moel@345: 		private bool Error() 
moel@345: 		{
moel@345: 			return status != StatusOK;
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Initializes or re-initializes reader
moel@345: 		 */
moel@345: 		private void Init() 
moel@345: 		{
moel@345: 			status = StatusOK;
moel@345: 			frameCount = 0;
moel@345: 			frames = new ArrayList();
moel@345: 			gct = null;
moel@345: 			lct = null;
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Reads a single byte from the input stream.
moel@345: 		 */
moel@345: 		private int Read() 
moel@345: 		{
moel@345: 			int curByte = 0;
moel@345: 			try 
moel@345: 			{
moel@345: 				curByte = inStream.ReadByte();
moel@345: 			} 
moel@345: 			catch (IOException) 
moel@345: 			{
moel@345: 				status = StatusFormatError;
moel@345: 			}
moel@345: 			return curByte;
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Reads next variable length block from input.
moel@345: 		 *
moel@345: 		 * @return number of bytes stored in "buffer"
moel@345: 		 */
moel@345: 		private int ReadBlock() 
moel@345: 		{
moel@345: 			blockSize = Read();
moel@345: 			int n = 0;
moel@345: 			if (blockSize > 0) 
moel@345: 			{
moel@345: 				try 
moel@345: 				{
moel@345: 					int count = 0;
moel@345: 					while (n < blockSize) 
moel@345: 					{
moel@345: 						count = inStream.Read(block, n, blockSize - n);
moel@345: 						if (count == -1) 
moel@345: 							break;
moel@345: 						n += count;
moel@345: 					}
moel@345: 				} 
moel@345: 				catch (IOException) 
moel@345: 				{
moel@345: 				}
moel@345: 
moel@345: 				if (n < blockSize) 
moel@345: 				{
moel@345: 					status = StatusFormatError;
moel@345: 				}
moel@345: 			}
moel@345: 			return n;
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Reads color table as 256 RGB integer values
moel@345: 		 *
moel@345: 		 * @param ncolors int number of colors to read
moel@345: 		 * @return int array containing 256 colors (packed ARGB with full alpha)
moel@345: 		 */
moel@345: 		private int[] ReadColorTable(int ncolors) 
moel@345: 		{
moel@345: 			int nbytes = 3 * ncolors;
moel@345: 			int[] tab = null;
moel@345: 			byte[] c = new byte[nbytes];
moel@345: 			int n = 0;
moel@345: 			try 
moel@345: 			{
moel@345: 				n = inStream.Read(c, 0, c.Length );
moel@345: 			} 
moel@345: 			catch (IOException) 
moel@345: 			{
moel@345: 			}
moel@345: 			if (n < nbytes) 
moel@345: 			{
moel@345: 				status = StatusFormatError;
moel@345: 			} 
moel@345: 			else 
moel@345: 			{
moel@345: 				tab = new int[256]; // max size to avoid bounds checks
moel@345: 				int i = 0;
moel@345: 				int j = 0;
moel@345: 				while (i < ncolors) 
moel@345: 				{
moel@345: 					int r = ((int) c[j++]) & 0xff;
moel@345: 					int g = ((int) c[j++]) & 0xff;
moel@345: 					int b = ((int) c[j++]) & 0xff;
moel@345: 					tab[i++] = ( int ) ( 0xff000000 | (r << 16) | (g << 8) | b );
moel@345: 				}
moel@345: 			}
moel@345: 			return tab;
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Main file parser.  Reads GIF content blocks.
moel@345: 		 */
moel@345: 		private void ReadContents() 
moel@345: 		{
moel@345: 			// read GIF file content blocks
moel@345: 			bool done = false;
moel@345: 			while (!(done || Error())) 
moel@345: 			{
moel@345: 				int code = Read();
moel@345: 				switch (code) 
moel@345: 				{
moel@345: 
moel@345: 					case 0x2C : // image separator
moel@345: 						ReadImage();
moel@345: 						break;
moel@345: 
moel@345: 					case 0x21 : // extension
moel@345: 						code = Read();
moel@345: 					switch (code) 
moel@345: 					{
moel@345: 						case 0xf9 : // graphics control extension
moel@345: 							ReadGraphicControlExt();
moel@345: 							break;
moel@345: 
moel@345: 						case 0xff : // application extension
moel@345: 							ReadBlock();
moel@345: 							String app = "";
moel@345: 							for (int i = 0; i < 11; i++) 
moel@345: 							{
moel@345: 								app += (char) block[i];
moel@345: 							}
moel@345: 							if (app.Equals("NETSCAPE2.0")) 
moel@345: 							{
moel@345: 								ReadNetscapeExt();
moel@345: 							}
moel@345: 							else
moel@345: 								Skip(); // don't care
moel@345: 							break;
moel@345: 
moel@345: 						default : // uninteresting extension
moel@345: 							Skip();
moel@345: 							break;
moel@345: 					}
moel@345: 						break;
moel@345: 
moel@345: 					case 0x3b : // terminator
moel@345: 						done = true;
moel@345: 						break;
moel@345: 
moel@345: 					case 0x00 : // bad byte, but keep going and see what happens
moel@345: 						break;
moel@345: 
moel@345: 					default :
moel@345: 						status = StatusFormatError;
moel@345: 						break;
moel@345: 				}
moel@345: 			}
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Reads Graphics Control Extension values
moel@345: 		 */
moel@345: 		private void ReadGraphicControlExt() 
moel@345: 		{
moel@345: 			Read(); // block size
moel@345: 			int packed = Read(); // packed fields
moel@345: 			dispose = (packed & 0x1c) >> 2; // disposal method
moel@345: 			if (dispose == 0) 
moel@345: 			{
moel@345: 				dispose = 1; // elect to keep old image if discretionary
moel@345: 			}
moel@345: 			transparency = (packed & 1) != 0;
moel@345: 			delay = ReadShort() * 10; // delay in milliseconds
moel@345: 			transIndex = Read(); // transparent color index
moel@345: 			Read(); // block terminator
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Reads GIF file header information.
moel@345: 		 */
moel@345: 		private void ReadHeader() 
moel@345: 		{
moel@345: 			String id = "";
moel@345: 			for (int i = 0; i < 6; i++) 
moel@345: 			{
moel@345: 				id += (char) Read();
moel@345: 			}
moel@345: 			if (!id.StartsWith("GIF")) 
moel@345: 			{
moel@345: 				status = StatusFormatError;
moel@345: 				return;
moel@345: 			}
moel@345: 
moel@345: 			ReadLSD();
moel@345: 			if (gctFlag && !Error()) 
moel@345: 			{
moel@345: 				gct = ReadColorTable(gctSize);
moel@345: 				bgColor = gct[bgIndex];
moel@345: 			}
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Reads next frame image
moel@345: 		 */
moel@345: 		private void ReadImage() 
moel@345: 		{
moel@345: 			ix = ReadShort(); // (sub)image position & size
moel@345: 			iy = ReadShort();
moel@345: 			iw = ReadShort();
moel@345: 			ih = ReadShort();
moel@345: 
moel@345: 			int packed = Read();
moel@345: 			lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
moel@345: 			interlace = (packed & 0x40) != 0; // 2 - interlace flag
moel@345: 			// 3 - sort flag
moel@345: 			// 4-5 - reserved
moel@345: 			lctSize = 2 << (packed & 7); // 6-8 - local color table size
moel@345: 
moel@345: 			if (lctFlag) 
moel@345: 			{
moel@345: 				lct = ReadColorTable(lctSize); // read table
moel@345: 				act = lct; // make local table active
moel@345: 			} 
moel@345: 			else 
moel@345: 			{
moel@345: 				act = gct; // make global table active
moel@345: 				if (bgIndex == transIndex)
moel@345: 					bgColor = 0;
moel@345: 			}
moel@345: 			int save = 0;
moel@345: 			if (transparency) 
moel@345: 			{
moel@345: 				save = act[transIndex];
moel@345: 				act[transIndex] = 0; // set transparent color if specified
moel@345: 			}
moel@345: 
moel@345: 			if (act == null) 
moel@345: 			{
moel@345: 				status = StatusFormatError; // no color table defined
moel@345: 			}
moel@345: 
moel@345: 			if (Error()) return;
moel@345: 
moel@345: 			DecodeImageData(); // decode pixel data
moel@345: 			Skip();
moel@345: 
moel@345: 			if (Error()) return;
moel@345: 
moel@345: 			frameCount++;
moel@345: 
moel@345: 			// create new image to receive frame data
moel@345: 			//		image =
moel@345: 			//			new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
moel@345: 
moel@345: 			bitmap = new Bitmap( width, height );
moel@345: 			image = bitmap;
moel@345: 			SetPixels(); // transfer pixel data to image
moel@345: 
moel@345: 			frames.Add(new GifFrame(bitmap, delay)); // add image to frame list
moel@345: 
moel@345: 			if (transparency) 
moel@345: 			{
moel@345: 				act[transIndex] = save;
moel@345: 			}
moel@345: 			ResetFrame();
moel@345: 
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Reads Logical Screen Descriptor
moel@345: 		 */
moel@345: 		private void ReadLSD() 
moel@345: 		{
moel@345: 
moel@345: 			// logical screen size
moel@345: 			width = ReadShort();
moel@345: 			height = ReadShort();
moel@345: 
moel@345: 			// packed fields
moel@345: 			int packed = Read();
moel@345: 			gctFlag = (packed & 0x80) != 0; // 1   : global color table flag
moel@345: 			// 2-4 : color resolution
moel@345: 			// 5   : gct sort flag
moel@345: 			gctSize = 2 << (packed & 7); // 6-8 : gct size
moel@345: 
moel@345: 			bgIndex = Read(); // background color index
moel@345: 			pixelAspect = Read(); // pixel aspect ratio
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Reads Netscape extenstion to obtain iteration count
moel@345: 		 */
moel@345: 		private void ReadNetscapeExt() 
moel@345: 		{
moel@345: 			do 
moel@345: 			{
moel@345: 				ReadBlock();
moel@345: 				if (block[0] == 1) 
moel@345: 				{
moel@345: 					// loop count sub-block
moel@345: 					int b1 = ((int) block[1]) & 0xff;
moel@345: 					int b2 = ((int) block[2]) & 0xff;
moel@345: 					loopCount = (b2 << 8) | b1;
moel@345: 				}
moel@345: 			} while ((blockSize > 0) && !Error());
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Reads next 16-bit value, LSB first
moel@345: 		 */
moel@345: 		private int ReadShort() 
moel@345: 		{
moel@345: 			// read 16-bit value, LSB first
moel@345: 			return Read() | (Read() << 8);
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Resets frame state for reading next image.
moel@345: 		 */
moel@345: 		private void ResetFrame() 
moel@345: 		{
moel@345: 			lastDispose = dispose;
moel@345: 			lastRect = new Rectangle(ix, iy, iw, ih);
moel@345: 			lastImage = image;
moel@345: 			lastBgColor = bgColor;
moel@345: 			//		int dispose = 0;
moel@345: 			lct = null;
moel@345: 		}
moel@345: 
moel@345: 		/**
moel@345: 		 * Skips variable length blocks up to and including
moel@345: 		 * next zero length block.
moel@345: 		 */
moel@345: 		private void Skip() 
moel@345: 		{
moel@345: 			do 
moel@345: 			{
moel@345: 				ReadBlock();
moel@345: 			} while ((blockSize > 0) && !Error());
moel@345: 		}
moel@345: 	}
moel@345: }