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