mirror of https://github.com/SixLabors/ImageSharp
3 changed files with 212 additions and 0 deletions
@ -0,0 +1,108 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="ImageExtensions.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Encapsulates a series of time saving extension methods to the <see cref="T:System.Drawing.Imaging.Image" /> class.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Extensions |
|||
{ |
|||
using System; |
|||
using System.Drawing; |
|||
using System.Drawing.Imaging; |
|||
|
|||
/// <summary>
|
|||
/// Encapsulates a series of time saving extension methods to the <see cref="T:System.Drawing.Imaging.Image" /> class.
|
|||
/// </summary>
|
|||
public static class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Returns information about the given <see cref="System.Drawing.Image"/>.
|
|||
/// </summary>
|
|||
/// <param name="image">
|
|||
/// The image.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="ImageInfo"/>.
|
|||
/// </returns>
|
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides information about an image.
|
|||
/// <see cref="http://madskristensen.net/post/examine-animated-gife28099s-in-c"/>
|
|||
/// </summary>
|
|||
public struct ImageInfo |
|||
{ |
|||
/// <summary>
|
|||
/// The image width.
|
|||
/// </summary>
|
|||
public int Width; |
|||
|
|||
/// <summary>
|
|||
/// The image height.
|
|||
/// </summary>
|
|||
public int Height; |
|||
|
|||
/// <summary>
|
|||
/// Whether the is indexed.
|
|||
/// </summary>
|
|||
public bool IsIndexed; |
|||
|
|||
/// <summary>
|
|||
/// Whether the is animated.
|
|||
/// </summary>
|
|||
public bool IsAnimated; |
|||
|
|||
/// <summary>
|
|||
/// The is looped.
|
|||
/// </summary>
|
|||
public bool IsLooped; |
|||
|
|||
/// <summary>
|
|||
/// The animation length in milliseconds.
|
|||
/// </summary>
|
|||
public int AnimationLength; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
|
|||
|
|||
namespace ImageProcessor.Imaging |
|||
{ |
|||
using System; |
|||
|
|||
/// <summary>
|
|||
/// Encodes multiple images as an animated gif to a stream.
|
|||
/// <remarks>
|
|||
/// Always wire this up in a using block.
|
|||
/// Disposing the encoder will complete the file.
|
|||
/// Uses default .NET GIF encoding and adds animation headers.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
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; |
|||
|
|||
/// <summary>
|
|||
/// A value indicating whether this instance of the given entity has been disposed.
|
|||
/// </summary>
|
|||
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
|
|||
/// <remarks>
|
|||
/// 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.
|
|||
/// </remarks>
|
|||
private bool isDisposed; |
|||
#endregion
|
|||
|
|||
#region IDisposable Members
|
|||
/// <summary>
|
|||
/// Disposes the object and frees resources for the Garbage Collector.
|
|||
/// </summary>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes the object and frees resources for the Garbage Collector.
|
|||
/// </summary>
|
|||
/// <param name="disposing">If true, the object gets disposed.</param>
|
|||
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
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue