Browse Source

Copy input stream and adding gifdecoder

Former-commit-id: e8a3833fe02c401d75e413299332e7037eb89395
Former-commit-id: f212567b18c276d51368b8ee2d2ad6f5e24a53a8
af/merge-core
James South 11 years ago
parent
commit
fe1fe45daa
  1. 8
      src/ImageProcessor.Playground/Program.cs
  2. 17
      src/ImageProcessor/ImageFactory.cs
  3. 2
      src/ImageProcessor/ImageProcessor.csproj
  4. 76
      src/ImageProcessor/Imaging/Formats/FormatUtilities.cs
  5. 117
      src/ImageProcessor/Imaging/Formats/GifDecoder.cs
  6. 10
      src/ImageProcessor/Imaging/Formats/GifFormat.cs
  7. 58
      src/ImageProcessor/Imaging/Formats/GifInfo.cs

8
src/ImageProcessor.Playground/Program.cs

@ -79,17 +79,19 @@ namespace ImageProcessor.PlayGround
//.Resize(new Size((int)(size.Width * 1.1), 0))
//.ContentAwareResize(layer)
//.Constrain(size)
//.Rotate(66)
//.Rotate(-64)
//.Mask(mask)
//.Format(new PngFormat())
//.BackgroundColor(Color.Cyan)
//.ReplaceColor(Color.FromArgb(255, 1, 107, 165), Color.FromArgb(255, 1, 165, 13), 80)
.Resize(size)
//.DetectEdges(new SobelEdgeFilter(), false)
//.Resize(size)
.DetectEdges(new SobelEdgeFilter(), true)
//.DetectEdges(new LaplacianOfGaussianEdgeFilter())
//.EntropyCrop()
//.Filter(MatrixFilters.Invert)
//.Contrast(50)
//.Filter(MatrixFilters.Comic)
//.Flip()
//.Filter(MatrixFilters.HiSatch)
//.Pixelate(8)
//.GaussianSharpen(10)

17
src/ImageProcessor/ImageFactory.cs

@ -146,11 +146,20 @@ namespace ImageProcessor
throw new ImageFormatException("Input stream is not a supported format.");
}
MemoryStream memoryStream = new MemoryStream();
// Copy the stream. Disposal of the input stream is the responsibility
// of the user.
stream.CopyTo(memoryStream);
// Set the position to 0 afterwards.
stream.Position = memoryStream.Position = 0;
// Set our image as the memory stream value.
this.Image = format.Load(stream);
this.Image = format.Load(memoryStream);
// Store the stream so we can dispose of it later.
this.InputStream = stream;
this.InputStream = memoryStream;
// Set the other properties.
format.Quality = DefaultQuality;
@ -242,6 +251,8 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
// Set our new image as the memory stream value.
this.InputStream.Position = 0;
#if !__MonoCS__
Image newImage = Image.FromStream(this.InputStream, true);
#else
@ -1027,7 +1038,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
// Allow the same stream to be used as for input.
stream.Seek(0, SeekOrigin.Begin);
stream.Position = 0;
this.Image = this.CurrentImageFormat.Save(stream, this.Image);
}

2
src/ImageProcessor/ImageProcessor.csproj

@ -154,7 +154,7 @@
<Compile Include="Imaging\FastBitmap.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Imaging\Formats\GifInfo.cs" />
<Compile Include="Imaging\Formats\GifDecoder.cs" />
<Compile Include="Common\Extensions\IntegerExtensions.cs" />
<Compile Include="Common\Exceptions\ImageFormatException.cs" />
<Compile Include="ImageFactory.cs" />

76
src/ImageProcessor/Imaging/Formats/FormatUtilities.cs

@ -98,82 +98,6 @@ namespace ImageProcessor.Imaging.Formats
return ImageAnimator.CanAnimate(image);
}
/// <summary>
/// Returns information about the given <see cref="System.Drawing.Image"/>.
/// </summary>
/// <param name="image">
/// The image to extend.
/// </param>
/// <param name="format">
/// The image format.
/// </param>
/// <param name="fetchFrames">
/// Whether to fetch the images frames.
/// </param>
/// <returns>
/// The <see cref="GifInfo"/>.
/// </returns>
public static GifInfo GetGifInfo(Image image, ImageFormat format, bool fetchFrames = true)
{
if (image.RawFormat.Guid != ImageFormat.Gif.Guid && format.Guid != ImageFormat.Gif.Guid)
{
throw new ArgumentException("Image is not a gif.");
}
GifInfo info = new GifInfo
{
Height = image.Height,
Width = image.Width
};
if (IsAnimated(image))
{
info.IsAnimated = true;
if (fetchFrames)
{
int frameCount = image.GetFrameCount(FrameDimension.Time);
int last = frameCount - 1;
double length = 0;
List<GifFrame> gifFrames = new List<GifFrame>();
// Get the times stored in the gif.
byte[] times = image.GetPropertyItem((int)ExifPropertyTag.FrameDelay).Value;
for (int i = 0; i < frameCount; i++)
{
// Convert each 4-byte chunk into an integer.
// GDI returns a single array with all delays, while Mono returns a different array for each frame.
TimeSpan delay = TimeSpan.FromMilliseconds(BitConverter.ToInt32(times, (4 * i) % times.Length) * 10);
// Find the frame
image.SelectActiveFrame(FrameDimension.Time, i);
// TODO: Get positions.
gifFrames.Add(new GifFrame { Delay = delay, Image = new Bitmap(image) });
// Reset the position.
if (i == last)
{
image.SelectActiveFrame(FrameDimension.Time, 0);
}
length += delay.TotalMilliseconds;
}
info.GifFrames = gifFrames;
info.AnimationLength = length;
// Loop info is stored at byte 20737.
info.LoopCount = BitConverter.ToInt16(image.GetPropertyItem((int)ExifPropertyTag.LoopCount).Value, 0);
info.IsLooped = info.LoopCount != 1;
}
}
return info;
}
/// <summary>
/// Returns an instance of EncodingParameters for jpeg compression.
/// </summary>

