mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Code is an absolute mess just now. Former-commit-id: 10e1e7f8b6806b1a83d1a54ae7d685e3d590cffcpull/17/head
12 changed files with 788 additions and 76 deletions
@ -0,0 +1,409 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="GifEncoder - Copy.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Imaging |
|||
{ |
|||
#region
|
|||
|
|||
using System; |
|||
using System.Drawing; |
|||
using System.Drawing.Imaging; |
|||
using System.IO; |
|||
using System.Linq; |
|||
|
|||
#endregion
|
|||
|
|||
/// <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>
|
|||
/// <summary>
|
|||
/// Encodes multiple images as an animated gif to a stream. <br />
|
|||
/// ALWAYS ALWAYS ALWAYS wire this up in a using block <br />
|
|||
/// Disposing the encoder will complete the file. <br />
|
|||
/// Uses default .net GIF encoding and adds animation headers.
|
|||
/// </summary>
|
|||
public class GifEncoder2 : IDisposable |
|||
{ |
|||
#region Constants
|
|||
|
|||
/// <summary>
|
|||
/// The application block size.
|
|||
/// </summary>
|
|||
private const byte ApplicationBlockSize = 0x0b; |
|||
|
|||
/// <summary>
|
|||
/// The application extension block identifier.
|
|||
/// </summary>
|
|||
private const int ApplicationExtensionBlockIdentifier = 0xff21; |
|||
|
|||
/// <summary>
|
|||
/// The application identification.
|
|||
/// </summary>
|
|||
private const string ApplicationIdentification = "NETSCAPE2.0"; |
|||
|
|||
/// <summary>
|
|||
/// The file trailer.
|
|||
/// </summary>
|
|||
private const byte FileTrailer = 0x3b; |
|||
|
|||
/// <summary>
|
|||
/// The file type.
|
|||
/// </summary>
|
|||
private const string FileType = "GIF"; |
|||
|
|||
/// <summary>
|
|||
/// The file version.
|
|||
/// </summary>
|
|||
private const string FileVersion = "89a"; |
|||
|
|||
/// <summary>
|
|||
/// The graphic control extension block identifier.
|
|||
/// </summary>
|
|||
private const int GraphicControlExtensionBlockIdentifier = 0xf921; |
|||
|
|||
/// <summary>
|
|||
/// The graphic control extension block size.
|
|||
/// </summary>
|
|||
private const byte GraphicControlExtensionBlockSize = 0x04; |
|||
|
|||
/// <summary>
|
|||
/// The source color block length.
|
|||
/// </summary>
|
|||
private const long SourceColorBlockLength = 768; |
|||
|
|||
/// <summary>
|
|||
/// The source color block position.
|
|||
/// </summary>
|
|||
private const long SourceColorBlockPosition = 13; |
|||
|
|||
/// <summary>
|
|||
/// The source global color info position.
|
|||
/// </summary>
|
|||
private const long SourceGlobalColorInfoPosition = 10; |
|||
|
|||
/// <summary>
|
|||
/// The source graphic control extension length.
|
|||
/// </summary>
|
|||
private const long SourceGraphicControlExtensionLength = 8; |
|||
|
|||
/// <summary>
|
|||
/// The source graphic control extension position.
|
|||
/// </summary>
|
|||
private const long SourceGraphicControlExtensionPosition = 781; |
|||
|
|||
/// <summary>
|
|||
/// The source image block header length.
|
|||
/// </summary>
|
|||
private const long SourceImageBlockHeaderLength = 11; |
|||
|
|||
/// <summary>
|
|||
/// The source image block position.
|
|||
/// </summary>
|
|||
private const long SourceImageBlockPosition = 789; |
|||
|
|||
#endregion
|
|||
|
|||
#region Fields
|
|||
|
|||
/// <summary>
|
|||
/// The _stream.
|
|||
/// </summary>
|
|||
private readonly Stream _stream; |
|||
|
|||
/// <summary>
|
|||
/// The _height.
|
|||
/// </summary>
|
|||
private int? _height; |
|||
|
|||
/// <summary>
|
|||
/// The _is first image.
|
|||
/// </summary>
|
|||
private bool _isFirstImage = true; |
|||
|
|||
/// <summary>
|
|||
/// The _repeat count.
|
|||
/// </summary>
|
|||
private int? _repeatCount; |
|||
|
|||
/// <summary>
|
|||
/// The _width.
|
|||
/// </summary>
|
|||
private int? _width; |
|||
|
|||
#endregion
|
|||
|
|||
#region Constructors and Destructors
|
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GifEncoder2"/> class.
|
|||
/// Encodes multiple images as an animated gif to a stream. <br/>
|
|||
/// ALWAYS ALWAYS ALWAYS wire this in a using block <br/>
|
|||
/// Disposing the encoder will complete the file. <br/>
|
|||
/// Uses default .net GIF encoding and adds animation headers.
|
|||
/// </summary>
|
|||
/// <param name="stream">
|
|||
/// The stream that will be written to.
|
|||
/// </param>
|
|||
/// <param name="width">
|
|||
/// Sets the width for this gif or null to use the first frame's width.
|
|||
/// </param>
|
|||
/// <param name="height">
|
|||
/// Sets the height for this gif or null to use the first frame's height.
|
|||
/// </param>
|
|||
/// <param name="repeatCount">
|
|||
/// The repeat Count.
|
|||
/// </param>
|
|||
public GifEncoder2(Stream stream, int? width = null, int? height = null, int? repeatCount = null) |
|||
{ |
|||
this._stream = stream; |
|||
this._width = width; |
|||
this._height = height; |
|||
this._repeatCount = repeatCount; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Public Properties
|
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the frame delay.
|
|||
/// </summary>
|
|||
public TimeSpan FrameDelay { get; set; } |
|||
|
|||
#endregion
|
|||
|
|||
#region Public Methods and Operators
|
|||
|
|||
/// <summary>
|
|||
/// Adds a frame to this animation.
|
|||
/// </summary>
|
|||
/// <param name="img">
|
|||
/// The image to add
|
|||
/// </param>
|
|||
/// <param name="x">
|
|||
/// The positioning x offset this image should be displayed at.
|
|||
/// </param>
|
|||
/// <param name="y">
|
|||
/// The positioning y offset this image should be displayed at.
|
|||
/// </param>
|
|||
/// <param name="frameDelay">
|
|||
/// The frame Delay.
|
|||
/// </param>
|
|||
public void AddFrame(Image img, int x = 0, int y = 0, TimeSpan? frameDelay = null) |
|||
{ |
|||
using (var gifStream = new MemoryStream()) |
|||
{ |
|||
img.Save(gifStream, ImageFormat.Gif); |
|||
if (this._isFirstImage) |
|||
{ |
|||
// Steal the global color table info
|
|||
this.InitHeader(gifStream, img.Width, img.Height); |
|||
} |
|||
|
|||
this.WriteGraphicControlBlock(gifStream, frameDelay.GetValueOrDefault(this.FrameDelay)); |
|||
this.WriteImageBlock(gifStream, !this._isFirstImage, x, y, img.Width, img.Height); |
|||
} |
|||
|
|||
this._isFirstImage = false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The dispose.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
// Complete Application Block
|
|||
this.WriteByte(0); |
|||
|
|||
// Complete File
|
|||
this.WriteByte(FileTrailer); |
|||
|
|||
// Pushing data
|
|||
this._stream.Flush(); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Methods
|
|||
|
|||
/// <summary>
|
|||
/// The init header.
|
|||
/// </summary>
|
|||
/// <param name="sourceGif">
|
|||
/// The source gif.
|
|||
/// </param>
|
|||
/// <param name="w">
|
|||
/// The w.
|
|||
/// </param>
|
|||
/// <param name="h">
|
|||
/// The h.
|
|||
/// </param>
|
|||
private void InitHeader(Stream sourceGif, int w, int h) |
|||
{ |
|||
// File Header
|
|||
this.WriteString(FileType); |
|||
this.WriteString(FileVersion); |
|||
this.WriteShort(this._width.GetValueOrDefault(w)); // Initial Logical Width
|
|||
this.WriteShort(this._height.GetValueOrDefault(h)); // Initial Logical Height
|
|||
sourceGif.Position = SourceGlobalColorInfoPosition; |
|||
this.WriteByte(sourceGif.ReadByte()); // Global Color Table Info
|
|||
this.WriteByte(0); // Background Color Index
|
|||
this.WriteByte(0); // Pixel aspect ratio
|
|||
this.WriteColorTable(sourceGif); |
|||
|
|||
// App Extension Header
|
|||
this.WriteShort(ApplicationExtensionBlockIdentifier); |
|||
this.WriteByte(ApplicationBlockSize); |
|||
this.WriteString(ApplicationIdentification); |
|||
this.WriteByte(3); // Application block length
|
|||
this.WriteByte(1); |
|||
this.WriteShort(this._repeatCount.GetValueOrDefault(0)); // Repeat count for images.
|
|||
this.WriteByte(0); // terminator
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The write byte.
|
|||
/// </summary>
|
|||
/// <param name="value">
|
|||
/// The value.
|
|||
/// </param>
|
|||
private void WriteByte(int value) |
|||
{ |
|||
this._stream.WriteByte(Convert.ToByte(value)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The write color table.
|
|||
/// </summary>
|
|||
/// <param name="sourceGif">
|
|||
/// The source gif.
|
|||
/// </param>
|
|||
private void WriteColorTable(Stream sourceGif) |
|||
{ |
|||
sourceGif.Position = SourceColorBlockPosition; // Locating the image color table
|
|||
var colorTable = new byte[SourceColorBlockLength]; |
|||
sourceGif.Read(colorTable, 0, colorTable.Length); |
|||
this._stream.Write(colorTable, 0, colorTable.Length); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The write graphic control block.
|
|||
/// </summary>
|
|||
/// <param name="sourceGif">
|
|||
/// The source gif.
|
|||
/// </param>
|
|||
/// <param name="frameDelay">
|
|||
/// The frame delay.
|
|||
/// </param>
|
|||
private void WriteGraphicControlBlock(Stream sourceGif, TimeSpan frameDelay) |
|||
{ |
|||
sourceGif.Position = SourceGraphicControlExtensionPosition; // Locating the source GCE
|
|||
var blockhead = new byte[SourceGraphicControlExtensionLength]; |
|||
sourceGif.Read(blockhead, 0, blockhead.Length); // Reading source GCE
|
|||
|
|||
this.WriteShort(GraphicControlExtensionBlockIdentifier); // Identifier
|
|||
this.WriteByte(GraphicControlExtensionBlockSize); // Block Size
|
|||
this.WriteByte(blockhead[3] & 0xf7 | 0x08); // Setting disposal flag
|
|||
this.WriteShort(Convert.ToInt32(frameDelay.TotalMilliseconds / 10)); // Setting frame delay
|
|||
this.WriteByte(blockhead[6]); // Transparent color index
|
|||
this.WriteByte(0); // Terminator
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The write image block.
|
|||
/// </summary>
|
|||
/// <param name="sourceGif">
|
|||
/// The source gif.
|
|||
/// </param>
|
|||
/// <param name="includeColorTable">
|
|||
/// The include color table.
|
|||
/// </param>
|
|||
/// <param name="x">
|
|||
/// The x.
|
|||
/// </param>
|
|||
/// <param name="y">
|
|||
/// The y.
|
|||
/// </param>
|
|||
/// <param name="h">
|
|||
/// The h.
|
|||
/// </param>
|
|||
/// <param name="w">
|
|||
/// The w.
|
|||
/// </param>
|
|||
private void WriteImageBlock(Stream sourceGif, bool includeColorTable, int x, int y, int h, int w) |
|||
{ |
|||
sourceGif.Position = SourceImageBlockPosition; // Locating the image block
|
|||
var header = new byte[SourceImageBlockHeaderLength]; |
|||
sourceGif.Read(header, 0, header.Length); |
|||
this.WriteByte(header[0]); // Separator
|
|||
this.WriteShort(x); // Position X
|
|||
this.WriteShort(y); // Position Y
|
|||
this.WriteShort(h); // Height
|
|||
this.WriteShort(w); // Width
|
|||
|
|||
if (includeColorTable) |
|||
{ |
|||
// If first frame, use global color table - else use local
|
|||
sourceGif.Position = SourceGlobalColorInfoPosition; |
|||
this.WriteByte(sourceGif.ReadByte() & 0x3f | 0x80); // Enabling local color table
|
|||
this.WriteColorTable(sourceGif); |
|||
} |
|||
else |
|||
{ |
|||
this.WriteByte(header[9] & 0x07 | 0x07); // Disabling local color table
|
|||
} |
|||
|
|||
this.WriteByte(header[10]); // LZW Min Code Size
|
|||
|
|||
// Read/Write image data
|
|||
sourceGif.Position = SourceImageBlockPosition + SourceImageBlockHeaderLength; |
|||
|
|||
int dataLength = sourceGif.ReadByte(); |
|||
while (dataLength > 0) |
|||
{ |
|||
var imgData = new byte[dataLength]; |
|||
sourceGif.Read(imgData, 0, dataLength); |
|||
|
|||
this._stream.WriteByte(Convert.ToByte(dataLength)); |
|||
this._stream.Write(imgData, 0, dataLength); |
|||
dataLength = sourceGif.ReadByte(); |
|||
} |
|||
|
|||
this._stream.WriteByte(0); // Terminator
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The write short.
|
|||
/// </summary>
|
|||
/// <param name="value">
|
|||
/// The value.
|
|||
/// </param>
|
|||
private void WriteShort(int value) |
|||
{ |
|||
this._stream.WriteByte(Convert.ToByte(value & 0xff)); |
|||
this._stream.WriteByte(Convert.ToByte((value >> 8) & 0xff)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The write string.
|
|||
/// </summary>
|
|||
/// <param name="value">
|
|||
/// The value.
|
|||
/// </param>
|
|||
private void WriteString(string value) |
|||
{ |
|||
this._stream.Write(value.ToArray().Select(c => (byte)c).ToArray(), 0, value.Length); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<configuration> |
|||
<startup> |
|||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" /> |
|||
</startup> |
|||
</configuration> |
|||
@ -0,0 +1,66 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|||
<PropertyGroup> |
|||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
|||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
|||
<ProjectGuid>{7BF5274B-56A7-4B62-8105-E9BDF25BAFE7}</ProjectGuid> |
|||
<OutputType>Exe</OutputType> |
|||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|||
<RootNamespace>ImageProcessorConsole</RootNamespace> |
|||
<AssemblyName>ImageProcessorConsole</AssemblyName> |
|||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion> |
|||
<FileAlignment>512</FileAlignment> |
|||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|||
<PlatformTarget>AnyCPU</PlatformTarget> |
|||
<DebugSymbols>true</DebugSymbols> |
|||
<DebugType>full</DebugType> |
|||
<Optimize>false</Optimize> |
|||
<OutputPath>bin\Debug\</OutputPath> |
|||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|||
<PlatformTarget>AnyCPU</PlatformTarget> |
|||
<DebugType>pdbonly</DebugType> |
|||
<Optimize>true</Optimize> |
|||
<OutputPath>bin\Release\</OutputPath> |
|||
<DefineConstants>TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<Reference Include="System" /> |
|||
<Reference Include="System.Core" /> |
|||
<Reference Include="System.Drawing" /> |
|||
<Reference Include="System.Xml.Linq" /> |
|||
<Reference Include="System.Data.DataSetExtensions" /> |
|||
<Reference Include="Microsoft.CSharp" /> |
|||
<Reference Include="System.Data" /> |
|||
<Reference Include="System.Xml" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Compile Include="Program.cs" /> |
|||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<None Include="App.config" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\ImageProcessor\ImageProcessor.csproj"> |
|||
<Project>{3b5dd734-fb7a-487d-8ce6-55e7af9aea7e}</Project> |
|||
<Name>ImageProcessor</Name> |
|||
</ProjectReference> |
|||
</ItemGroup> |
|||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
|||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
|||
Other similar extension points exist, see Microsoft.Common.targets. |
|||
<Target Name="BeforeBuild"> |
|||
</Target> |
|||
<Target Name="AfterBuild"> |
|||
</Target> |
|||
--> |
|||
</Project> |
|||
@ -0,0 +1,52 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace ImageProcessorConsole |
|||
{ |
|||
using System.Drawing; |
|||
using System.Drawing.Imaging; |
|||
using System.IO; |
|||
|
|||
using ImageProcessor; |
|||
|
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
string path = new Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath; |
|||
// ReSharper disable once AssignNullToNotNullAttribute
|
|||
string resolvedPath = Path.Combine(Path.GetDirectoryName(path), "images"); |
|||
DirectoryInfo di = new DirectoryInfo(resolvedPath); |
|||
if (!di.Exists) |
|||
{ |
|||
di.Create(); |
|||
} |
|||
|
|||
FileInfo[] files = di.GetFiles("*.gif"); |
|||
|
|||
foreach (FileInfo fileInfo in files) |
|||
{ |
|||
byte[] photoBytes = File.ReadAllBytes(fileInfo.FullName); |
|||
|
|||
// ImageProcessor
|
|||
using (MemoryStream inStream = new MemoryStream(photoBytes)) |
|||
{ |
|||
using (ImageFactory imageFactory = new ImageFactory()) |
|||
{ |
|||
Size size = new Size(150, 150); |
|||
ImageFormat format = ImageFormat.Gif; |
|||
|
|||
// Load, resize, set the format and quality and save an image.
|
|||
imageFactory.Load(inStream) |
|||
.Constrain(size) |
|||
.Format(format) |
|||
.Save(Path.Combine(Path.GetDirectoryName(path), "output", fileInfo.Name)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using System.Reflection; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// General Information about an assembly is controlled through the following
|
|||
// set of attributes. Change these attribute values to modify the information
|
|||
// associated with an assembly.
|
|||
[assembly: AssemblyTitle("ImageProcessorConsole")] |
|||
[assembly: AssemblyDescription("")] |
|||
[assembly: AssemblyConfiguration("")] |
|||
[assembly: AssemblyCompany("")] |
|||
[assembly: AssemblyProduct("ImageProcessorConsole")] |
|||
[assembly: AssemblyCopyright("Copyright © 2014")] |
|||
[assembly: AssemblyTrademark("")] |
|||
[assembly: AssemblyCulture("")] |
|||
|
|||
// Setting ComVisible to false makes the types in this assembly not visible
|
|||
// to COM components. If you need to access a type in this assembly from
|
|||
// COM, set the ComVisible attribute to true on that type.
|
|||
[assembly: ComVisible(false)] |
|||
|
|||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|||
[assembly: Guid("ac215639-6e27-4b9c-9ebb-9116c7a5d8a6")] |
|||
|
|||
// Version information for an assembly consists of the following four values:
|
|||
//
|
|||
// Major Version
|
|||
// Minor Version
|
|||
// Build Number
|
|||
// Revision
|
|||
//
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
|||
// by using the '*' as shown below:
|
|||
// [assembly: AssemblyVersion("1.0.*")]
|
|||
[assembly: AssemblyVersion("1.0.0.0")] |
|||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|||
Loading…
Reference in new issue