private void SaveData(DataBlock identificationBlock,
                              Collection <DataBlock> applicationData)
        {
            _identificationBlock = identificationBlock;

            StringBuilder sb;

            if (_identificationBlock.Data.Length < 11)
            {
                string message
                    = "The identification block should be 11 bytes long but "
                      + "is only " + _identificationBlock.Data.Length + " bytes.";
                throw new ArgumentException(message, "identificationBlock");
            }

            if (_identificationBlock.Data.Length > 11)
            {
                string message
                    = "The identification block should be 11 bytes long but "
                      + "is " + _identificationBlock.Data.Length + " bytes long. "
                      + "Additional bytes are ignored.";
                SetStatus(ErrorState.IdentificationBlockTooLong, message);
            }

            // Read application identifer
            sb = new StringBuilder();
            for (int i = 0; i < 8; i++)
            {
                sb.Append((char)_identificationBlock[i]);
            }
            _applicationIdentifier = sb.ToString();

            // Read application authentication code
            sb = new StringBuilder();
            for (int i = 8; i < 11; i++)
            {
                sb.Append((char)_identificationBlock[i]);
            }
            _applicationAuthenticationCode = sb.ToString();

            _applicationData = applicationData;

            if (XmlDebugging)
            {
                WriteDebugXmlStartElement("IdentificationData");
                WriteDebugXmlNode(identificationBlock.DebugXmlReader);
                WriteDebugXmlElement("ApplicationIdentifier",
                                     _applicationIdentifier);
                WriteDebugXmlElement("ApplicationAuthenticationCode",
                                     _applicationAuthenticationCode);
                WriteDebugXmlEndElement();

                WriteDebugXmlStartElement("ApplicationData");
                foreach (DataBlock db in applicationData)
                {
                    WriteDebugXmlNode(db.DebugXmlReader);
                }
                WriteDebugXmlEndElement();

                WriteDebugXmlFinish();
            }
        }
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="identificationBlock">
 /// Sets the <see cref="IdentificationBlock"/>
 /// </param>
 /// <param name="applicationData">
 /// Sets the <see cref="ApplicationData"/>
 /// </param>
 public ApplicationExtension(DataBlock identificationBlock,
                             Collection <DataBlock> applicationData)
 {
     SaveData(identificationBlock, applicationData);
 }
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="identificationBlock">
		/// Sets the <see cref="IdentificationBlock"/>
		/// </param>
		/// <param name="applicationData">
		/// Sets the <see cref="ApplicationData"/>
		/// </param>
		public ApplicationExtension( DataBlock identificationBlock, 
		                             Collection<DataBlock> applicationData )
		{
			SaveData( identificationBlock, applicationData );
		}
 /// <summary>
 /// Skips variable length blocks up to and including next zero length 
 /// block (block terminator).
 /// </summary>
 /// <param name="inputStream">
 /// The input stream to read.
 /// </param>
 protected void SkipBlocks( Stream inputStream )
 {
     DataBlock block;
     do
     {
         block = new DataBlock( inputStream, _xmlDebugging );
     }
     while( block.DeclaredBlockSize > 0 && block.ErrorState == ErrorState.Ok );
 }
        public void WriteToStreamTest()
        {
            ReportStart();
            MemoryStream s = new MemoryStream();
            _block.WriteToStream( s );
            s.Seek( 0, SeekOrigin.Begin );

            DataBlock d = new DataBlock( s );
            Assert.AreEqual( ErrorState.Ok, d.ConsolidatedState );
            Assert.AreEqual( _block.DeclaredBlockSize, d.DeclaredBlockSize );
            Assert.AreEqual( _block.ActualBlockSize, d.ActualBlockSize );
            for( int i = 0; i < _block.ActualBlockSize; i++ )
            {
                Assert.AreEqual( _block.Data[i], d.Data[i], "byte " + i );
            }
            ReportEnd();
        }
        private void ConstructorStreamTestEmptyBlock( bool xmlDebugging )
        {
            Stream s = new MemoryStream();
            s.WriteByte( 0 ); // write length (0 bytes)
            s.Seek( 0, SeekOrigin.Begin ); // go to start of stream

            _block = new DataBlock( s, xmlDebugging );
            Assert.AreEqual( 0, _block.DeclaredBlockSize );
            Assert.AreEqual( 0, _block.ActualBlockSize );
            Assert.AreEqual( 0, _block.Data.Length );
            Assert.AreEqual( ErrorState.Ok, _block.ConsolidatedState );

            if( xmlDebugging )
            {
                Assert.AreEqual( ExpectedDebugXml, _block.DebugXml );
            }
        }
        public void SkipBlocksTest()
        {
            ReportStart();

            byte[] data1 = new byte[] { 24, 38, 53 };
            byte[] data2 = new byte[] { 66, 23, 58, 56, 23 };
            byte[] data3 = new byte[] { }; // block terminator
            byte[] data4 = new byte[] { 43 };
            byte[] data5 = new byte[] { }; // block terminator

            MemoryStream s = new MemoryStream();
            s.WriteByte( (byte) data1.Length );
            s.Write( data1, 0, data1.Length );
            s.WriteByte( (byte) data2.Length );
            s.Write( data2, 0, data2.Length );
            s.WriteByte( (byte) data3.Length );
            s.Write( data3, 0, data3.Length );
            s.WriteByte( (byte) data4.Length );
            s.Write( data4, 0, data4.Length );
            s.WriteByte( (byte) data5.Length );
            s.Write( data5, 0, data5.Length );
            s.Seek( 0, SeekOrigin.Begin );

            _component.CallSkipBlocks( s );
            DataBlock db;

            db = new DataBlock( s );
            Assert.AreEqual( data4.Length, db.DeclaredBlockSize );
            Assert.AreEqual( data4.Length, db.ActualBlockSize );
            Assert.AreEqual( data4, db.Data );

            db = new DataBlock( s );
            Assert.AreEqual( data5.Length, db.DeclaredBlockSize );
            Assert.AreEqual( data5.Length, db.ActualBlockSize );
            Assert.AreEqual( data5, db.Data );

            ReportEnd();
        }
 public void ConstructorTestNullArgument()
 {
     ReportStart();
     try
     {
         _block = new DataBlock( 1, null );
     }
     catch( ArgumentNullException ex )
     {
         Assert.AreEqual( "data", ex.ParamName );
         ReportEnd();
         throw;
     }
 }
