External/Aga.Controls/GifDecoder.cs
author sl
Thu, 01 Jan 2015 23:35:49 +0100
changeset 405 5715aefd2bcc
permissions -rw-r--r--
SharpDisplay: Migrating to new robust client scheme.
     1 #pragma warning disable 675 // Bitwise-or operator used on a sign-extended operand
     2 
     3 #region Java Info
     4 /**
     5  * Class GifDecoder - Decodes a GIF file into one or more frames.
     6  * <br><pre>
     7  * Example:
     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
    15  *    }
    16  * </pre>
    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.
    20  *
    21  * @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy's ImageMagick.
    22  * @version 1.03 November 2003
    23  *
    24  */
    25 #endregion
    26 
    27 using System;
    28 using System.Collections;
    29 using System.Drawing;
    30 using System.Drawing.Imaging;
    31 using System.IO;
    32 
    33 namespace Aga.Controls
    34 {
    35 	public class GifFrame
    36 	{
    37 		private Image _image;
    38 		public Image Image
    39 		{
    40 			get { return _image; }
    41 		}
    42 
    43 		private int _delay;
    44 		public int Delay
    45 		{
    46 			get { return _delay; }
    47 		}
    48 
    49 		public GifFrame(Image im, int del)
    50 		{
    51 			_image = im;
    52 			_delay = del;
    53 		}
    54 	}
    55 
    56 	public class GifDecoder 
    57 	{
    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.
    61 
    62 		private Stream inStream;
    63 		private int status;
    64 
    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
    70 
    71 		private int[] gct; // global color table
    72 		private int[] lct; // local color table
    73 		private int[] act; // active color table
    74 
    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
    79 
    80 		private bool lctFlag; // local color table flag
    81 		private bool interlace; // interlace flag
    82 		private int lctSize; // local color table size
    83 
    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
    89 
    90 		private byte[] block = new byte[256]; // current data block
    91 		private int blockSize = 0; // block size
    92 
    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
   100 
   101 		private const int MaxStackSize = 4096;
   102 		// max decoder pixel stack size
   103 
   104 		// LZW decoder working arrays
   105 		private short[] prefix;
   106 		private byte[] suffix;
   107 		private byte[] pixelStack;
   108 		private byte[] pixels;
   109 
   110 		private ArrayList frames; // frames read from current file
   111 		private int frameCount;
   112 		private bool _makeTransparent;
   113 
   114 		/**
   115 		 * Gets the number of frames read from file.
   116 		 * @return frame count
   117 		 */
   118 		public int FrameCount
   119 		{
   120 			get
   121 			{
   122 				return frameCount;
   123 			}
   124 		}
   125 
   126 		/**
   127 		 * Gets the first (or only) image read.
   128 		 *
   129 		 * @return BufferedImage containing first frame, or null if none.
   130 		 */
   131 		public Image Image
   132 		{
   133 			get
   134 			{
   135 				return GetFrame(0).Image;
   136 			}
   137 		}
   138 
   139 		/**
   140 		 * Gets the "Netscape" iteration count, if any.
   141 		 * A count of 0 means repeat indefinitiely.
   142 		 *
   143 		 * @return iteration count if one was specified, else 1.
   144 		 */
   145 		public int LoopCount
   146 		{
   147 			get
   148 			{
   149 				return loopCount;
   150 			}
   151 		}
   152 
   153 		public GifDecoder(Stream stream, bool makeTransparent)
   154 		{
   155 			_makeTransparent = makeTransparent;
   156 			if (Read(stream) != 0)
   157 				throw new InvalidOperationException();
   158 		}
   159 
   160 		/**
   161 		 * Creates new frame image from current data (and previous
   162 		 * frames as specified by their disposition codes).
   163 		 */
   164 		private int[] GetPixels(Bitmap bitmap)
   165 		{
   166 			int [] pixels = new int [ 3 * image.Width * image.Height ];
   167 			int count = 0;
   168 			for (int th = 0; th < image.Height; th++)
   169 			{
   170 				for (int tw = 0; tw < image.Width; tw++)
   171 				{
   172 					Color color = bitmap.GetPixel(tw, th);
   173 					pixels[count] = color.R;
   174 					count++;
   175 					pixels[count] = color.G;
   176 					count++;
   177 					pixels[count] = color.B;
   178 					count++;
   179 				}
   180 			}
   181 			return pixels;
   182 		}
   183 
   184 		private void SetPixels(int[] pixels)
   185 		{
   186 			int count = 0;
   187 			for (int th = 0; th < image.Height; th++)
   188 			{
   189 				for (int tw = 0; tw < image.Width; tw++)
   190 				{
   191 					Color color = Color.FromArgb( pixels[count++] );
   192 					bitmap.SetPixel( tw, th, color );
   193 				}
   194 			}
   195 			if (_makeTransparent)
   196 				bitmap.MakeTransparent(bitmap.GetPixel(0, 0));
   197 		}
   198 
   199 		private void SetPixels() 
   200 		{
   201 			// expose destination image's pixels as int array
   202 			//		int[] dest =
   203 			//			(( int ) image.getRaster().getDataBuffer()).getData();
   204 			int[] dest = GetPixels( bitmap );
   205 
   206 			// fill in starting image contents based on last image's dispose code
   207 			if (lastDispose > 0) 
   208 			{
   209 				if (lastDispose == 3) 
   210 				{
   211 					// use image before last
   212 					int n = frameCount - 2;
   213 					if (n > 0) 
   214 					{
   215 						lastImage = GetFrame(n - 1).Image;
   216 					} 
   217 					else 
   218 					{
   219 						lastImage = null;
   220 					}
   221 				}
   222 
   223 				if (lastImage != null) 
   224 				{
   225 					//				int[] prev =
   226 					//					((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();
   227 					int[] prev = GetPixels( new Bitmap( lastImage ) );
   228 					Array.Copy(prev, 0, dest, 0, width * height);
   229 					// copy pixels
   230 
   231 					if (lastDispose == 2) 
   232 					{
   233 						// fill last image rect area with background color
   234 						Graphics g = Graphics.FromImage( image );
   235 						Color c = Color.Empty;
   236 						if (transparency) 
   237 						{
   238 							c = Color.FromArgb( 0, 0, 0, 0 ); 	// assume background is transparent
   239 						} 
   240 						else 
   241 						{
   242 							c = Color.FromArgb( lastBgColor ) ;
   243 							//						c = new Color(lastBgColor); // use given background color
   244 						}
   245 						Brush brush = new SolidBrush( c );
   246 						g.FillRectangle( brush, lastRect );
   247 						brush.Dispose();
   248 						g.Dispose();
   249 					}
   250 				}
   251 			}
   252 
   253 			// copy each source line to the appropriate place in the destination
   254 			int pass = 1;
   255 			int inc = 8;
   256 			int iline = 0;
   257 			for (int i = 0; i < ih; i++) 
   258 			{
   259 				int line = i;
   260 				if (interlace) 
   261 				{
   262 					if (iline >= ih) 
   263 					{
   264 						pass++;
   265 						switch (pass) 
   266 						{
   267 							case 2 :
   268 								iline = 4;
   269 								break;
   270 							case 3 :
   271 								iline = 2;
   272 								inc = 4;
   273 								break;
   274 							case 4 :
   275 								iline = 1;
   276 								inc = 2;
   277 								break;
   278 						}
   279 					}
   280 					line = iline;
   281 					iline += inc;
   282 				}
   283 				line += iy;
   284 				if (line < height) 
   285 				{
   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) 
   290 					{
   291 						dlim = k + width; // past dest edge
   292 					}
   293 					int sx = i * iw; // start of line in source
   294 					while (dx < dlim) 
   295 					{
   296 						// map color and insert in destination
   297 						int index = ((int) pixels[sx++]) & 0xff;
   298 						int c = act[index];
   299 						if (c != 0) 
   300 						{
   301 							dest[dx] = c;
   302 						}
   303 						dx++;
   304 					}
   305 				}
   306 			}
   307 			SetPixels( dest );
   308 		}
   309 
   310 		/**
   311 		 * Gets the image contents of frame n.
   312 		 *
   313 		 * @return BufferedImage representation of frame.
   314 		 */
   315 		public GifFrame GetFrame(int n) 
   316 		{
   317 			if ((n >= 0) && (n < frameCount))
   318 				return (GifFrame)frames[n];
   319 			else
   320 				throw new ArgumentOutOfRangeException();
   321 		}
   322 
   323 		/**
   324 		 * Gets image size.
   325 		 *
   326 		 * @return GIF image dimensions
   327 		 */
   328 		public Size FrameSize
   329 		{
   330 			get
   331 			{
   332 				return new Size(width, height);
   333 			}
   334 		}
   335 
   336 		/**
   337 		 * Reads GIF image from stream
   338 		 *
   339 		 * @param BufferedInputStream containing GIF file.
   340 		 * @return read status code (0 = no errors)
   341 		 */
   342 		private int Read( Stream inStream ) 
   343 		{
   344 			Init();
   345 			if ( inStream != null) 
   346 			{
   347 				this.inStream = inStream;
   348 				ReadHeader();
   349 				if (!Error()) 
   350 				{
   351 					ReadContents();
   352 					if (frameCount < 0) 
   353 					{
   354 						status = StatusFormatError;
   355 					}
   356 				}
   357 				inStream.Close();
   358 			} 
   359 			else 
   360 			{
   361 				status = StatusOpenError;
   362 			}
   363 			return status;
   364 		}
   365 
   366 
   367 		/**
   368 		 * Decodes LZW image data into pixel array.
   369 		 * Adapted from John Cristy's ImageMagick.
   370 		 */
   371 		private void DecodeImageData() 
   372 		{
   373 			int NullCode = -1;
   374 			int npix = iw * ih;
   375 			int available, 
   376 				clear,
   377 				code_mask,
   378 				code_size,
   379 				end_of_information,
   380 				in_code,
   381 				old_code,
   382 				bits,
   383 				code,
   384 				count,
   385 				i,
   386 				datum,
   387 				data_size,
   388 				first,
   389 				top,
   390 				bi,
   391 				pi;
   392 
   393 			if ((pixels == null) || (pixels.Length < npix)) 
   394 			{
   395 				pixels = new byte[npix]; // allocate new pixel array
   396 			}
   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];
   400 
   401 			//  Initialize GIF data stream decoder.
   402 
   403 			data_size = Read();
   404 			clear = 1 << data_size;
   405 			end_of_information = clear + 1;
   406 			available = clear + 2;
   407 			old_code = NullCode;
   408 			code_size = data_size + 1;
   409 			code_mask = (1 << code_size) - 1;
   410 			for (code = 0; code < clear; code++) 
   411 			{
   412 				prefix[code] = 0;
   413 				suffix[code] = (byte) code;
   414 			}
   415 
   416 			//  Decode GIF pixel stream.
   417 
   418 			datum = bits = count = first = top = pi = bi = 0;
   419 
   420 			for (i = 0; i < npix;) 
   421 			{
   422 				if (top == 0) 
   423 				{
   424 					if (bits < code_size) 
   425 					{
   426 						//  Load bytes until there are enough bits for a code.
   427 						if (count == 0) 
   428 						{
   429 							// Read a new data block.
   430 							count = ReadBlock();
   431 							if (count <= 0)
   432 								break;
   433 							bi = 0;
   434 						}
   435 						datum += (((int) block[bi]) & 0xff) << bits;
   436 						bits += 8;
   437 						bi++;
   438 						count--;
   439 						continue;
   440 					}
   441 
   442 					//  Get the next code.
   443 
   444 					code = datum & code_mask;
   445 					datum >>= code_size;
   446 					bits -= code_size;
   447 
   448 					//  Interpret the code
   449 
   450 					if ((code > available) || (code == end_of_information))
   451 						break;
   452 					if (code == clear) 
   453 					{
   454 						//  Reset decoder.
   455 						code_size = data_size + 1;
   456 						code_mask = (1 << code_size) - 1;
   457 						available = clear + 2;
   458 						old_code = NullCode;
   459 						continue;
   460 					}
   461 					if (old_code == NullCode) 
   462 					{
   463 						pixelStack[top++] = suffix[code];
   464 						old_code = code;
   465 						first = code;
   466 						continue;
   467 					}
   468 					in_code = code;
   469 					if (code == available) 
   470 					{
   471 						pixelStack[top++] = (byte) first;
   472 						code = old_code;
   473 					}
   474 					while (code > clear) 
   475 					{
   476 						pixelStack[top++] = suffix[code];
   477 						code = prefix[code];
   478 					}
   479 					first = ((int) suffix[code]) & 0xff;
   480 
   481 					//  Add a new string to the string table,
   482 
   483 					if (available >= MaxStackSize)
   484 						break;
   485 					pixelStack[top++] = (byte) first;
   486 					prefix[available] = (short) old_code;
   487 					suffix[available] = (byte) first;
   488 					available++;
   489 					if (((available & code_mask) == 0)
   490 						&& (available < MaxStackSize)) 
   491 					{
   492 						code_size++;
   493 						code_mask += available;
   494 					}
   495 					old_code = in_code;
   496 				}
   497 
   498 				//  Pop a pixel off the pixel stack.
   499 
   500 				top--;
   501 				pixels[pi++] = pixelStack[top];
   502 				i++;
   503 			}
   504 
   505 			for (i = pi; i < npix; i++) 
   506 			{
   507 				pixels[i] = 0; // clear missing pixels
   508 			}
   509 
   510 		}
   511 
   512 		/**
   513 		 * Returns true if an error was encountered during reading/decoding
   514 		 */
   515 		private bool Error() 
   516 		{
   517 			return status != StatusOK;
   518 		}
   519 
   520 		/**
   521 		 * Initializes or re-initializes reader
   522 		 */
   523 		private void Init() 
   524 		{
   525 			status = StatusOK;
   526 			frameCount = 0;
   527 			frames = new ArrayList();
   528 			gct = null;
   529 			lct = null;
   530 		}
   531 
   532 		/**
   533 		 * Reads a single byte from the input stream.
   534 		 */
   535 		private int Read() 
   536 		{
   537 			int curByte = 0;
   538 			try 
   539 			{
   540 				curByte = inStream.ReadByte();
   541 			} 
   542 			catch (IOException) 
   543 			{
   544 				status = StatusFormatError;
   545 			}
   546 			return curByte;
   547 		}
   548 
   549 		/**
   550 		 * Reads next variable length block from input.
   551 		 *
   552 		 * @return number of bytes stored in "buffer"
   553 		 */
   554 		private int ReadBlock() 
   555 		{
   556 			blockSize = Read();
   557 			int n = 0;
   558 			if (blockSize > 0) 
   559 			{
   560 				try 
   561 				{
   562 					int count = 0;
   563 					while (n < blockSize) 
   564 					{
   565 						count = inStream.Read(block, n, blockSize - n);
   566 						if (count == -1) 
   567 							break;
   568 						n += count;
   569 					}
   570 				} 
   571 				catch (IOException) 
   572 				{
   573 				}
   574 
   575 				if (n < blockSize) 
   576 				{
   577 					status = StatusFormatError;
   578 				}
   579 			}
   580 			return n;
   581 		}
   582 
   583 		/**
   584 		 * Reads color table as 256 RGB integer values
   585 		 *
   586 		 * @param ncolors int number of colors to read
   587 		 * @return int array containing 256 colors (packed ARGB with full alpha)
   588 		 */
   589 		private int[] ReadColorTable(int ncolors) 
   590 		{
   591 			int nbytes = 3 * ncolors;
   592 			int[] tab = null;
   593 			byte[] c = new byte[nbytes];
   594 			int n = 0;
   595 			try 
   596 			{
   597 				n = inStream.Read(c, 0, c.Length );
   598 			} 
   599 			catch (IOException) 
   600 			{
   601 			}
   602 			if (n < nbytes) 
   603 			{
   604 				status = StatusFormatError;
   605 			} 
   606 			else 
   607 			{
   608 				tab = new int[256]; // max size to avoid bounds checks
   609 				int i = 0;
   610 				int j = 0;
   611 				while (i < ncolors) 
   612 				{
   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 );
   617 				}
   618 			}
   619 			return tab;
   620 		}
   621 
   622 		/**
   623 		 * Main file parser.  Reads GIF content blocks.
   624 		 */
   625 		private void ReadContents() 
   626 		{
   627 			// read GIF file content blocks
   628 			bool done = false;
   629 			while (!(done || Error())) 
   630 			{
   631 				int code = Read();
   632 				switch (code) 
   633 				{
   634 
   635 					case 0x2C : // image separator
   636 						ReadImage();
   637 						break;
   638 
   639 					case 0x21 : // extension
   640 						code = Read();
   641 					switch (code) 
   642 					{
   643 						case 0xf9 : // graphics control extension
   644 							ReadGraphicControlExt();
   645 							break;
   646 
   647 						case 0xff : // application extension
   648 							ReadBlock();
   649 							String app = "";
   650 							for (int i = 0; i < 11; i++) 
   651 							{
   652 								app += (char) block[i];
   653 							}
   654 							if (app.Equals("NETSCAPE2.0")) 
   655 							{
   656 								ReadNetscapeExt();
   657 							}
   658 							else
   659 								Skip(); // don't care
   660 							break;
   661 
   662 						default : // uninteresting extension
   663 							Skip();
   664 							break;
   665 					}
   666 						break;
   667 
   668 					case 0x3b : // terminator
   669 						done = true;
   670 						break;
   671 
   672 					case 0x00 : // bad byte, but keep going and see what happens
   673 						break;
   674 
   675 					default :
   676 						status = StatusFormatError;
   677 						break;
   678 				}
   679 			}
   680 		}
   681 
   682 		/**
   683 		 * Reads Graphics Control Extension values
   684 		 */
   685 		private void ReadGraphicControlExt() 
   686 		{
   687 			Read(); // block size
   688 			int packed = Read(); // packed fields
   689 			dispose = (packed & 0x1c) >> 2; // disposal method
   690 			if (dispose == 0) 
   691 			{
   692 				dispose = 1; // elect to keep old image if discretionary
   693 			}
   694 			transparency = (packed & 1) != 0;
   695 			delay = ReadShort() * 10; // delay in milliseconds
   696 			transIndex = Read(); // transparent color index
   697 			Read(); // block terminator
   698 		}
   699 
   700 		/**
   701 		 * Reads GIF file header information.
   702 		 */
   703 		private void ReadHeader() 
   704 		{
   705 			String id = "";
   706 			for (int i = 0; i < 6; i++) 
   707 			{
   708 				id += (char) Read();
   709 			}
   710 			if (!id.StartsWith("GIF")) 
   711 			{
   712 				status = StatusFormatError;
   713 				return;
   714 			}
   715 
   716 			ReadLSD();
   717 			if (gctFlag && !Error()) 
   718 			{
   719 				gct = ReadColorTable(gctSize);
   720 				bgColor = gct[bgIndex];
   721 			}
   722 		}
   723 
   724 		/**
   725 		 * Reads next frame image
   726 		 */
   727 		private void ReadImage() 
   728 		{
   729 			ix = ReadShort(); // (sub)image position & size
   730 			iy = ReadShort();
   731 			iw = ReadShort();
   732 			ih = ReadShort();
   733 
   734 			int packed = Read();
   735 			lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
   736 			interlace = (packed & 0x40) != 0; // 2 - interlace flag
   737 			// 3 - sort flag
   738 			// 4-5 - reserved
   739 			lctSize = 2 << (packed & 7); // 6-8 - local color table size
   740 
   741 			if (lctFlag) 
   742 			{
   743 				lct = ReadColorTable(lctSize); // read table
   744 				act = lct; // make local table active
   745 			} 
   746 			else 
   747 			{
   748 				act = gct; // make global table active
   749 				if (bgIndex == transIndex)
   750 					bgColor = 0;
   751 			}
   752 			int save = 0;
   753 			if (transparency) 
   754 			{
   755 				save = act[transIndex];
   756 				act[transIndex] = 0; // set transparent color if specified
   757 			}
   758 
   759 			if (act == null) 
   760 			{
   761 				status = StatusFormatError; // no color table defined
   762 			}
   763 
   764 			if (Error()) return;
   765 
   766 			DecodeImageData(); // decode pixel data
   767 			Skip();
   768 
   769 			if (Error()) return;
   770 
   771 			frameCount++;
   772 
   773 			// create new image to receive frame data
   774 			//		image =
   775 			//			new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
   776 
   777 			bitmap = new Bitmap( width, height );
   778 			image = bitmap;
   779 			SetPixels(); // transfer pixel data to image
   780 
   781 			frames.Add(new GifFrame(bitmap, delay)); // add image to frame list
   782 
   783 			if (transparency) 
   784 			{
   785 				act[transIndex] = save;
   786 			}
   787 			ResetFrame();
   788 
   789 		}
   790 
   791 		/**
   792 		 * Reads Logical Screen Descriptor
   793 		 */
   794 		private void ReadLSD() 
   795 		{
   796 
   797 			// logical screen size
   798 			width = ReadShort();
   799 			height = ReadShort();
   800 
   801 			// packed fields
   802 			int packed = Read();
   803 			gctFlag = (packed & 0x80) != 0; // 1   : global color table flag
   804 			// 2-4 : color resolution
   805 			// 5   : gct sort flag
   806 			gctSize = 2 << (packed & 7); // 6-8 : gct size
   807 
   808 			bgIndex = Read(); // background color index
   809 			pixelAspect = Read(); // pixel aspect ratio
   810 		}
   811 
   812 		/**
   813 		 * Reads Netscape extenstion to obtain iteration count
   814 		 */
   815 		private void ReadNetscapeExt() 
   816 		{
   817 			do 
   818 			{
   819 				ReadBlock();
   820 				if (block[0] == 1) 
   821 				{
   822 					// loop count sub-block
   823 					int b1 = ((int) block[1]) & 0xff;
   824 					int b2 = ((int) block[2]) & 0xff;
   825 					loopCount = (b2 << 8) | b1;
   826 				}
   827 			} while ((blockSize > 0) && !Error());
   828 		}
   829 
   830 		/**
   831 		 * Reads next 16-bit value, LSB first
   832 		 */
   833 		private int ReadShort() 
   834 		{
   835 			// read 16-bit value, LSB first
   836 			return Read() | (Read() << 8);
   837 		}
   838 
   839 		/**
   840 		 * Resets frame state for reading next image.
   841 		 */
   842 		private void ResetFrame() 
   843 		{
   844 			lastDispose = dispose;
   845 			lastRect = new Rectangle(ix, iy, iw, ih);
   846 			lastImage = image;
   847 			lastBgColor = bgColor;
   848 			//		int dispose = 0;
   849 			lct = null;
   850 		}
   851 
   852 		/**
   853 		 * Skips variable length blocks up to and including
   854 		 * next zero length block.
   855 		 */
   856 		private void Skip() 
   857 		{
   858 			do 
   859 			{
   860 				ReadBlock();
   861 			} while ((blockSize > 0) && !Error());
   862 		}
   863 	}
   864 }