117
src/ImageProcessor/Imaging/Formats/GifDecoder.cs

@ -0,0 +1,117 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GifDecoder.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Decodes gifs to provides information.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Formats
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using ImageProcessor.Imaging.MetaData;
/// <summary>
/// Decodes gifs to provides information.
/// </summary>
public class GifDecoder
{
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoder"/> class.
/// </summary>
/// <param name="image">
/// The <see cref="Image"/> to decode.
/// </param>
public GifDecoder(Image image)
{
this.Height = image.Height;
this.Width = image.Width;
if (FormatUtilities.IsAnimated(image))
{
this.IsAnimated = true;
if (this.IsAnimated)
{
int frameCount = image.GetFrameCount(FrameDimension.Time);
int last = frameCount - 1;
double length = 0;
List<GifFrame> gifFrames = new List<GifFrame>();
// Get the times stored in the gif.
byte[] times = image.GetPropertyItem((int)ExifPropertyTag.FrameDelay).Value;
for (int i = 0; i < frameCount; i++)
{
// Convert each 4-byte chunk into an integer.
// GDI returns a single array with all delays, while Mono returns a different array for each frame.
TimeSpan delay = TimeSpan.FromMilliseconds(BitConverter.ToInt32(times, (4 * i) % times.Length) * 10);
// Find the frame
image.SelectActiveFrame(FrameDimension.Time, i);
// TODO: Get positions.
gifFrames.Add(new GifFrame { Delay = delay, Image = new Bitmap(image) });
// Reset the position.
if (i == last)
{
image.SelectActiveFrame(FrameDimension.Time, 0);
}
length += delay.TotalMilliseconds;
}
this.GifFrames = gifFrames;
this.AnimationLength = length;
// Loop info is stored at byte 20737.
this.LoopCount = BitConverter.ToInt16(image.GetPropertyItem((int)ExifPropertyTag.LoopCount).Value, 0);
this.IsLooped = this.LoopCount != 1;
}
}
}
/// <summary>
/// Gets or sets the image width.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Gets or sets the image height.
/// </summary>
public int Height { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the image is animated.
/// </summary>
public bool IsAnimated { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the image is looped.
/// </summary>
public bool IsLooped { get; set; }
/// <summary>
/// Gets or sets the loop count.
/// </summary>
public int LoopCount { get; set; }
/// <summary>
/// Gets or sets the gif frames.
/// </summary>
public ICollection<GifFrame> GifFrames { get; set; }
/// <summary>
/// Gets or sets the animation length in milliseconds.
/// </summary>
public double AnimationLength { get; set; }
}
}

10
src/ImageProcessor/Imaging/Formats/GifFormat.cs

@ -74,18 +74,20 @@ namespace ImageProcessor.Imaging.Formats
/// <param name="factory">The <see cref="ImageFactory" />.</param>
public override void ApplyProcessor(Func<ImageFactory, Image> processor, ImageFactory factory)
{
GifInfo info = FormatUtilities.GetGifInfo(factory.Image, this.ImageFormat);
//GifInfo info = FormatUtilities.GetGifInfo(factory.Image, this.ImageFormat);
if (info.IsAnimated)
GifDecoder decoder = new GifDecoder(factory.Image);
if (decoder.IsAnimated)
{
OctreeQuantizer quantizer = new OctreeQuantizer(255, 8);
// We don't dispose of the memory stream as that is disposed when a new image is created and doing so
// beforehand will cause an exception.
MemoryStream stream = new MemoryStream();
using (GifEncoder encoder = new GifEncoder(stream, null, null, info.LoopCount))
using (GifEncoder encoder = new GifEncoder(stream, null, null, decoder.LoopCount))
{
foreach (GifFrame frame in info.GifFrames)
foreach (GifFrame frame in decoder.GifFrames)
{
factory.Image = frame.Image;
frame.Image = quantizer.Quantize(processor.Invoke(factory));

58
src/ImageProcessor/Imaging/Formats/GifInfo.cs

@ -1,58 +0,0 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GifInfo.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides information about an image.
// <see href="http://madskristensen.net/post/examine-animated-gife28099s-in-c" />
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Formats
{
using System.Collections.Generic;
using ImageProcessor.Imaging;
/// <summary>
/// Provides information about an image.
/// <see href="http://madskristensen.net/post/examine-animated-gife28099s-in-c"/>
/// </summary>
public class GifInfo
{
/// <summary>
/// Gets or sets the image width.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Gets or sets the image height.
/// </summary>
public int Height { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the image is animated.
/// </summary>
public bool IsAnimated { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the image is looped.
/// </summary>
public bool IsLooped { get; set; }
/// <summary>
/// Gets or sets the loop count.
/// </summary>
public int LoopCount { get; set; }
/// <summary>
/// Gets or sets the gif frames.
/// </summary>
public ICollection<GifFrame> GifFrames { get; set; }
/// <summary>
/// Gets or sets the animation length in milliseconds.
/// </summary>
public double AnimationLength { get; set; }
}
}
Loading…
Cancel
Save