diff --git a/src/ImageProcessor/Extensions/ImageExtensions.cs b/src/ImageProcessor/Extensions/ImageExtensions.cs
new file mode 100644
index 0000000000..e45bb8881d
--- /dev/null
+++ b/src/ImageProcessor/Extensions/ImageExtensions.cs
@@ -0,0 +1,108 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Encapsulates a series of time saving extension methods to the class.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Extensions
+{
+ using System;
+ using System.Drawing;
+ using System.Drawing.Imaging;
+
+ ///
+ /// Encapsulates a series of time saving extension methods to the class.
+ ///
+ public static class ImageExtensions
+ {
+ ///
+ /// Returns information about the given .
+ ///
+ ///
+ /// The image.
+ ///
+ ///
+ /// The .
+ ///
+ public static ImageInfo GetImageInfo(this Image image)
+ {
+ ImageInfo info = new ImageInfo
+ {
+ Height = image.Height,
+ Width = image.Width,
+
+ // Test value of flags using bitwise AND.
+ // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
+ IsIndexed = (image.PixelFormat & PixelFormat.Indexed) != 0
+ };
+
+ if (image.RawFormat.Equals(ImageFormat.Gif))
+ {
+ if (ImageAnimator.CanAnimate(image))
+ {
+ FrameDimension frameDimension = new FrameDimension(image.FrameDimensionsList[0]);
+
+ int frameCount = image.GetFrameCount(frameDimension);
+ int delay = 0;
+ int index = 0;
+
+ for (int f = 0; f < frameCount; f++)
+ {
+ int thisDelay = BitConverter.ToInt32(image.GetPropertyItem(20736).Value, index) * 10;
+ delay += thisDelay < 100 ? 100 : thisDelay; // Minimum delay is 100 ms
+ index += 4;
+ }
+
+ info.AnimationLength = delay;
+ info.IsAnimated = true;
+
+ // Loop info is stored at byte 20737.
+ info.IsLooped = BitConverter.ToInt16(image.GetPropertyItem(20737).Value, 0) != 1;
+ }
+ }
+
+ return info;
+ }
+
+ ///
+ /// Provides information about an image.
+ ///
+ ///
+ public struct ImageInfo
+ {
+ ///
+ /// The image width.
+ ///
+ public int Width;
+
+ ///
+ /// The image height.
+ ///
+ public int Height;
+
+ ///
+ /// Whether the is indexed.
+ ///
+ public bool IsIndexed;
+
+ ///
+ /// Whether the is animated.
+ ///
+ public bool IsAnimated;
+
+ ///
+ /// The is looped.
+ ///
+ public bool IsLooped;
+
+ ///
+ /// The animation length in milliseconds.
+ ///
+ public int AnimationLength;
+ }
+ }
+}
diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj
index f75cf9ff9d..723d88c68f 100644
--- a/src/ImageProcessor/ImageProcessor.csproj
+++ b/src/ImageProcessor/ImageProcessor.csproj
@@ -60,6 +60,7 @@
+
@@ -70,6 +71,7 @@
+
diff --git a/src/ImageProcessor/Imaging/GifEncoder.cs b/src/ImageProcessor/Imaging/GifEncoder.cs
new file mode 100644
index 0000000000..0e300ce470
--- /dev/null
+++ b/src/ImageProcessor/Imaging/GifEncoder.cs
@@ -0,0 +1,102 @@
+
+
+namespace ImageProcessor.Imaging
+{
+ using System;
+
+ ///
+ /// Encodes multiple images as an animated gif to a stream.
+ ///
+ /// Always wire this up in a using block.
+ /// Disposing the encoder will complete the file.
+ /// Uses default .NET GIF encoding and adds animation headers.
+ ///
+ ///
+ public class GifEncoder : IDisposable
+ {
+ #region Fields
+ private const string FileType = "GIF";
+ private const string FileVersion = "89a";
+ private const byte FileTrailer = 0x3b;
+
+ private const int ApplicationExtensionBlockIdentifier = 0xff21;
+ private const byte ApplicationBlockSize = 0x0b;
+ private const string ApplicationIdentification = "NETSCAPE2.0";
+
+ private const int GraphicControlExtensionBlockIdentifier = 0xf921;
+ private const byte GraphicControlExtensionBlockSize = 0x04;
+
+ private const long SourceGlobalColorInfoPosition = 10;
+ private const long SourceGraphicControlExtensionPosition = 781;
+ private const long SourceGraphicControlExtensionLength = 8;
+ private const long SourceImageBlockPosition = 789;
+ private const long SourceImageBlockHeaderLength = 11;
+ private const long SourceColorBlockPosition = 13;
+ private const long SourceColorBlockLength = 768;
+
+ ///
+ /// A value indicating whether this instance of the given entity has been disposed.
+ ///
+ /// if this instance has been disposed; otherwise, .
+ ///
+ /// If the entity is disposed, it must not be disposed a second
+ /// time. The isDisposed field is set the first time the entity
+ /// is disposed. If the isDisposed field is true, then the Dispose()
+ /// method will not dispose again. This help not to prolong the entity's
+ /// life in the Garbage Collector.
+ ///
+ private bool isDisposed;
+ #endregion
+
+ #region IDisposable Members
+ ///
+ /// Disposes the object and frees resources for the Garbage Collector.
+ ///
+ public void Dispose()
+ {
+ this.Dispose(true);
+
+ // This object will be cleaned up by the Dispose method.
+ // Therefore, you should call GC.SupressFinalize to
+ // take this object off the finalization queue
+ // and prevent finalization code for this object
+ // from executing a second time.
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Disposes the object and frees resources for the Garbage Collector.
+ ///
+ /// If true, the object gets disposed.
+ protected virtual void Dispose(bool disposing)
+ {
+ if (this.isDisposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ // Dispose of any managed resources here.
+ //if (this.Image != null)
+ //{
+ // // Dispose of the memory stream from Load and the image.
+ // if (this.inputStream != null)
+ // {
+ // this.inputStream.Dispose();
+ // this.inputStream = null;
+ // }
+
+ // this.Image.Dispose();
+ // this.Image = null;
+ //}
+ }
+
+ // Call the appropriate methods to clean up
+ // unmanaged resources here.
+ // Note disposing is done.
+ this.isDisposed = true;
+ }
+ #endregion
+ }
+}