Example #9
0
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="inputStream">
		/// The stream from which the image data is to be read, starting with
		/// the LZW minimum code size, and ending with a block terminator.
		/// </param>
		/// <param name="pixelCount">
		/// Number of pixels in the image.
		/// </param>
		/// <param name="xmlDebugging">Whether or not to create debug XML</param>
		/// <remarks>
		/// The input stream is read, first into the LZW minimum code size, then
		/// into data blocks. Bytes are extracted from the data blocks into a
		/// datum until the datum contains enough bits to form a code; this code
		/// is then extracted from the datum and decoded into a pixel index.
		/// Once all data has been read, or a block terminator, 
		/// end-of-information code or error condition is encountered, any
		/// remaining pixel indices not already populated default to zero.
		/// </remarks>
		public TableBasedImageData( Stream inputStream, 
		                            int pixelCount, 
		                            bool xmlDebugging )
			: base( xmlDebugging )
		{
			#region guard against silly image sizes
			if( pixelCount < 1 )
			{
				string message
					= "The pixel count must be greater than zero. "
					+ "Supplied value was " + pixelCount;
				throw new ArgumentOutOfRangeException( "pixelCount", message );
			}
			#endregion
			
			#region declare / initialise local variables
			_pixels = new IndexedPixels( pixelCount );
			_dataBlocks = new Collection<DataBlock>();
			int nextAvailableCode; // the next code to be added to the dictionary
			int	currentCodeSize;
			int	in_code;
			int	previousCode;
			int	code;
			int	datum = 0; // temporary storage for codes read from the input stream
			int	meaningfulBitsInDatum = 0; // number of bits of useful information held in the datum variable
			int	firstCode = 0; // first code read from the stream since last clear code
			int	indexInDataBlock = 0;
			int	pixelIndex = 0;
			
			// number of bytes still to be extracted from the current data block
			int bytesToExtract = 0; 

			short[] prefix = new short[_maxStackSize];
			byte[] suffix = new byte[_maxStackSize];
			Stack<byte> pixelStack = new Stack<byte>();
			#endregion

			//  Initialize GIF data stream decoder.
			_lzwMinimumCodeSize = Read( inputStream ); // number of bits initially used for LZW codes in image data
			WriteDebugXmlElement( "LzwMinimumCodeSize", _lzwMinimumCodeSize );
			nextAvailableCode = ClearCode + 2;
			previousCode = _nullCode;
			currentCodeSize = InitialCodeSize;
			
			#region guard against LZW code size being too large
			if( ClearCode >= _maxStackSize )
			{
				string message
					= "LZW minimum code size: " + _lzwMinimumCodeSize
					+ ". Clear code: " + ClearCode
					+ ". Max stack size: " + _maxStackSize;
				SetStatus( ErrorState.LzwMinimumCodeSizeTooLarge, message );
				WriteDebugXmlFinish();
				return;
			}
			#endregion
			
			// TODO: what are prefix and suffix and why are we initialising them like this?
			for( code = 0; code < ClearCode; code++ )
			{
				prefix[code] = 0;
				suffix[code] = (byte) code;
			}
			
			WriteDebugXmlStartElement( "DataBlocks" );

			#region decode LZW image data

			// Initialise block to an empty data block. This will be overwritten
			// first time through the loop with a data block read from the input
			// stream.
			DataBlock block = new DataBlock( 0, new byte[0] );
			
			for( pixelIndex = 0; pixelIndex < pixelCount; ) 
			{
				if( pixelStack.Count == 0 )
				{
					// There are no pixels in the stack at the moment, so...
					#region get some pixels and put them on the stack
					if( meaningfulBitsInDatum < currentCodeSize )
					{
						// Then we don't have enough bits in the datum to make
						// a code; we need to get some more from the current
						// data block, or we may need to read another data
						// block from the input stream
						#region get another byte from the current data block
						if( bytesToExtract == 0 )
						{
							// Then we've extracted all the bytes from the 
							// current data block, so...
							
							#region	read the next data block from the stream
							block = ReadDataBlock( inputStream );
							bytesToExtract = block.ActualBlockSize;
							
							// Point to the first byte in the new data block
							indexInDataBlock = 0;

							if( block.TestState( ErrorState.DataBlockTooShort ) )
							{
								// then we've reached the end of the stream
								// prematurely
								break;
							}
							
							if( bytesToExtract == 0 )
							{
								// then it's a block terminator, end of the
								// image data (this is a data block other than
								// the first one)
								break;
							}
							#endregion
						}
						// Append the contents of the current byte in the data 
						// block to the beginning of the datum
						int newDatum = block[indexInDataBlock] << meaningfulBitsInDatum;
						datum += newDatum;

						// so we've now got 8 more bits of information in the
						// datum.
						meaningfulBitsInDatum += 8;
						
						// Point to the next byte in the data block
						indexInDataBlock++;
						
						// We've one less byte still to read from the data block
						// now.
						bytesToExtract--;
						
						// and carry on reading through the data block
						continue;
						#endregion
					}

					#region get the next code from the datum
					// Get the least significant bits from the read datum, up
					// to the maximum allowed by the current code size.
					code = datum & GetMaximumPossibleCode( currentCodeSize );
					
					// Drop the bits we've just extracted from the datum.
					datum >>= currentCodeSize;
					
					// Reduce the count of meaningful bits held in the datum
					meaningfulBitsInDatum -= currentCodeSize;
					#endregion

					#region interpret the code

					#region end of information?
					if( code == EndOfInformation )
					{
						// We've reached an explicit marker for the end of the
						// image data.
						break;
					}
					#endregion

					#region code not in dictionary?
					if( code > nextAvailableCode )
					{
						// We expect the code to be either one which is already
						// in the dictionary, or the next available one to be
						// added. If it's neither of these then abandon 
						// processing of the image data.
						string message
							= "Next available code: " + nextAvailableCode
							+ ". Last code read from input stream: " + code;
						SetStatus( ErrorState.CodeNotInDictionary, message );
						break;
					}
					#endregion

					#region clear code?
					if( code == ClearCode )
					{
						// We can get a clear code at any point in the image
						// data, this is an instruction to reset the decoder
						// and empty the dictionary of codes.
						currentCodeSize = InitialCodeSize;
						nextAvailableCode = ClearCode + 2;
						previousCode = _nullCode;

						// Carry on reading from the input stream.
						continue;
					}
					#endregion
					
					#region first code since last clear code?
					if( previousCode == _nullCode )
					{
						// This is the first code read since the start of the
						// image data or the most recent clear code.
						// There's no previously read code in memory yet, so
						// get the pixel index for the current code and add it
						// to the stack.
						pixelStack.Push( suffix[code] );
						previousCode = code;
						firstCode = code;
						
						// and carry on to the next pixel
						continue;
					}
					#endregion

					in_code = code;
					if( code == nextAvailableCode )
					{
						pixelStack.Push( (byte) firstCode );
						code = previousCode;
					}
					
					while( code > ClearCode )
					{
						pixelStack.Push( suffix[code] );
						code = prefix[code];
					}

					#endregion

					firstCode = ((int) suffix[code]) & 0xff;

					#region add a new string to the string table
					if( nextAvailableCode >= _maxStackSize )
					{
						// TESTME: constructor - next available code >- _maxStackSize
						break;
					}
					pixelStack.Push( (byte) firstCode );
					prefix[nextAvailableCode] = (short) previousCode;
					suffix[nextAvailableCode] = (byte) firstCode;
					nextAvailableCode++;
					#endregion
					
					#region do we need to increase the code size?
					if( ( nextAvailableCode & GetMaximumPossibleCode( currentCodeSize ) ) == 0 )
					{
						// We've reached the largest code possible for this size
						if( nextAvailableCode < _maxStackSize )
						{
							// so increase the code size by 1
							currentCodeSize++;
						}
					}
					#endregion

					previousCode = in_code;
					#endregion
				}

				// Pop all the pixels currently on the stack off, and add them
				// to the return value.
				_pixels[pixelIndex] = pixelStack.Pop();
				pixelIndex++;
			}

			#endregion

			#region check input stream contains enough data to fill the image
			if( pixelIndex < pixelCount )
			{
				string message
					= "Expected pixel count: " + pixelCount
					+ ". Actual pixel count: " + pixelIndex;
				SetStatus( ErrorState.TooFewPixelsInImageData, message );
			}
			#endregion
			
			if( XmlDebugging )
			{
				WriteDebugXmlEndElement();
				byte[] bytes = new byte[_pixels.Count];
				_pixels.CopyTo( bytes, 0 );
				WriteDebugXmlByteValues( "IndexedPixels", bytes );
				WriteDebugXmlFinish();
			}
		}
