/// <summary> /// Default constructor. /// Sets repeat count to 0 (repeat indefinitely) /// Sets colour table strategy to UseGlobal /// Sets image quantization quality to 10. /// Sets quantizer type to NeuQuant. /// Screen size defaults to size of first frame. /// </summary> public AnimatedGifEncoder() { _frames = new Collection <GifFrame>(); _strategy = ColourTableStrategy.UseGlobal; _quality = 10; _quantizerType = QuantizerType.NeuQuant; _logicalScreenSize = Size.Empty; _palette = new Palette(); }
/// <summary> /// Tests the AnimatedGifEncoder and the encoded GIF file it produces /// using the supplied parameters as property values. /// </summary> private void TestAnimatedGifEncoder(ColourTableStrategy strategy, int colourQuality, Size logicalScreenSize) { _e = new AnimatedGifEncoder(); // Check default properties set by constructor. Assert.AreEqual(ColourTableStrategy.UseGlobal, _e.ColourTableStrategy, "Colour table strategy set by constructor"); Assert.AreEqual(10, _e.SamplingFactor, "Colour quantization quality set by constructor"); Assert.AreEqual(Size.Empty, _e.LogicalScreenSize, "Logical screen size set by constructor"); _e.ColourTableStrategy = strategy; _e.SamplingFactor = colourQuality; _e.LogicalScreenSize = logicalScreenSize; // Check property set/gets Assert.AreEqual(strategy, _e.ColourTableStrategy, "Colour table strategy property set/get"); Assert.AreEqual(colourQuality, _e.SamplingFactor, "Colour quantization quality property set/get"); Assert.AreEqual(logicalScreenSize, _e.LogicalScreenSize, "Logical screen size property get/set"); foreach (GifFrame thisFrame in _frames) { _e.AddFrame(thisFrame); } StackTrace t = new StackTrace(); StackFrame f = t.GetFrame(1); string fileName = "Checks." + this.GetType().Name + "." + f.GetMethod().Name + ".gif"; _e.WriteToFile(fileName); Stream s = File.OpenRead(fileName); // global info CheckGifHeader(s); bool shouldHaveGlobalColourTable = (strategy == ColourTableStrategy.UseGlobal); LogicalScreenDescriptor lsd = CheckLogicalScreenDescriptor(s, shouldHaveGlobalColourTable); // Only check the global colour table if there should be one ColourTable gct = null; if (shouldHaveGlobalColourTable) { gct = CheckColourTable(s, lsd.GlobalColourTableSize); } CheckExtensionIntroducer(s); CheckAppExtensionLabel(s); CheckNetscapeExtension(s, 0); CheckFrame(s, gct, Bitmap1()); CheckFrame(s, gct, Bitmap2()); // end of image data CheckGifTrailer(s); CheckEndOfStream(s); s.Close(); // Check the file using the decoder _d = new GifDecoder(fileName); _d.Decode(); Assert.AreEqual(ErrorState.Ok, _d.ConsolidatedState, "Decoder consolidated state"); Assert.AreEqual(2, _d.Frames.Count, "Decoder frame count"); Assert.AreEqual(shouldHaveGlobalColourTable, _d.LogicalScreenDescriptor.HasGlobalColourTable, "Should have global colour table"); Assert.AreEqual(logicalScreenSize, _d.LogicalScreenDescriptor.LogicalScreenSize, "Decoder logical screen size"); BitmapAssert.AreEqual(Bitmap1(), (Bitmap)_d.Frames[0].TheImage, "frame 0"); BitmapAssert.AreEqual(Bitmap2(), (Bitmap)_d.Frames[1].TheImage, "frame 1"); bool shouldHaveLocalColourTable = !shouldHaveGlobalColourTable; Assert.AreEqual(shouldHaveLocalColourTable, _d.Frames[0].ImageDescriptor.HasLocalColourTable, "Frame 0 has local colour table"); Assert.AreEqual(shouldHaveLocalColourTable, _d.Frames[1].ImageDescriptor.HasLocalColourTable, "Frame 0 has local colour table"); }
private void TestUseSuppliedPalette(ColourTableStrategy strategy) { string globalLocal = strategy == ColourTableStrategy.UseGlobal ? "Global" : "Local"; // First, create and check a series of single-frame GIFs, one for // each of the available colour tables. string[] files = Directory.GetFiles(@"ColourTables", "*.act"); foreach (string act in files) { string actFileWithoutExtension = Path.GetFileNameWithoutExtension(act); _e = new AnimatedGifEncoder(); if (strategy == ColourTableStrategy.UseGlobal) { _e.Palette = Palette.FromFile(act); Assert.AreEqual(ColourTableStrategy.UseGlobal, _e.ColourTableStrategy); // QuantizerType should default to UseSuppliedPalette when // the encoder's Palette property is set. Assert.AreEqual(QuantizerType.UseSuppliedPalette, _e.QuantizerType); } else { _e.ColourTableStrategy = ColourTableStrategy.UseLocal; Assert.AreEqual(ColourTableStrategy.UseLocal, _e.ColourTableStrategy); _e.QuantizerType = QuantizerType.UseSuppliedPalette; Assert.AreEqual(QuantizerType.UseSuppliedPalette, _e.QuantizerType); } GifFrame frame = new GifFrame(Image.FromFile(@"images\smiley.bmp")); if (strategy == ColourTableStrategy.UseLocal) { frame.Palette = Palette.FromFile(act); } _e.AddFrame(frame); string fileName = "AnimatedGifEncoderTest.UseSuppliedPalette" + globalLocal + "-" + actFileWithoutExtension + ".gif"; _e.WriteToFile(fileName); _d = new GifDecoder(fileName, true); _d.Decode(); Assert.AreEqual(ErrorState.Ok, _d.ConsolidatedState, actFileWithoutExtension); Assert.AreEqual(1, _d.Frames.Count, actFileWithoutExtension); if (strategy == ColourTableStrategy.UseGlobal) { Assert.AreEqual(true, _d.LogicalScreenDescriptor.HasGlobalColourTable, actFileWithoutExtension); Assert.IsNotNull(_d.GlobalColourTable, actFileWithoutExtension); } else { Assert.AreEqual(false, _d.LogicalScreenDescriptor.HasGlobalColourTable, actFileWithoutExtension); Assert.IsNull(_d.GlobalColourTable, actFileWithoutExtension); } string expectedFileName = @"images\Smiley\Smiley" + "-" + actFileWithoutExtension + ".bmp"; Image expected = Image.FromFile(expectedFileName); ImageAssert.AreEqual(expected, _d.Frames[0].TheImage, expectedFileName); } // now encode a multi-frame animation with a user-supplied palette _d = new GifDecoder(@"images\globe\spinning globe better 200px transparent background.gif"); _d.Decode(); _e = new AnimatedGifEncoder(); _e.QuantizerType = QuantizerType.UseSuppliedPalette; _e.Palette = Palette.FromFile(@"ColourTables\C64.act"); foreach (GifFrame f in _d.Frames) { _e.AddFrame(f); } string globeFileName = "AnimatedGifEncoderTest.UseSuppliedPalette" + globalLocal + ".gif"; _e.WriteToFile(globeFileName); _d = new GifDecoder(globeFileName); _d.Decode(); Assert.AreEqual(ErrorState.Ok, _d.ConsolidatedState); Assert.AreEqual(_e.Frames.Count, _d.Frames.Count); }
public void ExportAnimationAsGif() { var error = false; if (CurrentShownSpriteSheet == null) { Messager.ShowMessage(Messager.Mode.Exception, "No spritesheet loaded! Please load a sprite sheet first by clicking the button Import Spritesheet on topbar"); return; } if (CurrentShownAnimation != null) { if (CurrentShownAnimation.Frames.Count == 0) { Messager.ShowMessage(Messager.Mode.Exception, "No frames to export. The animation is empty!"); return; } var path = ShowCompleteSaveToDialog("Choose location to save GIF", @"Animated GIF(.gif)|*.gif"); if (path == null) { return; } if (path.Length == 0) { Messager.ShowMessage(Messager.Mode.Exception, string.Format("Nothing exported. The path is invalid or empty!")); return; } try { Size frameSize = Size.Empty; Point offSetMin = Point.Empty; Point offSetMax = Point.Empty; foreach (var frame in CurrentShownAnimation.Frames) { if (frame.SpriteFrame.Width > frameSize.Width) { frameSize.Width = frame.SpriteFrame.Width; } if (frame.SpriteFrame.Height > frameSize.Height) { frameSize.Height = frame.SpriteFrame.Height; } } foreach (var frame in CurrentShownAnimation.Frames) { if (frame.OffSetX < offSetMin.X) { offSetMin.X = frame.OffSetX; } if (frame.OffSetY < offSetMin.Y) { offSetMin.Y = frame.OffSetY; } if (frame.OffSetX > offSetMax.X) { offSetMax.X = frame.OffSetX; } if (frame.OffSetY > offSetMax.Y) { offSetMax.Y = frame.OffSetY; } } frameSize.Width += Math.Abs(offSetMin.X) + offSetMax.X; frameSize.Height += Math.Abs(offSetMin.Y) + offSetMax.Y; frameSize.Width = frameSize.Width > frameSize.Height ? frameSize.Width : frameSize.Height; frameSize.Height = frameSize.Width; int repeatCount = CurrentShownAnimation.Loop ? 0 : 1; ColourTableStrategy strategy = ColourTableStrategy.UseGlobal; int quality = 10; AnimatedGifEncoder animatedGifEncoder = new AnimatedGifEncoder { LogicalScreenSize = frameSize, RepeatCount = repeatCount, ColourTableStrategy = strategy, SamplingFactor = quality, }; var texture = CurrentShownSpriteSheet.Texture; animatedGifEncoder.Transparent = Color.Black; foreach (var frame in CurrentShownAnimation.Frames) { Bitmap frameBitMap = texture.Surface.ToBitmap(frame.SpriteFrame.Region); GifFrame gifFrame = new GifFrame(frameBitMap); gifFrame.Delay = 100 * ((int)(frame.FrameDuration)); if (gifFrame.Delay < 1) { gifFrame.Delay = 1; } gifFrame.Position = new Point((int)((frameSize.Width / 2f - frame.SpriteFrame.Width / 2f) + frame.OffSetX), (int)((frameSize.Height / 2f - frame.SpriteFrame.Height / 2f) + frame.OffSetY)); animatedGifEncoder.AddFrame(gifFrame); } animatedGifEncoder.WriteToFile(path); animatedGifEncoder.Dispose(); } catch (Exception ex) { error = true; Messager.ShowMessage(Messager.Mode.Exception, string.Format("Error on exporting GIF: {0}", ex.Message)); Vortex.Debugging.Log.Error(ex, "Error on exporting GIF"); } finally { if (!error) { Messager.ShowMessage(Messager.Mode.Message, "GIF created successfully!"); } } } else { Messager.ShowMessage(Messager.Mode.Exception, "No animations to export!"); } }
/// <summary> /// Tests the AnimatedGifEncoder and the encoded GIF file it produces /// using the supplied parameters as property values. /// </summary> private void TestAnimatedGifEncoder( ColourTableStrategy strategy, int colourQuality, Size logicalScreenSize) { _e = new AnimatedGifEncoder(); // Check default properties set by constructor. Assert.AreEqual( ColourTableStrategy.UseGlobal, _e.ColourTableStrategy, "Colour table strategy set by constructor" ); Assert.AreEqual( 10, _e.SamplingFactor, "Colour quantization quality set by constructor" ); Assert.AreEqual( Size.Empty, _e.LogicalScreenSize, "Logical screen size set by constructor" ); _e.ColourTableStrategy = strategy; _e.SamplingFactor = colourQuality; _e.LogicalScreenSize = logicalScreenSize; // Check property set/gets Assert.AreEqual( strategy, _e.ColourTableStrategy, "Colour table strategy property set/get" ); Assert.AreEqual( colourQuality, _e.SamplingFactor, "Colour quantization quality property set/get" ); Assert.AreEqual( logicalScreenSize, _e.LogicalScreenSize, "Logical screen size property get/set" ); foreach( GifFrame thisFrame in _frames ) { _e.AddFrame( thisFrame ); } StackTrace t = new StackTrace(); StackFrame f = t.GetFrame( 1 ); string fileName = "Checks." + this.GetType().Name + "." + f.GetMethod().Name + ".gif"; _e.WriteToFile( fileName ); Stream s = File.OpenRead( fileName ); // global info CheckGifHeader( s ); bool shouldHaveGlobalColourTable = (strategy == ColourTableStrategy.UseGlobal); LogicalScreenDescriptor lsd = CheckLogicalScreenDescriptor( s, shouldHaveGlobalColourTable ); // Only check the global colour table if there should be one ColourTable gct = null; if( shouldHaveGlobalColourTable ) { gct = CheckColourTable( s, lsd.GlobalColourTableSize ); } CheckExtensionIntroducer( s ); CheckAppExtensionLabel( s ); CheckNetscapeExtension( s, 0 ); CheckFrame( s, gct, Bitmap1() ); CheckFrame( s, gct, Bitmap2() ); // end of image data CheckGifTrailer( s ); CheckEndOfStream( s ); s.Close(); // Check the file using the decoder _d = new GifDecoder( fileName ); _d.Decode(); Assert.AreEqual( ErrorState.Ok, _d.ConsolidatedState, "Decoder consolidated state" ); Assert.AreEqual( 2, _d.Frames.Count, "Decoder frame count" ); Assert.AreEqual( shouldHaveGlobalColourTable, _d.LogicalScreenDescriptor.HasGlobalColourTable, "Should have global colour table" ); Assert.AreEqual( logicalScreenSize, _d.LogicalScreenDescriptor.LogicalScreenSize, "Decoder logical screen size" ); BitmapAssert.AreEqual( Bitmap1(), (Bitmap) _d.Frames[0].TheImage, "frame 0" ); BitmapAssert.AreEqual( Bitmap2(), (Bitmap) _d.Frames[1].TheImage, "frame 1" ); bool shouldHaveLocalColourTable = !shouldHaveGlobalColourTable; Assert.AreEqual( shouldHaveLocalColourTable, _d.Frames[0].ImageDescriptor.HasLocalColourTable, "Frame 0 has local colour table" ); Assert.AreEqual( shouldHaveLocalColourTable, _d.Frames[1].ImageDescriptor.HasLocalColourTable, "Frame 0 has local colour table" ); }
private void TestUseSuppliedPalette( ColourTableStrategy strategy ) { string globalLocal = strategy == ColourTableStrategy.UseGlobal ? "Global" : "Local"; // First, create and check a series of single-frame GIFs, one for // each of the available colour tables. string[] files = Directory.GetFiles( @"ColourTables", "*.act" ); foreach( string act in files ) { string actFileWithoutExtension = Path.GetFileNameWithoutExtension( act ); _e = new AnimatedGifEncoder(); if( strategy == ColourTableStrategy.UseGlobal ) { _e.Palette = Palette.FromFile( act ); Assert.AreEqual( ColourTableStrategy.UseGlobal, _e.ColourTableStrategy ); // QuantizerType should default to UseSuppliedPalette when // the encoder's Palette property is set. Assert.AreEqual( QuantizerType.UseSuppliedPalette, _e.QuantizerType ); } else { _e.ColourTableStrategy = ColourTableStrategy.UseLocal; Assert.AreEqual( ColourTableStrategy.UseLocal, _e.ColourTableStrategy ); _e.QuantizerType = QuantizerType.UseSuppliedPalette; Assert.AreEqual( QuantizerType.UseSuppliedPalette, _e.QuantizerType ); } GifFrame frame = new GifFrame( Image.FromFile( @"images\smiley.bmp" ) ); if( strategy == ColourTableStrategy.UseLocal ) { frame.Palette = Palette.FromFile( act ); } _e.AddFrame( frame ); string fileName = "AnimatedGifEncoderTest.UseSuppliedPalette" + globalLocal + "-" + actFileWithoutExtension + ".gif"; _e.WriteToFile( fileName ); _d = new GifDecoder( fileName, true ); _d.Decode(); Assert.AreEqual( ErrorState.Ok, _d.ConsolidatedState, actFileWithoutExtension ); Assert.AreEqual( 1, _d.Frames.Count, actFileWithoutExtension ); if( strategy == ColourTableStrategy.UseGlobal ) { Assert.AreEqual( true, _d.LogicalScreenDescriptor.HasGlobalColourTable, actFileWithoutExtension ); Assert.IsNotNull( _d.GlobalColourTable, actFileWithoutExtension ); } else { Assert.AreEqual( false, _d.LogicalScreenDescriptor.HasGlobalColourTable, actFileWithoutExtension ); Assert.IsNull( _d.GlobalColourTable, actFileWithoutExtension ); } string expectedFileName = @"images\Smiley\Smiley" + "-" + actFileWithoutExtension + ".bmp"; Image expected = Image.FromFile( expectedFileName ); ImageAssert.AreEqual( expected, _d.Frames[0].TheImage, expectedFileName ); } // now encode a multi-frame animation with a user-supplied palette _d = new GifDecoder( @"images\globe\spinning globe better 200px transparent background.gif" ); _d.Decode(); _e = new AnimatedGifEncoder(); _e.QuantizerType = QuantizerType.UseSuppliedPalette; _e.Palette = Palette.FromFile( @"ColourTables\C64.act" ); foreach( GifFrame f in _d.Frames ) { _e.AddFrame( f ); } string globeFileName = "AnimatedGifEncoderTest.UseSuppliedPalette" + globalLocal + ".gif"; _e.WriteToFile( globeFileName ); _d = new GifDecoder( globeFileName ); _d.Decode(); Assert.AreEqual( ErrorState.Ok, _d.ConsolidatedState ); Assert.AreEqual( _e.Frames.Count, _d.Frames.Count ); }
/// <summary> /// Default constructor. /// Sets repeat count to 0 (repeat indefinitely) /// Sets colour table strategy to UseGlobal /// Sets image quantization quality to 10. /// Sets quantizer type to NeuQuant. /// Screen size defaults to size of first frame. /// </summary> public AnimatedGifEncoder() { _frames = new Collection<GifFrame>(); _strategy = ColourTableStrategy.UseGlobal; _quality = 10; _quantizerType = QuantizerType.NeuQuant; _logicalScreenSize = Size.Empty; _palette = new Palette(); }