diff --git a/src/ImageProcessor/Filters/Contrast.cs b/src/ImageProcessor/Filters/Contrast.cs index 752bef018..30823a8f5 100644 --- a/src/ImageProcessor/Filters/Contrast.cs +++ b/src/ImageProcessor/Filters/Contrast.cs @@ -8,9 +8,9 @@ namespace ImageProcessor.Filters using System; /// - /// An to change the contrast of an . + /// An to change the contrast of an . /// - public class Contrast : ParallelImageFilter + public class Contrast : ParallelImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessor/Filters/IImageFilter.cs b/src/ImageProcessor/Filters/IImageFilter.cs deleted file mode 100644 index 11843d021..000000000 --- a/src/ImageProcessor/Filters/IImageFilter.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace ImageProcessor.Filters -{ - /// - /// Image processing filter interface. - /// - /// - /// The interface defines the set of methods, which should be - /// provided by all image processing filters. Methods of this interface - /// manipulate the original image. - /// - public interface IImageFilter - { - /// - /// Apply filter to an image at the area of the specified rectangle. - /// - /// Target image to apply filter to. - /// The source image. Cannot be null. - /// The rectangle, which defines the area of the - /// image where the filter should be applied to. - /// The method keeps the source image unchanged and returns the - /// the result of image processing filter as new image. - /// - /// - /// is null. - /// - or - - /// - /// is null. - /// - /// - /// doesnt fit the dimension of the image. - /// - void Apply(ImageBase target, ImageBase source, Rectangle rectangle); - } -} diff --git a/src/ImageProcessor/Filters/ImageFilterExtensions.cs b/src/ImageProcessor/Filters/ImageFilterExtensions.cs index c97500a23..674f4f0e4 100644 --- a/src/ImageProcessor/Filters/ImageFilterExtensions.cs +++ b/src/ImageProcessor/Filters/ImageFilterExtensions.cs @@ -5,77 +5,17 @@ namespace ImageProcessor.Filters { - using System; - /// - /// Exstension methods for performing filtering methods again an image. + /// Exstensions methods for to apply filters to the image. /// public static class ImageFilterExtensions { - /// - /// Applies the collection of filters to the image. - /// - /// The image this method extends. - /// Any filters to apply to the image. - /// The . - public static Image Filter(this Image source, params IImageFilter[] filters) => Filter(source, source.Bounds, filters); - - /// - /// Applies the collection of filters to the image. - /// - /// The image this method extends. - /// - /// The rectangle defining the bounds of the pixels the image filter with adjust. - /// Any filters to apply to the image. - /// The . - public static Image Filter(this Image source, Rectangle rectangle, params IImageFilter[] filters) - { - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (IImageFilter filter in filters) - { - source = PerformAction(source, true, (sourceImage, targetImage) => filter.Apply(targetImage, sourceImage, rectangle)); - } - - return source; - } - /// /// Alters the contrast component of the image. /// /// The image this method extends. /// The new contrast of the image. Must be between -100 and 100. /// The . - public static Image Contrast(this Image source, int amount) => source.Filter(new Contrast(amount)); - - /// - /// Performs the given action on the source image. - /// - /// The image to perform the action against. - /// Whether to clone the image. - /// The to perform against the image. - /// The . - private static Image PerformAction(Image source, bool clone, Action action) - { - Image transformedImage = clone ? new Image(source) : new Image(source.Width, source.Height); - action(source, transformedImage); - - for (int i = 0; i < source.Frames.Count; i++) - { - ImageFrame frame = source.Frames[i]; - ImageFrame tranformedFrame = new ImageFrame(frame); - action(frame, tranformedFrame); - - if (!clone) - { - transformedImage.Frames.Add(tranformedFrame); - } - else - { - transformedImage.Frames[i] = tranformedFrame; - } - } - - return transformedImage; - } + public static Image Contrast(this Image source, int amount) => source.Process(new Contrast(amount)); } } diff --git a/src/ImageProcessor/IImage.cs b/src/ImageProcessor/IImage.cs index 82c9710ec..58f3380bd 100644 --- a/src/ImageProcessor/IImage.cs +++ b/src/ImageProcessor/IImage.cs @@ -85,7 +85,7 @@ namespace ImageProcessor void Save(Stream stream); /// - /// Saves the image to the given stream using the currently loaded image format. + /// Saves the image to the given stream using the given image format. /// /// The stream to save the image to. /// The format to save the image as. diff --git a/src/ImageProcessor/IImageProcessor.cs b/src/ImageProcessor/IImageProcessor.cs new file mode 100644 index 000000000..667f1ae30 --- /dev/null +++ b/src/ImageProcessor/IImageProcessor.cs @@ -0,0 +1,33 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor +{ + /// + /// Encapsulates methods to alter the pixels of an image. + /// + public interface IImageProcessor + { + /// + /// Apply a process to an image to alter the pixels at the area of the specified rectangle. + /// + /// Target image to apply the process to. + /// The source image. Cannot be null. + /// + /// The rectangle, which defines the area of the image where the process should be applied to. + /// + /// + /// The method keeps the source image unchanged and returns the + /// the result of image processing filter as new image. + /// + /// + /// is null or is null. + /// + /// + /// doesnt fit the dimension of the image. + /// + void Apply(ImageBase target, ImageBase source, Rectangle rectangle); + } +} diff --git a/src/ImageProcessor/Image.cs b/src/ImageProcessor/Image.cs index a75364ae2..d141568ee 100644 --- a/src/ImageProcessor/Image.cs +++ b/src/ImageProcessor/Image.cs @@ -1,13 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Image class which stores the pixels and provides common functionality -// such as loading images from files and streams or operation like resizing or cropping. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor { @@ -187,7 +181,7 @@ namespace ImageProcessor public IList Properties { get; } = new List(); /// - public IImageFormat CurrentImageFormat { get; private set; } + public IImageFormat CurrentImageFormat { get; internal set; } /// public void Save(Stream stream) diff --git a/src/ImageProcessor/ImageBase.cs b/src/ImageProcessor/ImageBase.cs index 890f6c861..7996cebc0 100644 --- a/src/ImageProcessor/ImageBase.cs +++ b/src/ImageProcessor/ImageBase.cs @@ -1,13 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// The base class of all images. Encapsulates all the properties and methods -// required to manipulate images. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor { @@ -168,7 +162,6 @@ namespace ImageProcessor throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height."); } #endif - int start = ((y * this.Width) + x) * 4; this.Pixels[start + 0] = value.B; diff --git a/src/ImageProcessor/ImageExtensions.cs b/src/ImageProcessor/ImageExtensions.cs new file mode 100644 index 000000000..2298f8785 --- /dev/null +++ b/src/ImageProcessor/ImageExtensions.cs @@ -0,0 +1,111 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor +{ + using System; + using System.IO; + + using Formats; + + /// + /// Exstension methods for the type. + /// + public static class ImageExtensions + { + /// + /// Saves the image to the given stream with the bmp format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + public static void SaveAsBmp(this ImageBase source, Stream stream) => new BmpEncoder().Encode(source, stream); + + /// + /// Saves the image to the given stream with the png format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + public static void SaveAsPng(this ImageBase source, Stream stream) => new PngEncoder().Encode(source, stream); + + /// + /// Saves the image to the given stream with the jpeg format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The quality to save the image to. Between 1 and 100. + /// Thrown if the stream is null. + public static void SaveAsJpeg(this ImageBase source, Stream stream, int quality = 80) => new JpegEncoder { Quality = quality }.Encode(source, stream); + + /// + /// Saves the image to the given stream with the gif format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The quality to save the image to representing the number of colors. Between 1 and 100. + /// Thrown if the stream is null. + public static void SaveAsGif(this ImageBase source, Stream stream, int quality = 256) => new GifEncoder() { Quality = quality }.Encode(source, stream); + + /// + /// Applies the collection of processors to the image. + /// + /// The image this method extends. + /// Any processors to apply to the image. + /// The . + public static Image Process(this Image source, params IImageProcessor[] processors) => Process(source, source.Bounds, processors); + + /// + /// Applies the collection of processors to the image. + /// + /// The image this method extends. + /// + /// The rectangle defining the bounds of the pixels the image filter with adjust. + /// Any processors to apply to the image. + /// The . + public static Image Process(this Image source, Rectangle rectangle, params IImageProcessor[] processors) + { + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (IImageProcessor filter in processors) + { + source = PerformAction(source, true, (sourceImage, targetImage) => filter.Apply(targetImage, sourceImage, rectangle)); + } + + return source; + } + + /// + /// Performs the given action on the source image. + /// + /// The image to perform the action against. + /// Whether to clone the image. + /// The to perform against the image. + /// The . + private static Image PerformAction(Image source, bool clone, Action action) + { + Image transformedImage = clone ? new Image(source) : new Image(); + transformedImage.CurrentImageFormat = source.CurrentImageFormat; + action(source, transformedImage); + + for (int i = 0; i < source.Frames.Count; i++) + { + ImageFrame sourceFrame = source.Frames[i]; + ImageFrame tranformedFrame = clone ? new ImageFrame(sourceFrame) : new ImageFrame(); + action(sourceFrame, tranformedFrame); + + if (!clone) + { + transformedImage.Frames.Add(tranformedFrame); + } + else + { + transformedImage.Frames[i] = tranformedFrame; + } + } + + return transformedImage; + } + } +} diff --git a/src/ImageProcessor/ImageFrame.cs b/src/ImageProcessor/ImageFrame.cs index b2d4f5235..84166507a 100644 --- a/src/ImageProcessor/ImageFrame.cs +++ b/src/ImageProcessor/ImageFrame.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Represents a single frame in a animation. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor { diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 377b7c363..8e779c1c2 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -13,7 +13,7 @@ en-US 512 {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile78 + Profile259 v4.5 ..\..\ true @@ -38,12 +38,14 @@ + - - + + + @@ -177,7 +179,6 @@ - diff --git a/src/ImageProcessor/Filters/ParallelImageFilter.cs b/src/ImageProcessor/ParallelImageProcessor.cs similarity index 52% rename from src/ImageProcessor/Filters/ParallelImageFilter.cs rename to src/ImageProcessor/ParallelImageProcessor.cs index 647e32515..3bc28e15d 100644 --- a/src/ImageProcessor/Filters/ParallelImageFilter.cs +++ b/src/ImageProcessor/ParallelImageProcessor.cs @@ -1,15 +1,20 @@ -namespace ImageProcessor.Filters +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor { using System; using System.Threading.Tasks; /// - /// Allows the application of filters using prallel processing. + /// Allows the application of processors using parallel processing. /// - public abstract class ParallelImageFilter : IImageFilter + public abstract class ParallelImageProcessor : IImageProcessor { /// - /// Gets or sets the count of workers to run the filter in parallel. + /// Gets or sets the count of workers to run the process in parallel. /// public int Parallelism { get; set; } = Environment.ProcessorCount; @@ -46,12 +51,26 @@ } /// - /// This method is called before the filter is applied to prepare the filter. + /// This method is called before the process is applied to prepare the processor. /// protected virtual void OnApply() { } + /// + /// Apply a process to an image to alter the pixels at the area of the specified rectangle. + /// + /// Target image to apply the process to. + /// The source image. Cannot be null. + /// + /// The rectangle, which defines the area of the image where the process should be applied to. + /// + /// The index of the row within the image to start processing. + /// The index of the row within the image to end processing. + /// + /// The method keeps the source image unchanged and returns the + /// the result of image processing filter as new image. + /// protected abstract void Apply(ImageBase target, ImageBase source, Rectangle rectangle, int startY, int endY); } } diff --git a/src/ImageProcessor/Samplers/IImageSampler.cs b/src/ImageProcessor/Samplers/IImageSampler.cs deleted file mode 100644 index 24e815ef6..000000000 --- a/src/ImageProcessor/Samplers/IImageSampler.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor.Samplers -{ - /// - /// Encapsulates the methods required for all image sampling (resizing) algorithms. - /// - public interface IImageSampler - { - /// - /// Resizes the specified source image by creating a new image with - /// the specified size which is a resized version of the passed image. - /// - /// The source image. - /// The target image. - /// The width. - /// The height. - void Sample(ImageBase source, ImageBase target, int width, int height); - } -} diff --git a/tests/ImageProcessor.Tests/Filters/FilterTests.cs b/tests/ImageProcessor.Tests/Filters/FilterTests.cs index e6b7b4944..a6452d37a 100644 --- a/tests/ImageProcessor.Tests/Filters/FilterTests.cs +++ b/tests/ImageProcessor.Tests/Filters/FilterTests.cs @@ -23,7 +23,7 @@ namespace ImageProcessor.Tests.Filters //{ "../../TestImages/Formats/Gif/giphy.gif" }, }; - public static readonly TheoryData Filters = new TheoryData + public static readonly TheoryData Filters = new TheoryData { { "Contrast-50", new Contrast(50) }, { "Contrast--50", new Contrast(-50) }, @@ -31,7 +31,7 @@ namespace ImageProcessor.Tests.Filters [Theory] [MemberData("Filters")] - public void FilterImage(string name, IImageFilter filter) + public void FilterImage(string name, IImageProcessor processor) { if (!Directory.Exists("Filtered")) { @@ -47,7 +47,7 @@ namespace ImageProcessor.Tests.Filters string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); using (FileStream output = File.OpenWrite($"Filtered/{ Path.GetFileName(filename) }")) { - image.Filter(filter).Save(output); + image.Process(processor).Save(output); } Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms"); diff --git a/tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs b/tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs index 713adf60d..74c8ab304 100644 --- a/tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs +++ b/tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs @@ -31,29 +31,10 @@ string encodedFilename = "Encoded/" + Path.GetFileName(filename); - //if (!image.IsAnimated) - //{ using (FileStream output = File.OpenWrite(encodedFilename)) { image.Save(output); } - //} - //else - //{ - // using (var output = File.OpenWrite( - // string.Format("Encoded/{ Path.GetFileNameWithoutExtension(filename) }.jpg")) - // { - // image.SaveAsJpeg(output, 40); - // } - - // for (int i = 0; i < image.Frames.Count; i++) - // { - // using (var output = File.OpenWrite($"Encoded/{ i }_{ Path.GetFileNameWithoutExtension(filename) }.png")) - // { - // image.Frames[i].SaveAsPng(output); - // } - // } - //} Trace.WriteLine($"{filename} : {watch.ElapsedMilliseconds}ms"); } @@ -75,7 +56,7 @@ using (FileStream output = File.OpenWrite($"Quantized/{ Path.GetFileName(filename) }")) { - quantizedImage.ToImage().Save(output, image.CurrentImageFormat); + quantizedImage.ToImage().Save(output); } } }