From 684345f7ebc8793d56f0c62e64e4d0d8b2149ecc Mon Sep 17 00:00:00 2001 From: James South Date: Tue, 22 Apr 2014 17:03:36 +0100 Subject: [PATCH] Adding skeleton Former-commit-id: 341e5a0d6f29347ebe35e4c113aafbc4d6c8bb73 --- .../Extensions/ImageExtensions.cs | 108 ++++++++++++++++++ src/ImageProcessor/ImageProcessor.csproj | 2 + src/ImageProcessor/Imaging/GifEncoder.cs | 102 +++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 src/ImageProcessor/Extensions/ImageExtensions.cs create mode 100644 src/ImageProcessor/Imaging/GifEncoder.cs diff --git a/src/ImageProcessor/Extensions/ImageExtensions.cs b/src/ImageProcessor/Extensions/ImageExtensions.cs new file mode 100644 index 000000000..e45bb8881 --- /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 f75cf9ff9..723d88c68 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 000000000..0e300ce47 --- /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 + } +}