Example #10
0
		private DataBlock ReadDataBlock( Stream inputStream )
		{
			DataBlock block = new DataBlock( inputStream, XmlDebugging );
			_dataBlocks.Add( block );
			WriteDebugXmlNode( block.DebugXmlReader );
			return block;
		}
Example #11
0
		private static Collection<DataBlock> GetApplicationData( int repeatCount )
		{
			MemoryStream s = new MemoryStream();
			WriteByte( 1, s );
			WriteShort( repeatCount, s );
			s.Seek( 0, SeekOrigin.Begin );
			byte[] repeatData = new byte[3];
			s.Read( repeatData, 0, 3 );
			DataBlock repeatBlock = new DataBlock( 3, repeatData );
			
			byte[] terminatorData = new byte[0];
			DataBlock terminatorBlock = new DataBlock( 0, terminatorData );
			
			Collection<DataBlock> appData = new Collection<DataBlock>();
			appData.Add( repeatBlock );
			appData.Add( terminatorBlock );
			return appData;
		}
Example #12
0
		private static DataBlock GetIdentificationBlock()
		{
			MemoryStream s = new MemoryStream();
			WriteString( "NETSCAPE2.0", s );
			s.Seek( 0, SeekOrigin.Begin );
			byte[] identificationData = new byte[11];
			s.Read( identificationData, 0, 11 );
			DataBlock identificationBlock = new DataBlock( 11, identificationData );
			return identificationBlock;
		}
