public GifFrame( Stream inputStream, LogicalScreenDescriptor lsd, ColourTable gct, GraphicControlExtension gce, GifFrame previousFrame, GifFrame previousFrameBut1 ) : base( inputStream, lsd, gct, gce, previousFrame, previousFrameBut1 ) {}
public GifFrame(Stream inputStream, LogicalScreenDescriptor lsd, ColourTable gct, GraphicControlExtension gce, GifFrame previousFrame, GifFrame previousFrameBut1) : this(inputStream, lsd, gct, gce, previousFrame, previousFrameBut1, false) { }
/// <summary> /// Reads GIF image from stream /// </summary> /// <param name="inputStream"> /// Stream containing GIF file. /// </param> /// <exception cref="ArgumentNullException"> /// The supplied stream is null. /// </exception> private void ReadStream( Stream inputStream ) { _frames = new Collection<GifFrame>(); _applicationExtensions = new Collection<ApplicationExtension>(); _gct = null; _readStreamCounterText = "Reading stream byte"; AddCounter( _readStreamCounterText, (int) inputStream.Length ); _header = new GifHeader( inputStream, XmlDebugging ); MyProgressCounters[_readStreamCounterText].Value = (int) inputStream.Position; WriteDebugXmlNode( _header.DebugXmlReader ); if( _header.ErrorState != ErrorState.Ok ) { WriteDebugXmlFinish(); return; } _lsd = new LogicalScreenDescriptor( inputStream, XmlDebugging ); MyProgressCounters[_readStreamCounterText].Value = (int) inputStream.Position; WriteDebugXmlNode( _lsd.DebugXmlReader ); if( TestState( ErrorState.EndOfInputStream ) ) { WriteDebugXmlFinish(); return; } if( _lsd.HasGlobalColourTable ) { _gct = new ColourTable( inputStream, _lsd.GlobalColourTableSize, XmlDebugging ); MyProgressCounters[_readStreamCounterText].Value = (int) inputStream.Position; WriteDebugXmlNode( _gct.DebugXmlReader ); } if( ConsolidatedState == ErrorState.Ok ) { ReadContents( inputStream ); MyProgressCounters[_readStreamCounterText].Value = (int) inputStream.Position; } inputStream.Close(); WriteDebugXmlFinish(); RemoveCounter( _readStreamCounterText ); }
public void ConstructorTest() { ReportStart(); int width = 15; int height = 20; Size screenSize = new Size( width, height ); bool hasGlobalColourTable = true; int colourResolution = 6; bool colourTableIsSorted = false; int globalColourTableSizeBits = 3; int backgroundColourIndex = 12; int pixelAspectRatio = 2; _lsd = new LogicalScreenDescriptor( screenSize, hasGlobalColourTable, colourResolution, colourTableIsSorted, globalColourTableSizeBits, backgroundColourIndex, pixelAspectRatio ); Assert.AreEqual( screenSize, _lsd.LogicalScreenSize ); Assert.AreEqual( hasGlobalColourTable, _lsd.HasGlobalColourTable ); Assert.AreEqual( colourResolution, _lsd.ColourResolution ); Assert.AreEqual( colourTableIsSorted, _lsd.GlobalColourTableIsSorted ); Assert.AreEqual( globalColourTableSizeBits, _lsd.GlobalColourTableSizeBits ); Assert.AreEqual( Math.Pow( 2, globalColourTableSizeBits + 1 ), _lsd.GlobalColourTableSize ); Assert.AreEqual( backgroundColourIndex, _lsd.BackgroundColourIndex ); Assert.AreEqual( pixelAspectRatio, _lsd.PixelAspectRatio ); Assert.AreEqual( ErrorState.Ok, _lsd.ConsolidatedState ); ReportEnd(); }
private void ConstructorStreamTest( bool xmlDebugging ) { int width = 15; int height = 20; Size screenSize = new Size( width, height ); bool hasGlobalColourTable = true; int colourResolution = 6; bool colourTableIsSorted = false; int globalColourTableSizeBits = 3; int backgroundColourIndex = 12; int pixelAspectRatio = 2; MemoryStream s = new MemoryStream(); // Write screen width and height, least significant bit first s.WriteByte( (byte) ( width & 0xff ) ); s.WriteByte( (byte) ( ( width & 0xff00 ) >> 8 ) ); s.WriteByte( (byte) ( height & 0xff ) ); s.WriteByte( (byte) ( ( height & 0xff00 ) >> 8 ) ); // Packed fields: // bit 1 = global colour table flag // bits 2-4 = colour resolution // bit 5 = sort flag // bits 6-8 = global colour table size byte packed = (byte) ( ( hasGlobalColourTable ? 1 : 0 ) << 7 | ( colourResolution & 7 ) << 4 | ( colourTableIsSorted ? 1 : 0 ) << 3 | ( globalColourTableSizeBits & 7 ) ); s.WriteByte( packed ); s.WriteByte( (byte) backgroundColourIndex ); s.WriteByte( (byte) pixelAspectRatio ); s.Seek( 0, SeekOrigin.Begin ); _lsd = new LogicalScreenDescriptor( s, xmlDebugging ); Assert.AreEqual( ErrorState.Ok, _lsd.ConsolidatedState ); Assert.AreEqual( screenSize, _lsd.LogicalScreenSize ); Assert.AreEqual( hasGlobalColourTable, _lsd.HasGlobalColourTable ); Assert.AreEqual( colourResolution, _lsd.ColourResolution ); Assert.AreEqual( colourTableIsSorted, _lsd.GlobalColourTableIsSorted ); Assert.AreEqual( globalColourTableSizeBits, _lsd.GlobalColourTableSizeBits ); Assert.AreEqual( Math.Pow( 2, globalColourTableSizeBits + 1 ), _lsd.GlobalColourTableSize ); Assert.AreEqual( backgroundColourIndex, _lsd.BackgroundColourIndex ); Assert.AreEqual( pixelAspectRatio, _lsd.PixelAspectRatio ); Assert.AreEqual( ErrorState.Ok, _lsd.ConsolidatedState ); if( xmlDebugging ) { Assert.AreEqual( ExpectedDebugXml, _lsd.DebugXml ); } }
private void ConstructorStreamEndOfStreamTest( bool xmlDebugging ) { Size screenSize = new Size( 12, 4 ); bool hasGlobalColourTable = false; int colourResolution = 3; bool globalColourTableIsSorted = true; int globalColourTableSizeBits = 4; int backgroundColourIndex = 2; int pixelAspectRatio = 1; _lsd = new LogicalScreenDescriptor( screenSize, hasGlobalColourTable, colourResolution, globalColourTableIsSorted, globalColourTableSizeBits, backgroundColourIndex, pixelAspectRatio ); MemoryStream s = new MemoryStream(); _lsd.WriteToStream( s ); s.SetLength( s.Length - 1 ); // remove final byte from stream s.Seek( 0, SeekOrigin.Begin ); _lsd = new LogicalScreenDescriptor( s, xmlDebugging ); Assert.AreEqual( ErrorState.EndOfInputStream, _lsd.ConsolidatedState ); Assert.AreEqual( screenSize, _lsd.LogicalScreenSize ); Assert.AreEqual( hasGlobalColourTable, _lsd.HasGlobalColourTable ); Assert.AreEqual( colourResolution, _lsd.ColourResolution ); Assert.AreEqual( globalColourTableIsSorted, _lsd.GlobalColourTableIsSorted ); Assert.AreEqual( globalColourTableSizeBits, _lsd.GlobalColourTableSizeBits ); Assert.AreEqual( backgroundColourIndex, _lsd.BackgroundColourIndex ); Assert.AreEqual( -1, _lsd.PixelAspectRatio ); if( xmlDebugging ) { Assert.AreEqual( ExpectedDebugXml, _lsd.DebugXml ); } }
private void DecodeSmiley( bool xmlDebugging ) { string fileName = @"images\smiley\smiley.gif"; _decoder = new GifDecoder( fileName, xmlDebugging ); _decoder.Decode(); _lsd = _decoder.LogicalScreenDescriptor; int expectedColourTableSize = 128; Assert.AreEqual( "GIF", _decoder.Header.Signature ); Assert.AreEqual( "89a", _decoder.Header.Version ); Assert.IsNotNull( _decoder.GlobalColourTable ); Assert.AreEqual( true, _lsd.HasGlobalColourTable ); Assert.AreEqual( expectedColourTableSize, _lsd.GlobalColourTableSize ); Assert.AreEqual( expectedColourTableSize, _decoder.GlobalColourTable.Length ); Assert.AreEqual( 7, _lsd.ColourResolution ); Assert.AreEqual( Color.FromArgb( 255, 0, 0, 255 ), _decoder.BackgroundColour ); Assert.AreEqual( 0, _lsd.BackgroundColourIndex ); Assert.AreEqual( false, _lsd.GlobalColourTableIsSorted ); Assert.AreEqual( 18, _lsd.LogicalScreenSize.Width ); Assert.AreEqual( 18, _lsd.LogicalScreenSize.Height ); Assert.AreEqual( 0, _lsd.PixelAspectRatio ); Assert.AreEqual( 0, _decoder.NetscapeExtension.LoopCount ); Assert.AreEqual( ErrorState.Ok, _decoder.ConsolidatedState ); Assert.AreEqual( 4, _decoder.Frames.Count ); int frameNumber = 0; string frameId; foreach( GifFrame thisFrame in _decoder.Frames ) { frameId = "Frame " + frameNumber; Assert.IsNull( thisFrame.LocalColourTable, frameId ); #region image descriptor properties ImageDescriptor descriptor = thisFrame.ImageDescriptor; Assert.AreEqual( false, descriptor.HasLocalColourTable, frameId ); Assert.AreEqual( expectedColourTableSize, descriptor.LocalColourTableSize, frameId ); Assert.AreEqual( false, descriptor.IsInterlaced, frameId ); Assert.AreEqual( false, descriptor.IsSorted, frameId ); #endregion #region graphic control extension properties GraphicControlExtension gce = thisFrame.GraphicControlExtension; Assert.AreEqual( 4, gce.BlockSize, frameId ); Assert.AreEqual( 0, gce.TransparentColourIndex, frameId ); Assert.IsTrue( gce.HasTransparentColour, frameId ); Assert.IsFalse( gce.ExpectsUserInput, frameId ); #endregion switch( frameNumber ) { case 0: Assert.AreEqual( 250, thisFrame.Delay, frameId ); Assert.AreEqual( 250, gce.DelayTime, frameId ); Assert.AreEqual( DisposalMethod.DoNotDispose, gce.DisposalMethod, frameId ); break; case 1: Assert.AreEqual( 5, thisFrame.Delay, frameId ); Assert.AreEqual( 5, gce.DelayTime, frameId ); Assert.AreEqual( DisposalMethod.RestoreToBackgroundColour, gce.DisposalMethod, frameId ); break; case 2: Assert.AreEqual( 10, thisFrame.Delay, frameId ); Assert.AreEqual( 10, gce.DelayTime, frameId ); Assert.AreEqual( DisposalMethod.RestoreToBackgroundColour, gce.DisposalMethod, frameId ); break; case 3: Assert.AreEqual( 5, thisFrame.Delay, frameId ); Assert.AreEqual( 5, gce.DelayTime, frameId ); Assert.AreEqual( DisposalMethod.DoNotDispose, gce.DisposalMethod, frameId ); break; } frameNumber++; } CompareFrames( fileName ); if( xmlDebugging ) { Assert.AreEqual( ExpectedDebugXml, _decoder.DebugXml ); } }
/// <summary> /// Gets the base image for this frame. This will be overpainted with /// the pixels for this frame, where they are not transparent. /// </summary> /// <param name="previousFrame"> /// The frame which preceded this frame in the GIF stream. /// Null if this is the first frame in the stream. /// </param> /// <param name="previousFrameBut1"> /// The frame which preceded the previous frame in the GIF stream. /// Null if this is the first or seond frame in the stream. /// </param> /// <param name="lsd"> /// The logical screen descriptor for this GIF stream. /// </param> /// <param name="gce"> /// The graphic control extension for this frame. /// </param> /// <param name="act"> /// The active colour table for this frame. /// </param> /// <returns></returns> private static Bitmap GetBaseImage( GifFrame previousFrame, GifFrame previousFrameBut1, LogicalScreenDescriptor lsd, GraphicControlExtension gce, ColourTable act ) { #region Get the disposal method of the previous frame read from the GIF stream DisposalMethod previousDisposalMethod; if( previousFrame == null ) { previousDisposalMethod = DisposalMethod.NotSpecified; } else { previousDisposalMethod = previousFrame.GraphicControlExtension.DisposalMethod; } #endregion Bitmap baseImage; int width = lsd.LogicalScreenSize.Width; int height = lsd.LogicalScreenSize.Height; #region paint baseImage switch( previousDisposalMethod ) { case DisposalMethod.DoNotDispose: // pre-populate image with previous frame baseImage = new Bitmap( previousFrame.TheImage ); break; case DisposalMethod.RestoreToBackgroundColour: // pre-populate image with background colour Color backgroundColour ; if( lsd.BackgroundColourIndex == gce.TransparentColourIndex ) { backgroundColour = Color.Empty; } else { // TESTME: background colour index different to transparent colour backgroundColour = act[lsd.BackgroundColourIndex]; } baseImage = new Bitmap( width, height ); for( int y = 0; y < height; y++ ) { for( int x = 0; x < height; x++ ) { baseImage.SetPixel( x, y, backgroundColour ); } } break; case DisposalMethod.RestoreToPrevious: // pre-populate image with previous frame but 1 // TESTME: DisposalMethod.RestoreToPrevious baseImage = new Bitmap( previousFrameBut1.TheImage ); break; default: // DisposalMethod.NotSpecified if( previousFrame == null ) { // this is the first frame so start with an empty bitmap baseImage = new Bitmap( width, height ); } else { // pre-populate image with previous frame // TESTME: DisposalMethod.NotSpecified on 2nd frame or later baseImage = new Bitmap( previousFrame.TheImage ); } break; } #endregion return baseImage; }
private static LogicalScreenDescriptor CheckLogicalScreenDescriptor( Stream s, bool shouldHaveGlobalColourTable) { // check logical screen descriptor LogicalScreenDescriptor lsd = new LogicalScreenDescriptor( s ); Assert.AreEqual( ErrorState.Ok, lsd.ConsolidatedState, "Logical screen descriptor consolidated state" ); Assert.AreEqual( new Size( 2, 2 ), lsd.LogicalScreenSize, "Logical screen size" ); Assert.AreEqual( shouldHaveGlobalColourTable, lsd.HasGlobalColourTable, "Should have global colour table" ); Assert.AreEqual( false, lsd.GlobalColourTableIsSorted, "Global colour table is sorted" ); return lsd; }
public void WikipediaExampleTest() { ReportStart(); _e = new AnimatedGifEncoder(); GifFrame frame = new GifFrame( WikipediaExample.ExpectedBitmap ); frame.Delay = WikipediaExample.DelayTime; _e.AddFrame( frame ); // TODO: some way of creating/testing a UseLocal version of WikipediaExample string fileName = "WikipediaExampleUseGlobal.gif"; _e.WriteToFile( fileName ); Stream s = File.OpenRead( fileName ); int code; // check GIF header GifHeader gh = new GifHeader( s ); Assert.AreEqual( ErrorState.Ok, gh.ConsolidatedState ); // check logical screen descriptor LogicalScreenDescriptor lsd = new LogicalScreenDescriptor( s ); Assert.AreEqual( ErrorState.Ok, lsd.ConsolidatedState ); WikipediaExample.CheckLogicalScreenDescriptor( lsd ); // read global colour table ColourTable gct = new ColourTable( s, WikipediaExample.GlobalColourTableSize ); Assert.AreEqual( ErrorState.Ok, gct.ConsolidatedState ); // cannot compare global colour table as different encoders will // produce difference colour tables. // WikipediaExample.CheckGlobalColourTable( gct ); // check for extension introducer code = ExampleComponent.CallRead( s ); Assert.AreEqual( GifComponent.CodeExtensionIntroducer, code ); // check for app extension label code = ExampleComponent.CallRead( s ); Assert.AreEqual( GifComponent.CodeApplicationExtensionLabel, code ); // check netscape extension ApplicationExtension ae = new ApplicationExtension( s ); Assert.AreEqual( ErrorState.Ok, ae.ConsolidatedState ); NetscapeExtension ne = new NetscapeExtension( ae ); Assert.AreEqual( ErrorState.Ok, ne.ConsolidatedState ); Assert.AreEqual( 0, ne.LoopCount ); // check for extension introducer code = ExampleComponent.CallRead( s ); Assert.AreEqual( GifComponent.CodeExtensionIntroducer, code ); // check for gce label code = ExampleComponent.CallRead( s ); Assert.AreEqual( GifComponent.CodeGraphicControlLabel, code ); // check graphic control extension GraphicControlExtension gce = new GraphicControlExtension( s ); Assert.AreEqual( ErrorState.Ok, gce.ConsolidatedState ); WikipediaExample.CheckGraphicControlExtension( gce ); // check for image separator code = ExampleComponent.CallRead( s ); Assert.AreEqual( GifComponent.CodeImageSeparator, code ); // check for image descriptor ImageDescriptor id = new ImageDescriptor( s ); Assert.AreEqual( ErrorState.Ok, id.ConsolidatedState ); WikipediaExample.CheckImageDescriptor( id ); // read, decode and check image data // Cannot compare encoded LZW data directly as different encoders // will create different colour tables, so even if the bitmaps are // identical, the colour indices will be different int pixelCount = WikipediaExample.FrameSize.Width * WikipediaExample.FrameSize.Height; TableBasedImageData tbid = new TableBasedImageData( s, pixelCount ); for( int y = 0; y < WikipediaExample.LogicalScreenSize.Height; y++ ) { for( int x = 0; x < WikipediaExample.LogicalScreenSize.Width; x++ ) { int i = (y * WikipediaExample.LogicalScreenSize.Width) + x; Assert.AreEqual( WikipediaExample.ExpectedBitmap.GetPixel( x, y ), gct[tbid.Pixels[i]], "X: " + x + ", Y: " + y ); } } // Check for block terminator after image data code = ExampleComponent.CallRead( s ); Assert.AreEqual( 0x00, code ); // check for GIF trailer code = ExampleComponent.CallRead( s ); Assert.AreEqual( GifComponent.CodeTrailer, code ); // check we're at the end of the stream code = ExampleComponent.CallRead( s ); Assert.AreEqual( -1, code ); s.Close(); _d = new GifDecoder( fileName ); _d.Decode(); Assert.AreEqual( ErrorState.Ok, _d.ConsolidatedState ); BitmapAssert.AreEqual( WikipediaExample.ExpectedBitmap, (Bitmap) _d.Frames[0].TheImage, "" ); ReportEnd(); }
/// <summary> /// Gets the base image for this frame. This will be overpainted with /// the pixels for this frame, where they are not transparent. /// </summary> /// <param name="previousFrame"> /// The frame which preceded this frame in the GIF stream. /// Null if this is the first frame in the stream. /// </param> /// <param name="previousFrameBut1"> /// The frame which preceded the previous frame in the GIF stream. /// Null if this is the first or seond frame in the stream. /// </param> /// <param name="lsd"> /// The logical screen descriptor for this GIF stream. /// </param> /// <param name="gce"> /// The graphic control extension for this frame. /// </param> /// <param name="act"> /// The active colour table for this frame. /// </param> /// <returns></returns> private static Bitmap GetBaseImage(GifFrame previousFrame, GifFrame previousFrameBut1, LogicalScreenDescriptor lsd, GraphicControlExtension gce, ColourTable act) { #region Get the disposal method of the previous frame read from the GIF stream DisposalMethod previousDisposalMethod; if (previousFrame == null) { previousDisposalMethod = DisposalMethod.NotSpecified; } else { previousDisposalMethod = previousFrame.GraphicControlExtension.DisposalMethod; } #endregion Bitmap baseImage; int width = lsd.LogicalScreenSize.Width; int height = lsd.LogicalScreenSize.Height; #region paint baseImage switch (previousDisposalMethod) { case DisposalMethod.DoNotDispose: // pre-populate image with previous frame baseImage = new Bitmap(previousFrame.TheImage); break; case DisposalMethod.RestoreToBackgroundColour: // pre-populate image with background colour Color backgroundColour; if (lsd.BackgroundColourIndex == gce.TransparentColourIndex) { backgroundColour = Color.Empty; } else { // TESTME: background colour index different to transparent colour backgroundColour = act[lsd.BackgroundColourIndex]; } baseImage = new Bitmap(width, height); for (int y = 0; y < height; y++) { for (int x = 0; x < height; x++) { baseImage.SetPixel(x, y, backgroundColour); } } break; case DisposalMethod.RestoreToPrevious: // pre-populate image with previous frame but 1 // TESTME: DisposalMethod.RestoreToPrevious baseImage = new Bitmap(previousFrameBut1.TheImage); break; default: // DisposalMethod.NotSpecified if (previousFrame == null) { // this is the first frame so start with an empty bitmap baseImage = new Bitmap(width, height); } else { // pre-populate image with previous frame // TESTME: DisposalMethod.NotSpecified on 2nd frame or later baseImage = new Bitmap(previousFrame.TheImage); } break; } #endregion return(baseImage); }
/// <summary> /// Sets the pixels of the decoded image. /// </summary> /// <param name="imageData"> /// Table based image data containing the indices within the active /// colour table of the colours of the pixels in this frame. /// </param> /// <param name="lsd"> /// The logical screen descriptor for the GIF stream. /// </param> /// <param name="id"> /// The image descriptor for this frame. /// </param> /// <param name="activeColourTable"> /// The colour table to use with this frame - either the global colour /// table or a local colour table. /// </param> /// <param name="gce"> /// The graphic control extension, if any, which precedes this image in /// the input stream. /// </param> /// <param name="previousFrame"> /// The frame which precedes this one in the GIF stream, if present. /// </param> /// <param name="previousFrameBut1"> /// The frame which precedes the frame before this one in the GIF stream, /// if present. /// </param> /// <param name="status"> /// GifComponentStatus containing any errors which occurred during the /// creation of the bitmap. /// </param> private static Bitmap CreateBitmap(TableBasedImageData imageData, LogicalScreenDescriptor lsd, ImageDescriptor id, ColourTable activeColourTable, GraphicControlExtension gce, GifFrame previousFrame, GifFrame previousFrameBut1, out GifComponentStatus status) { status = new GifComponentStatus(ErrorState.Ok, ""); Color[] pixelsForThisFrame = new Color[lsd.LogicalScreenSize.Width * lsd.LogicalScreenSize.Height]; Bitmap baseImage = GetBaseImage(previousFrame, previousFrameBut1, lsd, gce, activeColourTable); // copy each source line to the appropriate place in the destination int pass = 1; int interlaceRowIncrement = 8; int interlaceRowNumber = 0; // the row of pixels we're currently // setting in an interlaced image. for (int i = 0; i < id.Size.Height; i++) { int pixelRowNumber = i; if (id.IsInterlaced) { #region work out the pixel row we're setting for an interlaced image if (interlaceRowNumber >= id.Size.Height) { pass++; switch (pass) { case 2: interlaceRowNumber = 4; break; case 3: interlaceRowNumber = 2; interlaceRowIncrement = 4; break; case 4: interlaceRowNumber = 1; interlaceRowIncrement = 2; break; } } #endregion pixelRowNumber = interlaceRowNumber; interlaceRowNumber += interlaceRowIncrement; } // Colour in the pixels for this row pixelRowNumber += id.Position.Y; if (pixelRowNumber < lsd.LogicalScreenSize.Height) { int k = pixelRowNumber * lsd.LogicalScreenSize.Width; int dx = k + id.Position.X; // start of line in dest int dlim = dx + id.Size.Width; // end of dest line if ((k + lsd.LogicalScreenSize.Width) < dlim) { // TESTME: CreateBitmap - past dest edge dlim = k + lsd.LogicalScreenSize.Width; // past dest edge } int sx = i * id.Size.Width; // start of line in source while (dx < dlim) { // map color and insert in destination int indexInColourTable = (int)imageData.Pixels[sx++]; // Set this pixel's colour if its index isn't the // transparent colour index, or if this frame doesn't // have a transparent colour. Color c; if (gce.HasTransparentColour && indexInColourTable == gce.TransparentColourIndex) { c = Color.Empty; // transparent pixel } else { if (indexInColourTable < activeColourTable.Length) { c = activeColourTable[indexInColourTable]; } else { // TESTME: CreateBitmap - BadColourIndex c = Color.Black; string message = "Colour index: " + indexInColourTable + ", colour table length: " + activeColourTable.Length + " (" + dx + "," + pixelRowNumber + ")"; status = new GifComponentStatus(ErrorState.BadColourIndex, message); } } pixelsForThisFrame[dx] = c; dx++; } } } return(CreateBitmap(baseImage, pixelsForThisFrame)); }
public GifFrame(Stream inputStream, LogicalScreenDescriptor lsd, ColourTable gct, GraphicControlExtension gce, GifFrame previousFrame, GifFrame previousFrameBut1, bool xmlDebugging) : base(xmlDebugging) { #region guard against null arguments if (lsd == null) { throw new ArgumentNullException("lsd"); } if (gce == null) { SetStatus(ErrorState.NoGraphicControlExtension, ""); // use a default GCE gce = new GraphicControlExtension(GraphicControlExtension.ExpectedBlockSize, DisposalMethod.NotSpecified, false, false, 100, 0); } #endregion int transparentColourIndex = gce.TransparentColourIndex; ImageDescriptor imageDescriptor = new ImageDescriptor(inputStream, XmlDebugging); WriteDebugXmlNode(imageDescriptor.DebugXmlReader); #region determine the colour table to use for this frame Color backgroundColour = Color.FromArgb(0); // TODO: is this the right background colour? // TODO: use backgroundColourIndex from the logical screen descriptor? ColourTable activeColourTable; if (imageDescriptor.HasLocalColourTable) { _localColourTable = new ColourTable(inputStream, imageDescriptor.LocalColourTableSize, XmlDebugging); WriteDebugXmlNode(_localColourTable.DebugXmlReader); activeColourTable = _localColourTable; // make local table active } else { if (gct == null) { // We have neither local nor global colour table, so we // won't be able to decode this frame. Bitmap emptyBitmap = new Bitmap(lsd.LogicalScreenSize.Width, lsd.LogicalScreenSize.Height); _image = emptyBitmap; _delay = gce.DelayTime; SetStatus(ErrorState.FrameHasNoColourTable, ""); return; } activeColourTable = gct; // make global table active if (lsd.BackgroundColourIndex == transparentColourIndex) { backgroundColour = Color.FromArgb(0); } } #endregion // decode pixel data int pixelCount = imageDescriptor.Size.Width * imageDescriptor.Size.Height; TableBasedImageData indexedPixels = new TableBasedImageData(inputStream, pixelCount, XmlDebugging); WriteDebugXmlNode(indexedPixels.DebugXmlReader); if (indexedPixels.Pixels.Count == 0) { // TESTME: constructor - indexedPixels.Pixels.Count == 0 Bitmap emptyBitmap = new Bitmap(lsd.LogicalScreenSize.Width, lsd.LogicalScreenSize.Height); _image = emptyBitmap; _delay = gce.DelayTime; SetStatus(ErrorState.FrameHasNoImageData, ""); WriteDebugXmlFinish(); return; } // Skip any remaining blocks up to the next block terminator (in // case there is any surplus data before the next frame) SkipBlocks(inputStream); _indexedPixels = indexedPixels; _extension = gce; if (gce != null) { _delay = gce.DelayTime; } _imageDescriptor = imageDescriptor; _backgroundColour = backgroundColour; GifComponentStatus status; _image = CreateBitmap(indexedPixels, lsd, imageDescriptor, activeColourTable, gce, previousFrame, previousFrameBut1, out status); WriteDebugXmlFinish(); }
/// <summary> /// Writes a Logical Screen Descriptor to the supplied stream. /// Also writes a global colour table if required. /// </summary> /// <param name="outputStream"> /// The stream to write to. /// </param> private void WriteLogicalScreenDescriptor( Stream outputStream ) { bool hasGlobalColourTable = _strategy == ColourTableStrategy.UseGlobal; int colourResolution = 7; // TODO: parameterise colourResolution? bool globalColourTableIsSorted = false; // Sorting of colour tables is not currently supported int backgroundColorIndex = 0; // TODO: parameterise backgroundColourIndex? int pixelAspectRatio = 0; // TODO: parameterise pixelAspectRatio? if( _strategy == ColourTableStrategy.UseGlobal ) { if( _quantizerType == QuantizerType.UseSuppliedPalette ) { // use supplied palette _globalColourTable = new ColourTable(); string buildColourTableCounterText = "Building colour table from supplied palette"; AddCounter( buildColourTableCounterText, _palette.Count ); int paletteIndex = 0; foreach( Color c in _palette ) { _globalColourTable.Add( c ); MyProgressCounters[buildColourTableCounterText].Value = paletteIndex; paletteIndex++; } _globalColourTable.Pad(); RemoveCounter( buildColourTableCounterText ); } else { // Analyse the pixels in all the images to build the // global colour table. Collection<Image> images = new Collection<Image>(); foreach( GifFrame thisFrame in _frames ) { Image thisImage = thisFrame.TheImage; images.Add( thisImage ); } _pixelAnalysis = new PixelAnalysis( images ); _pixelAnalysis.ColourQuality = _quality; _pixelAnalysis.Analyse(); _globalColourTable = _pixelAnalysis.ColourTable; } LogicalScreenDescriptor lsd = new LogicalScreenDescriptor( _logicalScreenSize, hasGlobalColourTable, colourResolution, globalColourTableIsSorted, _globalColourTable.SizeBits, backgroundColorIndex, pixelAspectRatio ); lsd.WriteToStream( outputStream ); _globalColourTable.WriteToStream( outputStream ); } else { LogicalScreenDescriptor lsd = new LogicalScreenDescriptor( _logicalScreenSize, hasGlobalColourTable, colourResolution, globalColourTableIsSorted, 7, backgroundColorIndex, pixelAspectRatio ); lsd.WriteToStream( outputStream ); } }
public GifFrame( Stream inputStream, LogicalScreenDescriptor lsd, ColourTable gct, GraphicControlExtension gce, GifFrame previousFrame, GifFrame previousFrameBut1, bool xmlDebugging ) : base( xmlDebugging ) { #region guard against null arguments if( lsd == null ) { throw new ArgumentNullException( "lsd" ); } if( gce == null ) { SetStatus( ErrorState.NoGraphicControlExtension, "" ); // use a default GCE gce = new GraphicControlExtension( GraphicControlExtension.ExpectedBlockSize, DisposalMethod.NotSpecified, false, false, 100, 0 ); } #endregion int transparentColourIndex = gce.TransparentColourIndex; ImageDescriptor imageDescriptor = new ImageDescriptor( inputStream, XmlDebugging ); WriteDebugXmlNode( imageDescriptor.DebugXmlReader ); #region determine the colour table to use for this frame Color backgroundColour = Color.FromArgb( 0 ); // TODO: is this the right background colour? // TODO: use backgroundColourIndex from the logical screen descriptor? ColourTable activeColourTable; if( imageDescriptor.HasLocalColourTable ) { _localColourTable = new ColourTable( inputStream, imageDescriptor.LocalColourTableSize, XmlDebugging ); WriteDebugXmlNode( _localColourTable.DebugXmlReader ); activeColourTable = _localColourTable; // make local table active } else { if( gct == null ) { // We have neither local nor global colour table, so we // won't be able to decode this frame. Bitmap emptyBitmap = new Bitmap( lsd.LogicalScreenSize.Width, lsd.LogicalScreenSize.Height ); _image = emptyBitmap; _delay = gce.DelayTime; SetStatus( ErrorState.FrameHasNoColourTable, "" ); return; } activeColourTable = gct; // make global table active if( lsd.BackgroundColourIndex == transparentColourIndex ) { backgroundColour = Color.FromArgb( 0 ); } } #endregion // decode pixel data int pixelCount = imageDescriptor.Size.Width * imageDescriptor.Size.Height; TableBasedImageData indexedPixels = new TableBasedImageData( inputStream, pixelCount, XmlDebugging ); WriteDebugXmlNode( indexedPixels.DebugXmlReader ); if( indexedPixels.Pixels.Count == 0 ) { // TESTME: constructor - indexedPixels.Pixels.Count == 0 Bitmap emptyBitmap = new Bitmap( lsd.LogicalScreenSize.Width, lsd.LogicalScreenSize.Height ); _image = emptyBitmap; _delay = gce.DelayTime; SetStatus( ErrorState.FrameHasNoImageData, "" ); WriteDebugXmlFinish(); return; } // Skip any remaining blocks up to the next block terminator (in // case there is any surplus data before the next frame) SkipBlocks( inputStream ); _indexedPixels = indexedPixels; _extension = gce; if( gce != null ) { _delay = gce.DelayTime; } _imageDescriptor = imageDescriptor; _backgroundColour = backgroundColour; GifComponentStatus status; _image = CreateBitmap( indexedPixels, lsd, imageDescriptor, activeColourTable, gce, previousFrame, previousFrameBut1, out status ); WriteDebugXmlFinish(); }
public void ConstructorTestTooWide() { ReportStart(); int width = ushort.MaxValue + 1; int height = 20; Size screenSize = new Size( width, height ); bool hasGlobalColourTable = true; int colourResolution = 6; bool colourTableIsSorted = false; int globalColourTableSizeBits = 3; int backgroundColourIndex = 12; int pixelAspectRatio = 2; try { _lsd = new LogicalScreenDescriptor( screenSize, hasGlobalColourTable, colourResolution, colourTableIsSorted, globalColourTableSizeBits, backgroundColourIndex, pixelAspectRatio ); } catch( ArgumentException ex ) { string message = "Logical screen width cannot be more than " + ushort.MaxValue + ". " + "Supplied value: " + width; StringAssert.Contains( message, ex.Message ); Assert.AreEqual( "logicalScreenSize", ex.ParamName ); ReportEnd(); throw; } }
/// <summary> /// Sets the pixels of the decoded image. /// </summary> /// <param name="imageData"> /// Table based image data containing the indices within the active /// colour table of the colours of the pixels in this frame. /// </param> /// <param name="lsd"> /// The logical screen descriptor for the GIF stream. /// </param> /// <param name="id"> /// The image descriptor for this frame. /// </param> /// <param name="activeColourTable"> /// The colour table to use with this frame - either the global colour /// table or a local colour table. /// </param> /// <param name="gce"> /// The graphic control extension, if any, which precedes this image in /// the input stream. /// </param> /// <param name="previousFrame"> /// The frame which precedes this one in the GIF stream, if present. /// </param> /// <param name="previousFrameBut1"> /// The frame which precedes the frame before this one in the GIF stream, /// if present. /// </param> /// <param name="status"> /// GifComponentStatus containing any errors which occurred during the /// creation of the bitmap. /// </param> private static Bitmap CreateBitmap( TableBasedImageData imageData, LogicalScreenDescriptor lsd, ImageDescriptor id, ColourTable activeColourTable, GraphicControlExtension gce, GifFrame previousFrame, GifFrame previousFrameBut1, out GifComponentStatus status ) { status = new GifComponentStatus( ErrorState.Ok, "" ); Color[] pixelsForThisFrame = new Color[lsd.LogicalScreenSize.Width * lsd.LogicalScreenSize.Height]; Bitmap baseImage = GetBaseImage( previousFrame, previousFrameBut1, lsd, gce, activeColourTable ); // copy each source line to the appropriate place in the destination int pass = 1; int interlaceRowIncrement = 8; int interlaceRowNumber = 0; // the row of pixels we're currently // setting in an interlaced image. for( int i = 0; i < id.Size.Height; i++) { int pixelRowNumber = i; if( id.IsInterlaced ) { #region work out the pixel row we're setting for an interlaced image if( interlaceRowNumber >= id.Size.Height ) { pass++; switch( pass ) { case 2 : interlaceRowNumber = 4; break; case 3 : interlaceRowNumber = 2; interlaceRowIncrement = 4; break; case 4 : interlaceRowNumber = 1; interlaceRowIncrement = 2; break; } } #endregion pixelRowNumber = interlaceRowNumber; interlaceRowNumber += interlaceRowIncrement; } // Colour in the pixels for this row pixelRowNumber += id.Position.Y; if( pixelRowNumber < lsd.LogicalScreenSize.Height ) { int k = pixelRowNumber * lsd.LogicalScreenSize.Width; int dx = k + id.Position.X; // start of line in dest int dlim = dx + id.Size.Width; // end of dest line if( (k + lsd.LogicalScreenSize.Width) < dlim ) { // TESTME: CreateBitmap - past dest edge dlim = k + lsd.LogicalScreenSize.Width; // past dest edge } int sx = i * id.Size.Width; // start of line in source while (dx < dlim) { // map color and insert in destination int indexInColourTable = (int) imageData.Pixels[sx++]; // Set this pixel's colour if its index isn't the // transparent colour index, or if this frame doesn't // have a transparent colour. Color c; if( gce.HasTransparentColour && indexInColourTable == gce.TransparentColourIndex ) { c = Color.Empty; // transparent pixel } else { if( indexInColourTable < activeColourTable.Length ) { c = activeColourTable[indexInColourTable]; } else { // TESTME: CreateBitmap - BadColourIndex c = Color.Black; string message = "Colour index: " + indexInColourTable + ", colour table length: " + activeColourTable.Length + " (" + dx + "," + pixelRowNumber + ")"; status = new GifComponentStatus( ErrorState.BadColourIndex, message ); } } pixelsForThisFrame[dx] = c; dx++; } } } return CreateBitmap( baseImage, pixelsForThisFrame ); }
public void WriteToStreamTest() { ReportStart(); Size screenSize = new Size( 12, 4 ); bool hasGlobalColourTable = false; int colourResolution = 3; bool globalColourTableIsSorted = true; int globalColourTableSizeBits = 4; int backgroundColourIndex = 2; int pixelAspectRatio = 1; _lsd = new LogicalScreenDescriptor( screenSize, hasGlobalColourTable, colourResolution, globalColourTableIsSorted, globalColourTableSizeBits, backgroundColourIndex, pixelAspectRatio ); MemoryStream s = new MemoryStream(); _lsd.WriteToStream( s ); s.Seek( 0, SeekOrigin.Begin ); _lsd = new LogicalScreenDescriptor( s ); Assert.AreEqual( ErrorState.Ok, _lsd.ConsolidatedState ); Assert.AreEqual( screenSize, _lsd.LogicalScreenSize ); Assert.AreEqual( hasGlobalColourTable, _lsd.HasGlobalColourTable ); Assert.AreEqual( colourResolution, _lsd.ColourResolution ); Assert.AreEqual( globalColourTableIsSorted, _lsd.GlobalColourTableIsSorted ); Assert.AreEqual( globalColourTableSizeBits, _lsd.GlobalColourTableSizeBits ); Assert.AreEqual( backgroundColourIndex, _lsd.BackgroundColourIndex ); Assert.AreEqual( pixelAspectRatio, _lsd.PixelAspectRatio ); ReportEnd(); }
/// <summary> /// Checks whether the supplied logical screen descriptor matches that /// returned by this class. /// </summary> /// <param name="lsd"></param> internal static void CheckLogicalScreenDescriptor( LogicalScreenDescriptor lsd ) { Assert.AreEqual( BackgroundColourIndex, lsd.BackgroundColourIndex, "BackgroundColourIndex" ); Assert.AreEqual( ColourResolution, lsd.ColourResolution, "ColourResolution" ); Assert.AreEqual( GlobalColourTableSize, lsd.GlobalColourTableSize, "GlobalColourTableSize" ); Assert.AreEqual( GlobalColourTableSizeBits, lsd.GlobalColourTableSizeBits, "GlobalColourTableSizeBits" ); Assert.AreEqual( HasGlobalColourTable, lsd.HasGlobalColourTable, "HasGlobalColourTable" ); Assert.AreEqual( LogicalScreenSize, lsd.LogicalScreenSize, "LogicalScreenSize" ); Assert.AreEqual( PixelAspectRatio, lsd.PixelAspectRatio, "PixelAspectRatio" ); Assert.AreEqual( GlobalColourTableIsSorted, lsd.GlobalColourTableIsSorted, "GlobalColourTableIsSorted" ); }
private void DecodeRotatingGlobe( bool xmlDebugging ) { string fileName = @"images\globe\spinning globe better 200px transparent background.gif"; _decoder = new GifDecoder( fileName, xmlDebugging ); WriteMessage( "Started decoding. XmlDebugging=" + xmlDebugging ); _decoder.Decode(); WriteMessage( "Finished decoding. XmlDebugging=" + xmlDebugging ); if( xmlDebugging ) { Assert.AreEqual( ExpectedDebugXml, _decoder.DebugXml ); } _lsd = _decoder.LogicalScreenDescriptor; int expectedColourTableSize = 64; Assert.AreEqual( ErrorState.Ok, _decoder.ConsolidatedState ); Assert.AreEqual( "GIF", _decoder.Header.Signature ); Assert.AreEqual( "89a", _decoder.Header.Version ); Assert.IsNotNull( _decoder.GlobalColourTable ); Assert.AreEqual( true, _lsd.HasGlobalColourTable ); Assert.AreEqual( expectedColourTableSize, _lsd.GlobalColourTableSize ); Assert.AreEqual( expectedColourTableSize, _decoder.GlobalColourTable.Length ); Assert.AreEqual( 2, _lsd.ColourResolution ); Assert.AreEqual( Color.FromArgb( 255, 255, 255, 255 ), _decoder.BackgroundColour ); Assert.AreEqual( 63, _lsd.BackgroundColourIndex ); Assert.AreEqual( false, _lsd.GlobalColourTableIsSorted ); Assert.AreEqual( 200, _lsd.LogicalScreenSize.Width ); Assert.AreEqual( 191, _lsd.LogicalScreenSize.Height ); Assert.AreEqual( 0, _lsd.PixelAspectRatio ); Assert.AreEqual( 0, _decoder.NetscapeExtension.LoopCount ); Assert.AreEqual( ErrorState.Ok, _decoder.ErrorState ); Assert.AreEqual( 20, _decoder.Frames.Count ); int frameNumber = 0; string frameId; foreach( GifFrame thisFrame in _decoder.Frames ) { frameId = "Frame " + frameNumber; Assert.IsNull( thisFrame.LocalColourTable, frameId ); Assert.AreEqual( 10, thisFrame.Delay, frameId ); #region image descriptor tests ImageDescriptor descriptor = thisFrame.ImageDescriptor; Assert.AreEqual( false, descriptor.HasLocalColourTable, frameId ); Assert.AreEqual( 2, descriptor.LocalColourTableSize, frameId ); Assert.AreEqual( 200, descriptor.Size.Width, frameId ); Assert.AreEqual( 191, descriptor.Size.Height, frameId ); Assert.AreEqual( 0, descriptor.Position.X, frameId ); Assert.AreEqual( 0, descriptor.Position.Y, frameId ); Assert.AreEqual( false, descriptor.IsInterlaced, frameId ); Assert.AreEqual( false, descriptor.IsSorted, frameId ); #endregion #region graphic control extension tests GraphicControlExtension gce = thisFrame.GraphicControlExtension; Assert.AreEqual( 4, gce.BlockSize, frameId ); Assert.AreEqual( 10, gce.DelayTime, frameId ); if( frameNumber == 19 ) { Assert.AreEqual( DisposalMethod.DoNotDispose, gce.DisposalMethod, frameId ); } else { Assert.AreEqual( DisposalMethod.RestoreToBackgroundColour, gce.DisposalMethod, frameId ); } Assert.AreEqual( 63, gce.TransparentColourIndex, frameId ); Assert.IsTrue( gce.HasTransparentColour, frameId ); Assert.IsFalse( gce.ExpectsUserInput, frameId ); #endregion frameNumber++; } CompareFrames( fileName ); }