From fe1fe45daae0b11f01b1a16c2b9f170387ffd7a1 Mon Sep 17 00:00:00 2001 From: James South Date: Fri, 17 Oct 2014 14:18:51 +0100 Subject: [PATCH] Copy input stream and adding gifdecoder Former-commit-id: e8a3833fe02c401d75e413299332e7037eb89395 Former-commit-id: f212567b18c276d51368b8ee2d2ad6f5e24a53a8 --- src/ImageProcessor.Playground/Program.cs | 8 +- src/ImageProcessor/ImageFactory.cs | 17 ++- src/ImageProcessor/ImageProcessor.csproj | 2 +- .../Imaging/Formats/FormatUtilities.cs | 76 ------------ .../Imaging/Formats/GifDecoder.cs | 117 ++++++++++++++++++ .../Imaging/Formats/GifFormat.cs | 10 +- src/ImageProcessor/Imaging/Formats/GifInfo.cs | 58 --------- 7 files changed, 143 insertions(+), 145 deletions(-) create mode 100644 src/ImageProcessor/Imaging/Formats/GifDecoder.cs delete mode 100644 src/ImageProcessor/Imaging/Formats/GifInfo.cs diff --git a/src/ImageProcessor.Playground/Program.cs b/src/ImageProcessor.Playground/Program.cs index 80193905f..403acb323 100644 --- a/src/ImageProcessor.Playground/Program.cs +++ b/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) diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs index 7b0acfdce..02d277bad 100644 --- a/src/ImageProcessor/ImageFactory.cs +++ b/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); } diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index d0f66a226..5112ef796 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -154,7 +154,7 @@ Code - + diff --git a/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs b/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs index adf927d04..f344e400d 100644 --- a/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs +++ b/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs @@ -98,82 +98,6 @@ namespace ImageProcessor.Imaging.Formats return ImageAnimator.CanAnimate(image); } - /// - /// Returns information about the given . - /// - /// - /// The image to extend. - /// - /// - /// The image format. - /// - /// - /// Whether to fetch the images frames. - /// - /// - /// The . - /// - 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 gifFrames = new List(); - - // 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; - } - /// /// Returns an instance of EncodingParameters for jpeg compression. /// diff --git a/src/ImageProcessor/Imaging/Formats/GifDecoder.cs b/src/ImageProcessor/Imaging/Formats/GifDecoder.cs new file mode 100644 index 000000000..a7008d162 --- /dev/null +++ b/src/ImageProcessor/Imaging/Formats/GifDecoder.cs @@ -0,0 +1,117 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Decodes gifs to provides information. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Imaging.Formats +{ + using System; + using System.Collections.Generic; + using System.Drawing; + using System.Drawing.Imaging; + + using ImageProcessor.Imaging.MetaData; + + /// + /// Decodes gifs to provides information. + /// + public class GifDecoder + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The to decode. + /// + 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 gifFrames = new List(); + + // 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; + } + } + } + + /// + /// Gets or sets the image width. + /// + public int Width { get; set; } + + /// + /// Gets or sets the image height. + /// + public int Height { get; set; } + + /// + /// Gets or sets a value indicating whether the image is animated. + /// + public bool IsAnimated { get; set; } + + /// + /// Gets or sets a value indicating whether the image is looped. + /// + public bool IsLooped { get; set; } + + /// + /// Gets or sets the loop count. + /// + public int LoopCount { get; set; } + + /// + /// Gets or sets the gif frames. + /// + public ICollection GifFrames { get; set; } + + /// + /// Gets or sets the animation length in milliseconds. + /// + public double AnimationLength { get; set; } + } +} diff --git a/src/ImageProcessor/Imaging/Formats/GifFormat.cs b/src/ImageProcessor/Imaging/Formats/GifFormat.cs index 8566e998b..a1819b3d8 100644 --- a/src/ImageProcessor/Imaging/Formats/GifFormat.cs +++ b/src/ImageProcessor/Imaging/Formats/GifFormat.cs @@ -74,18 +74,20 @@ namespace ImageProcessor.Imaging.Formats /// The . public override void ApplyProcessor(Func 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)); diff --git a/src/ImageProcessor/Imaging/Formats/GifInfo.cs b/src/ImageProcessor/Imaging/Formats/GifInfo.cs deleted file mode 100644 index 43142971a..000000000 --- a/src/ImageProcessor/Imaging/Formats/GifInfo.cs +++ /dev/null @@ -1,58 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James South. -// Licensed under the Apache License, Version 2.0. -// -// -// Provides information about an image. -// -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace ImageProcessor.Imaging.Formats -{ - using System.Collections.Generic; - using ImageProcessor.Imaging; - - /// - /// Provides information about an image. - /// - /// - public class GifInfo - { - /// - /// Gets or sets the image width. - /// - public int Width { get; set; } - - /// - /// Gets or sets the image height. - /// - public int Height { get; set; } - - /// - /// Gets or sets a value indicating whether the image is animated. - /// - public bool IsAnimated { get; set; } - - /// - /// Gets or sets a value indicating whether the image is looped. - /// - public bool IsLooped { get; set; } - - /// - /// Gets or sets the loop count. - /// - public int LoopCount { get; set; } - - /// - /// Gets or sets the gif frames. - /// - public ICollection GifFrames { get; set; } - - /// - /// Gets or sets the animation length in milliseconds. - /// - public double AnimationLength { get; set; } - } -} \ No newline at end of file