External/Aga.Controls/GifDecoder.cs
author StephaneLenclud
Thu, 18 Apr 2013 23:25:10 +0200
branchMiniDisplay
changeset 444 9b09e2ee0968
permissions -rw-r--r--
Front View plug-in does not init if no sensor added.
Fixing some format to make strings shorter.
Now trying to start SoundGraphAccess.exe process from same directory.
Packed mode now can display three sensors along with the current time.
     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 }