diff --git a/README.md b/README.md index 4eeefd54b1..5016825043 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ -**ImageProcessorCore** is a new cross-platform 2D graphics API to allow the processing of images without the use of `System.Drawing`. It's still in early stages but progress has been pretty quick. +**ImageProcessorCore** is a new cross-platform 2D graphics API designed to allow the processing of images without the use of `System.Drawing`. It's still in early stages but progress has been pretty quick. [![Build status](https://ci.appveyor.com/api/projects/status/8ypr7527dnao04yr/branch/Core?svg=true)](https://ci.appveyor.com/project/JamesSouth/imageprocessor/branch/Core) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/JimBobSquarePants/ImageProcessor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -182,8 +182,11 @@ using (Image image = new Image(stream)) new Sobel { Greyscale = true } }; - image.Process(processors.ToArray()) - .Save(output); + foreach (IImageProcessor processor in processors){ + + image.Process(processor) + .Save(output); + } } ``` Individual processors can be initialised and apply processing against images. This allows nesting which will allow the powerful combination of processing methods: diff --git a/src/ImageProcessorCore/Colors/Color.cs b/src/ImageProcessorCore/Colors/Color.cs index b86a2dfa05..0e47e03e1b 100644 --- a/src/ImageProcessorCore/Colors/Color.cs +++ b/src/ImageProcessorCore/Colors/Color.cs @@ -8,6 +8,7 @@ namespace ImageProcessorCore using System; using System.ComponentModel; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Represents a four-component color using red, green, blue, and alpha data. @@ -316,6 +317,7 @@ namespace ImageProcessorCore /// /// The /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color Average(Color first, Color second) { return new Color((first.backingVector + second.backingVector) * .5f); @@ -361,6 +363,7 @@ namespace ImageProcessorCore /// /// The to convert. /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color FromNonPremultiplied(Color color) { return new Color(FromNonPremultiplied(color.backingVector, color.A)); @@ -383,6 +386,7 @@ namespace ImageProcessorCore /// /// The to convert. /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color ToNonPremultiplied(Color color) { float a = color.A; @@ -398,6 +402,7 @@ namespace ImageProcessorCore /// Gets a representation for this . /// /// A representation for this object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.R, this.G, this.B, this.A); @@ -430,6 +435,7 @@ namespace ImageProcessorCore } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { if (obj is Color) @@ -447,6 +453,7 @@ namespace ImageProcessorCore } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool AlmostEquals(Color other, float precision) { Vector4 result = Vector4.Abs(this.backingVector - other.backingVector); @@ -466,6 +473,7 @@ namespace ImageProcessorCore /// /// The . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float Compress(float signal) { if (signal <= 0.0031308f) @@ -485,6 +493,7 @@ namespace ImageProcessorCore /// /// The . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float Expand(float signal) { if (signal <= 0.04045f) diff --git a/src/ImageProcessorCore/ImageExtensions.cs b/src/ImageProcessorCore/ImageExtensions.cs index 0e9603b515..597c681df2 100644 --- a/src/ImageProcessorCore/ImageExtensions.cs +++ b/src/ImageProcessorCore/ImageExtensions.cs @@ -56,11 +56,11 @@ namespace ImageProcessorCore /// This method does not resize the target image. /// /// The image this method extends. - /// Any processors to apply to the image. + /// The processor to apply to the image. /// The . - public static Image Process(this Image source, params IImageProcessor[] processors) + public static Image Process(this Image source, IImageProcessor processor) { - return Process(source, source.Bounds, processors); + return Process(source, source.Bounds, processor); } /// @@ -71,15 +71,11 @@ namespace ImageProcessorCore /// /// The structure that specifies the portion of the image object to draw. /// - /// Any processors to apply to the image. + /// The processors to apply to the image. /// The . - public static Image Process(this Image source, Rectangle sourceRectangle, params IImageProcessor[] processors) + public static Image Process(this Image source, Rectangle sourceRectangle, IImageProcessor processor) { - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (IImageProcessor filter in processors) - { - source = PerformAction(source, true, (sourceImage, targetImage) => filter.Apply(targetImage, sourceImage, sourceRectangle)); - } + source = PerformAction(source, true, (sourceImage, targetImage) => processor.Apply(targetImage, sourceImage, sourceRectangle)); return source; } @@ -93,11 +89,11 @@ namespace ImageProcessorCore /// The source image. Cannot be null. /// The target image width. /// The target image height. - /// Any processors to apply to the image. + /// The processor to apply to the image. /// The . - public static Image Process(this Image source, int width, int height, params IImageSampler[] processors) + internal static Image Process(this Image source, int width, int height, IImageSampler sampler) { - return Process(source, width, height, source.Bounds, default(Rectangle), processors); + return Process(source, width, height, source.Bounds, default(Rectangle), sampler); } /// @@ -117,15 +113,11 @@ namespace ImageProcessorCore /// The structure that specifies the location and size of the drawn image. /// The image is scaled to fit the rectangle. /// - /// Any processors to apply to the image. + /// The processor to apply to the image. /// The . - public static Image Process(this Image source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle, params IImageSampler[] processors) + public static Image Process(this Image source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle, IImageSampler sampler) { - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (IImageSampler sampler in processors) - { - source = PerformAction(source, false, (sourceImage, targetImage) => sampler.Apply(targetImage, sourceImage, width, height, targetRectangle, sourceRectangle)); - } + source = PerformAction(source, false, (sourceImage, targetImage) => sampler.Apply(targetImage, sourceImage, width, height, targetRectangle, sourceRectangle)); return source; } diff --git a/src/ImageProcessorCore/Samplers/Crop.cs b/src/ImageProcessorCore/Samplers/Crop.cs index 3c315c574d..3ee47149bb 100644 --- a/src/ImageProcessorCore/Samplers/Crop.cs +++ b/src/ImageProcessorCore/Samplers/Crop.cs @@ -19,6 +19,8 @@ namespace ImageProcessorCore.Samplers int targetBottom = targetRectangle.Bottom; int startX = targetRectangle.X; int endX = targetRectangle.Right; + int sourceX = sourceRectangle.X; + int sourceY = sourceRectangle.Y; Parallel.For( startY, @@ -29,7 +31,7 @@ namespace ImageProcessorCore.Samplers { for (int x = startX; x < endX; x++) { - target[x, y] = source[x + sourceRectangle.X, y + sourceRectangle.Y]; + target[x, y] = source[x + sourceX, y + sourceY]; } this.OnRowProcessed(); diff --git a/tests/ImageProcessorCore.Benchmarks/Color/ColorEquality.cs b/tests/ImageProcessorCore.Benchmarks/Color/ColorEquality.cs new file mode 100644 index 0000000000..e9ef2c1835 --- /dev/null +++ b/tests/ImageProcessorCore.Benchmarks/Color/ColorEquality.cs @@ -0,0 +1,22 @@ +namespace ImageProcessorCore.Benchmarks +{ + using BenchmarkDotNet.Attributes; + + using CoreColor = ImageProcessorCore.Color; + using SystemColor = System.Drawing.Color; + + public class ColorEquality + { + [Benchmark(Baseline = true, Description = "System.Drawing Color Equals")] + public bool SystemDrawingColorEqual() + { + return SystemColor.FromArgb(128, 128, 128, 128).Equals(SystemColor.FromArgb(128, 128, 128, 128)); + } + + [Benchmark(Description = "ImageProcessorCore Color Equals")] + public bool ColorEqual() + { + return new CoreColor(.5f, .5f, .5f, .5f).Equals(new CoreColor(.5f, .5f, .5f, .5f)); + } + } +} diff --git a/tests/ImageProcessorCore.Benchmarks/Colors.cs b/tests/ImageProcessorCore.Benchmarks/Colors.cs deleted file mode 100644 index 0a7d54c994..0000000000 --- a/tests/ImageProcessorCore.Benchmarks/Colors.cs +++ /dev/null @@ -1,23 +0,0 @@ -using BenchmarkDotNet.Attributes; - -namespace ImageProcessorCore.Benchmarks -{ - using System.Drawing; - - using CoreColor = ImageProcessorCore.Color; - - public class Colors - { - [Benchmark(Baseline = true, Description = "System.Drawing Color")] - public bool SystemDrawingColorEqual() - { - return Color.FromArgb(128, 128, 128, 128).Equals(Color.FromArgb(128, 128, 128, 128)); - } - - [Benchmark(Description = "ImageProcessorCore Color")] - public bool ColorEqual() - { - return new CoreColor(.5f, .5f, .5f, .5f).Equals(new CoreColor(.5f, .5f, .5f, .5f)); - } - } -} diff --git a/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs b/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs new file mode 100644 index 0000000000..fce14beb7f --- /dev/null +++ b/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs @@ -0,0 +1,33 @@ +namespace ImageProcessorCore.Benchmarks.Image +{ + using System.Drawing; + + using BenchmarkDotNet.Attributes; + + using CoreColor = ImageProcessorCore.Color; + using CoreImage = ImageProcessorCore.Image; + using SystemColor = System.Drawing.Color; + + public class GetSetPixel + { + [Benchmark(Baseline = true, Description = "System.Drawing GetSet Pixel")] + public SystemColor ResizeSystemDrawing() + { + using (Bitmap source = new Bitmap(400, 400)) + { + source.SetPixel(200, 200, SystemColor.White); + return source.GetPixel(200, 200); + } + } + + [Benchmark(Description = "ImageProcessorCore GetSet Pixel")] + public CoreColor ResizeCore() + { + using (CoreImage image = new CoreImage(400, 400)) + { + image[200, 200] = CoreColor.White; + return image[200, 200]; + } + } + } +} diff --git a/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs b/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs new file mode 100644 index 0000000000..85bc4190a2 --- /dev/null +++ b/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs @@ -0,0 +1,44 @@ +namespace ImageProcessorCore.Benchmarks +{ + using System.Drawing; + using System.Drawing.Drawing2D; + + using BenchmarkDotNet.Attributes; + + using ImageProcessorCore.Samplers; + using CoreImage = ImageProcessorCore.Image; + using CoreSize = ImageProcessorCore.Size; + + public class Crop + { + [Benchmark(Baseline = true, Description = "System.Drawing Crop")] + public Size CropSystemDrawing() + { + using (Bitmap source = new Bitmap(400, 400)) + { + using (Bitmap destination = new Bitmap(100, 100)) + { + using (Graphics graphics = Graphics.FromImage(destination)) + { + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.DrawImage(source, new Rectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel); + } + + return destination.Size; + } + } + } + + [Benchmark(Description = "ImageProcessorCore Crop")] + public CoreSize CropResizeCore() + { + using (CoreImage image = new CoreImage(400, 400)) + { + image.Crop(100, 100); + return new CoreSize(image.Width, image.Height); + } + } + } +} diff --git a/tests/ImageProcessorCore.Benchmarks/Resize.cs b/tests/ImageProcessorCore.Benchmarks/Samplers/Resize.cs similarity index 100% rename from tests/ImageProcessorCore.Benchmarks/Resize.cs rename to tests/ImageProcessorCore.Benchmarks/Samplers/Resize.cs