Example #13
0
		/// <summary>
		/// Reads and returns an application extension from the supplied input 
		/// stream.
		/// </summary>
		/// <param name="inputStream">
		/// The input stream to read.
		/// </param>
		/// <param name="xmlDebugging">Whether or not to create debug XML</param>
		public ApplicationExtension( Stream inputStream, bool xmlDebugging )
			: base( xmlDebugging )
		{
			DataBlock identificationBlock = new DataBlock( inputStream, 
			                                               XmlDebugging );
			Collection<DataBlock> applicationData = new Collection<DataBlock>();
			if( !identificationBlock.TestState( ErrorState.EndOfInputStream ) )
			{
				// Read application specific data
				DataBlock thisBlock;
				do
				{
					thisBlock = new DataBlock( inputStream, XmlDebugging );
					applicationData.Add( thisBlock );
				}
				// A zero-length block indicates the end of the data blocks
				while( thisBlock.DeclaredBlockSize != 0 
				       && !thisBlock.TestState( ErrorState.EndOfInputStream ) 
				     );
			}
			
			SaveData( identificationBlock, applicationData );
		}
 public void ConstructorTest()
 {
     ReportStart();
     _block = new DataBlock( 4, _data );
     Assert.AreEqual( 4, _block.ActualBlockSize );
     Assert.AreEqual( 4, _block.DeclaredBlockSize );
     Assert.AreEqual( ErrorState.Ok, _block.ConsolidatedState );
     Assert.AreEqual( _data.Length, _block.Data.Length );
     for( int i = 0; i < _data.Length; i++ )
     {
         Assert.AreEqual( _data[i], _block.Data[i], i );
         Assert.AreEqual( _data[i], _block[i], i );
     }
     ReportEnd();
 }
        public void ConstructorTestNot2Point0()
        {
            byte[] idData = new byte[]
            {
                (byte) 'N',
                (byte) 'E',
                (byte) 'T',
                (byte) 'S',
                (byte) 'C',
                (byte) 'A',
                (byte) 'P',
                (byte) 'E',
                (byte) '3',
                (byte) '.',
                (byte) '0',
            };
            DataBlock idBlock = new DataBlock( 11, idData );

            ApplicationExtension appExt
                = new ApplicationExtension( idBlock, new Collection<DataBlock>() );
            try
            {
                _ne = new NetscapeExtension( appExt );
            }
            catch( ArgumentException ex )
            {
                string message
                    = "The application authentication code is not '2.0' "
                    + "therefore this application extension is not a "
                    + "Netscape extension. Application authentication code: 3.0";
                StringAssert.Contains( message, ex.Message );
                Assert.AreEqual( "applicationExtension", ex.ParamName );
                throw;
            }
        }
 public void ConstructorTestBlockSizeTooSmall()
 {
     ReportStart();
     _block = new DataBlock( 5, _data );
     Assert.AreEqual( 4, _block.ActualBlockSize );
     Assert.AreEqual( 5, _block.DeclaredBlockSize );
     Assert.AreEqual( ErrorState.DataBlockTooShort, _block.ErrorState );
     Assert.AreEqual( _data.Length, _block.Data.Length );
     for( int i = 0; i < _data.Length; i++ )
     {
         Assert.AreEqual( _data[i], _block.Data[i], i );
     }
     ReportEnd();
 }
        public void ConstructorTestNotNetscape()
        {
            byte[] idData = new byte[]
            {
                (byte) 'B',
                (byte) 'I',
                (byte) 'G',
                (byte) 'P',
                (byte) 'A',
                (byte) 'N',
                (byte) 'T',
                (byte) 'S',
                (byte) '2',
                (byte) '.',
                (byte) '0',
            };
            DataBlock idBlock = new DataBlock( 11, idData );

            ApplicationExtension appExt
                = new ApplicationExtension( idBlock, new Collection<DataBlock>() );
            try
            {
                _ne = new NetscapeExtension( appExt );
            }
            catch( ArgumentException ex )
            {
                string message
                    = "The application identifier is not 'NETSCAPE' "
                    + "therefore this application extension is not a "
                    + "Netscape extension. Application identifier: BIGPANTS";
                StringAssert.Contains( message, ex.Message );
                Assert.AreEqual( "applicationExtension", ex.ParamName );
                throw;
            }
        }
 public void IndexerTestArgumentOutOfRange()
 {
     ReportStart();
     _block = new DataBlock( 4, _data );
     try
     {
         byte b = _block[4];
     }
     catch( ArgumentOutOfRangeException ex )
     {
         string message
             = "Supplied index: 4. Array length: 4";
         StringAssert.Contains( message, ex.Message );
         Assert.AreEqual( "index", ex.ParamName );
         ReportEnd();
         throw;
     }
 }
        public void ConstructorTest()
        {
            byte[] idData = new byte[]
            {
                (byte) 'N',
                (byte) 'E',
                (byte) 'T',
                (byte) 'S',
                (byte) 'C',
                (byte) 'A',
                (byte) 'P',
                (byte) 'E',
                (byte) '2',
                (byte) '.',
                (byte) '0',
            };
            DataBlock idBlock = new DataBlock( 11, idData );

            Collection<DataBlock> appData = new Collection<DataBlock>();

            // First byte in the data block is always 1 for a netscape extension
            // Second and third bytes are the loop count, lsb first
            byte[] loopCount = new byte[] { 1, 4, 0 };
            appData.Add( new DataBlock( 3, loopCount ) );

            // Add the block terminator
            appData.Add( new DataBlock( 0, new byte[0] ) );

            ApplicationExtension appExt
                = new ApplicationExtension( idBlock, appData );
            _ne = new NetscapeExtension( appExt );

            Assert.AreEqual( 4, _ne.LoopCount );
            Assert.AreEqual( "NETSCAPE", _ne.ApplicationIdentifier );
            Assert.AreEqual( "2.0", _ne.ApplicationAuthenticationCode );
            Assert.AreEqual( ErrorState.Ok, _ne.ConsolidatedState );
        }
        private void ConstructorStreamTestBlockSizeTooLarge( bool xmlDebugging )
        {
            Stream s = new MemoryStream();
            s.WriteByte( 5 ); // write block size
            s.Write( _data, 0, _data.Length ); // write data block
            s.Seek( 0, SeekOrigin.Begin ); // go to start of stream

            _block = new DataBlock( s, xmlDebugging );
            Assert.AreEqual( 5, _block.ActualBlockSize );
            Assert.AreEqual( 5, _block.DeclaredBlockSize );
            Assert.AreEqual( ErrorState.DataBlockTooShort, _block.ErrorState );
            Assert.AreEqual( 5, _block.Data.Length );
            for( int i = 0; i < _data.Length; i++ )
            {
                Assert.AreEqual( _data[i], _block.Data[i], i );
            }

            if( xmlDebugging )
            {
                Assert.AreEqual( ExpectedDebugXml, _block.DebugXml );
            }
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="inputStream">
        /// The stream from which the image data is to be read, starting with
        /// the LZW minimum code size, and ending with a block terminator.
        /// </param>
        /// <param name="pixelCount">
        /// Number of pixels in the image.
        /// </param>
        /// <param name="xmlDebugging">Whether or not to create debug XML</param>
        /// <remarks>
        /// The input stream is read, first into the LZW minimum code size, then
        /// into data blocks. Bytes are extracted from the data blocks into a
        /// datum until the datum contains enough bits to form a code; this code
        /// is then extracted from the datum and decoded into a pixel index.
        /// Once all data has been read, or a block terminator,
        /// end-of-information code or error condition is encountered, any
        /// remaining pixel indices not already populated default to zero.
        /// </remarks>
        public TableBasedImageData(Stream inputStream,
                                   int pixelCount,
                                   bool xmlDebugging)
            : base(xmlDebugging)
        {
            #region guard against silly image sizes
            if (pixelCount < 1)
            {
                string message
                    = "The pixel count must be greater than zero. "
                      + "Supplied value was " + pixelCount;
                throw new ArgumentOutOfRangeException("pixelCount", message);
            }
            #endregion

            #region declare / initialise local variables
            _pixels     = new IndexedPixels(pixelCount);
            _dataBlocks = new Collection <DataBlock>();
            int nextAvailableCode;             // the next code to be added to the dictionary
            int currentCodeSize;
            int in_code;
            int previousCode;
            int code;
            int datum = 0;                 // temporary storage for codes read from the input stream
            int meaningfulBitsInDatum = 0; // number of bits of useful information held in the datum variable
            int firstCode             = 0; // first code read from the stream since last clear code
            int indexInDataBlock      = 0;
            int pixelIndex            = 0;

            // number of bytes still to be extracted from the current data block
            int bytesToExtract = 0;

            short[]      prefix     = new short[_maxStackSize];
            byte[]       suffix     = new byte[_maxStackSize];
            Stack <byte> pixelStack = new Stack <byte>();
            #endregion

            //  Initialize GIF data stream decoder.
            _lzwMinimumCodeSize = Read(inputStream);               // number of bits initially used for LZW codes in image data
            WriteDebugXmlElement("LzwMinimumCodeSize", _lzwMinimumCodeSize);
            nextAvailableCode = ClearCode + 2;
            previousCode      = _nullCode;
            currentCodeSize   = InitialCodeSize;

            #region guard against LZW code size being too large
            if (ClearCode >= _maxStackSize)
            {
                string message
                    = "LZW minimum code size: " + _lzwMinimumCodeSize
                      + ". Clear code: " + ClearCode
                      + ". Max stack size: " + _maxStackSize;
                SetStatus(ErrorState.LzwMinimumCodeSizeTooLarge, message);
                WriteDebugXmlFinish();
                return;
            }
            #endregion

            // TODO: what are prefix and suffix and why are we initialising them like this?
            for (code = 0; code < ClearCode; code++)
            {
                prefix[code] = 0;
                suffix[code] = (byte)code;
            }

            WriteDebugXmlStartElement("DataBlocks");

            #region decode LZW image data

            // Initialise block to an empty data block. This will be overwritten
            // first time through the loop with a data block read from the input
            // stream.
            DataBlock block = new DataBlock(0, new byte[0]);

            for (pixelIndex = 0; pixelIndex < pixelCount;)
            {
                if (pixelStack.Count == 0)
                {
                    // There are no pixels in the stack at the moment, so...
                    #region get some pixels and put them on the stack
                    if (meaningfulBitsInDatum < currentCodeSize)
                    {
                        // Then we don't have enough bits in the datum to make
                        // a code; we need to get some more from the current
                        // data block, or we may need to read another data
                        // block from the input stream
                        #region get another byte from the current data block
                        if (bytesToExtract == 0)
                        {
                            // Then we've extracted all the bytes from the
                            // current data block, so...

                            #region read the next data block from the stream
                            block          = ReadDataBlock(inputStream);
                            bytesToExtract = block.ActualBlockSize;

                            // Point to the first byte in the new data block
                            indexInDataBlock = 0;

                            if (block.TestState(ErrorState.DataBlockTooShort))
                            {
                                // then we've reached the end of the stream
                                // prematurely
                                break;
                            }

                            if (bytesToExtract == 0)
                            {
                                // then it's a block terminator, end of the
                                // image data (this is a data block other than
                                // the first one)
                                break;
                            }
                            #endregion
                        }
                        // Append the contents of the current byte in the data
                        // block to the beginning of the datum
                        int newDatum = block[indexInDataBlock] << meaningfulBitsInDatum;
                        datum += newDatum;

                        // so we've now got 8 more bits of information in the
                        // datum.
                        meaningfulBitsInDatum += 8;

                        // Point to the next byte in the data block
                        indexInDataBlock++;

                        // We've one less byte still to read from the data block
                        // now.
                        bytesToExtract--;

                        // and carry on reading through the data block
                        continue;
                        #endregion
                    }

                    #region get the next code from the datum
                    // Get the least significant bits from the read datum, up
                    // to the maximum allowed by the current code size.
                    code = datum & GetMaximumPossibleCode(currentCodeSize);

                    // Drop the bits we've just extracted from the datum.
                    datum >>= currentCodeSize;

                    // Reduce the count of meaningful bits held in the datum
                    meaningfulBitsInDatum -= currentCodeSize;
                    #endregion

                    #region interpret the code

                    #region end of information?
                    if (code == EndOfInformation)
                    {
                        // We've reached an explicit marker for the end of the
                        // image data.
                        break;
                    }
                    #endregion

                    #region code not in dictionary?
                    if (code > nextAvailableCode)
                    {
                        // We expect the code to be either one which is already
                        // in the dictionary, or the next available one to be
                        // added. If it's neither of these then abandon
                        // processing of the image data.
                        string message
                            = "Next available code: " + nextAvailableCode
                              + ". Last code read from input stream: " + code;
                        SetStatus(ErrorState.CodeNotInDictionary, message);
                        break;
                    }
                    #endregion

                    #region clear code?
                    if (code == ClearCode)
                    {
                        // We can get a clear code at any point in the image
                        // data, this is an instruction to reset the decoder
                        // and empty the dictionary of codes.
                        currentCodeSize   = InitialCodeSize;
                        nextAvailableCode = ClearCode + 2;
                        previousCode      = _nullCode;

                        // Carry on reading from the input stream.
                        continue;
                    }
                    #endregion

                    #region first code since last clear code?
                    if (previousCode == _nullCode)
                    {
                        // This is the first code read since the start of the
                        // image data or the most recent clear code.
                        // There's no previously read code in memory yet, so
                        // get the pixel index for the current code and add it
                        // to the stack.
                        pixelStack.Push(suffix[code]);
                        previousCode = code;
                        firstCode    = code;

                        // and carry on to the next pixel
                        continue;
                    }
                    #endregion

                    in_code = code;
                    if (code == nextAvailableCode)
                    {
                        pixelStack.Push((byte)firstCode);
                        code = previousCode;
                    }

                    while (code > ClearCode)
                    {
                        pixelStack.Push(suffix[code]);
                        code = prefix[code];
                    }

                    #endregion

                    firstCode = ((int)suffix[code]) & 0xff;

                    #region add a new string to the string table
                    if (nextAvailableCode >= _maxStackSize)
                    {
                        // TESTME: constructor - next available code >- _maxStackSize
                        break;
                    }
                    pixelStack.Push((byte)firstCode);
                    prefix[nextAvailableCode] = (short)previousCode;
                    suffix[nextAvailableCode] = (byte)firstCode;
                    nextAvailableCode++;
                    #endregion

                    #region do we need to increase the code size?
                    if ((nextAvailableCode & GetMaximumPossibleCode(currentCodeSize)) == 0)
                    {
                        // We've reached the largest code possible for this size
                        if (nextAvailableCode < _maxStackSize)
                        {
                            // so increase the code size by 1
                            currentCodeSize++;
                        }
                    }
                    #endregion

                    previousCode = in_code;
                    #endregion
                }

                // Pop all the pixels currently on the stack off, and add them
                // to the return value.
                _pixels[pixelIndex] = pixelStack.Pop();
                pixelIndex++;
            }

            #endregion

            #region check input stream contains enough data to fill the image
            if (pixelIndex < pixelCount)
            {
                string message
                    = "Expected pixel count: " + pixelCount
                      + ". Actual pixel count: " + pixelIndex;
                SetStatus(ErrorState.TooFewPixelsInImageData, message);
            }
            #endregion

            if (XmlDebugging)
            {
                WriteDebugXmlEndElement();
                byte[] bytes = new byte[_pixels.Count];
                _pixels.CopyTo(bytes, 0);
                WriteDebugXmlByteValues("IndexedPixels", bytes);
                WriteDebugXmlFinish();
            }
        }
        private void ConstructorStreamTestEndOfStream( bool xmlDebugging )
        {
            Stream s = new MemoryStream();
            s.Seek( 0, SeekOrigin.Begin );

            _block = new DataBlock( s, xmlDebugging );
            Assert.AreEqual( ErrorState.EndOfInputStream, _block.ErrorState );
            if( xmlDebugging )
            {
                Assert.AreEqual( ExpectedDebugXml, _block.DebugXml );
            }
        }
Example #23
0
		private void SaveData( DataBlock identificationBlock, 
		                       Collection<DataBlock> applicationData )
		{
			_identificationBlock = identificationBlock;

			StringBuilder sb;
			
			if( _identificationBlock.Data.Length < 11 )
			{
				string message
					= "The identification block should be 11 bytes long but "
					+ "is only " + _identificationBlock.Data.Length + " bytes.";
				throw new ArgumentException( message, "identificationBlock" );
			}
			
			if( _identificationBlock.Data.Length > 11 )
			{
				string message
					= "The identification block should be 11 bytes long but "
					+ "is " + _identificationBlock.Data.Length + " bytes long. "
					+ "Additional bytes are ignored.";
				SetStatus( ErrorState.IdentificationBlockTooLong, message );
			}
			
			// Read application identifer
			sb = new StringBuilder();
			for( int i = 0; i < 8; i++ ) 
			{
				sb.Append( (char) _identificationBlock[i] );
			}
			_applicationIdentifier = sb.ToString();
			
			// Read application authentication code
			sb = new StringBuilder();
			for( int i = 8; i < 11; i++ )
			{
				sb.Append( (char) _identificationBlock[i] );
			}
			_applicationAuthenticationCode = sb.ToString();

			_applicationData = applicationData;
			
			if( XmlDebugging )
			{
				WriteDebugXmlStartElement( "IdentificationData" );
				WriteDebugXmlNode( identificationBlock.DebugXmlReader );
				WriteDebugXmlElement( "ApplicationIdentifier", 
				                      _applicationIdentifier );
				WriteDebugXmlElement( "ApplicationAuthenticationCode", 
				                      _applicationAuthenticationCode );
				WriteDebugXmlEndElement();
				
				WriteDebugXmlStartElement( "ApplicationData" );
				foreach( DataBlock db in applicationData )
				{
					WriteDebugXmlNode( db.DebugXmlReader );
				}
				WriteDebugXmlEndElement();
				
				WriteDebugXmlFinish();
			}
		}