/// <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); }
/// <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(); } }
/// <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(); } }
/// <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 ); }