diff --git a/README.md b/README.md index 0ea2325cc..af25681e9 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,8 @@ We already have a [MyGet package repository](https://www.myget.org/gallery/image If you prefer, you can compile ImageProcessorCore yourself (please do and help!), you'll need: -- Visual Studio 2015 (or above) -- The [.NET Core SDK Installer -(Preview 1)](https://www.microsoft.com/net/download) - Click `.NET Core SDK Installer -(Preview 1)` -- The [.NET Core Tooling Preview 1 for Visual Studio 2015](https://dev.windows.com/en-us/downloads) - Click `.NET Core Tooling Preview 1 for Visual Studio 2015`. +- [Visual Studio 2015 with Update 3 (or above)](https://www.visualstudio.com/news/releasenotes/vs2015-update3-vs) +- The [.NET Core 1.0 SDK Installer](https://www.microsoft.com/net/core#windows) - Non VSCode link. To clone it locally click the "Clone in Windows" button above or run the following git commands. diff --git a/Rebracer.xml b/Rebracer.xml index 8497d212e..9b0a654d2 100644 --- a/Rebracer.xml +++ b/Rebracer.xml @@ -22,8 +22,12 @@ + 0 + 0 1 - 1 + 1 + -1 + -1 0 1 0 @@ -81,7 +85,7 @@ 1 0 1 - 1 + 0 0 0 1 diff --git a/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs b/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs index 72ba79f9b..8ed703e03 100644 --- a/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// An to change the Alpha of an . /// - public class AlphaProcessor : ParallelImageProcessor + public class AlphaProcessor : ImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs index ef451a89d..e78987d23 100644 --- a/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors /// /// Sets the background color of the image. /// - public class BackgroundColorProcessor : ParallelImageProcessor + public class BackgroundColorProcessor : ImageProcessor { /// /// The epsilon for comparing floating point numbers. diff --git a/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs index dc3816a03..60b49d0ee 100644 --- a/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs @@ -13,7 +13,7 @@ namespace ImageProcessorCore.Processors /// . The image will be converted to greyscale before thresholding /// occurs. /// - public class ThresholdProcessor : ParallelImageProcessor + public class ThresholdProcessor : ImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs index cbf966a7c..5de0741fa 100644 --- a/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Processors /// /// Combines two images together by blending the pixels. /// - public class BlendProcessor : ParallelImageProcessor + public class BlendProcessor : ImageProcessor { /// /// The image to blend. diff --git a/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs index a100e5717..6710ff593 100644 --- a/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// An to change the brightness of an . /// - public class BrightnessProcessor : ParallelImageProcessor + public class BrightnessProcessor : ImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs index eb01c3c22..d27ea5c6d 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors /// /// The color matrix filter. /// - public abstract class ColorMatrixFilter : ParallelImageProcessor, IColorMatrixFilter + public abstract class ColorMatrixFilter : ImageProcessor, IColorMatrixFilter { /// public abstract Matrix4x4 Matrix { get; } @@ -22,8 +22,6 @@ namespace ImageProcessorCore.Processors /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - int sourceY = sourceRectangle.Y; - int sourceBottom = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; Matrix4x4 matrix = this.Matrix; @@ -36,15 +34,12 @@ namespace ImageProcessorCore.Processors endY, y => { - if (y >= sourceY && y < sourceBottom) + for (int x = startX; x < endX; x++) { - for (int x = startX; x < endX; x++) - { - targetPixels[x, y] = this.ApplyMatrix(sourcePixels[x, y], matrix); - } - - this.OnRowProcessed(); + targetPixels[x, y] = this.ApplyMatrix(sourcePixels[x, y], matrix); } + + this.OnRowProcessed(); }); } } diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs index 4b507fcd1..242299433 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs @@ -24,7 +24,7 @@ namespace ImageProcessorCore.Processors }; /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { new VignetteProcessor { Color = new Color(0, 10 / 255f, 0) }.Apply(target, target, targetRectangle); } diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs index dce2609e5..ea6f85a39 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs @@ -30,7 +30,7 @@ namespace ImageProcessorCore.Processors }; /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { new VignetteProcessor { Color = new Color(102 / 255f, 34 / 255f, 0) }.Apply(target, target, targetRectangle); new GlowProcessor diff --git a/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs index 920a6fb01..19f564be4 100644 --- a/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// An to change the contrast of an . /// - public class ContrastProcessor : ParallelImageProcessor + public class ContrastProcessor : ImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs index 4ad60d65b..042288ffd 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs @@ -42,9 +42,6 @@ namespace ImageProcessorCore.Processors /// public override float[,] KernelY => this.kernelY; - /// - public override int Parallelism => 1; - /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs index 0dad68485..b16a528db 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors /// /// Defines a filter that uses two one-dimensional matrices to perform convolution against an image. /// - public abstract class Convolution2DFilter : ParallelImageProcessor + public abstract class Convolution2DFilter : ImageProcessor { /// /// Gets the horizontal gradient operator. diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs index 66e8cac98..c9031dfd7 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Processors /// /// Defines a filter that uses two one-dimensional matrices to perform two-pass convolution against an image. /// - public abstract class Convolution2PassFilter : ParallelImageProcessor + public abstract class Convolution2PassFilter : ImageProcessor { /// /// Gets the horizontal gradient operator. @@ -64,7 +64,6 @@ namespace ImageProcessorCore.Processors int radiusY = kernelHeight >> 1; int radiusX = kernelWidth >> 1; - int sourceY = sourceRectangle.Y; int sourceBottom = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; @@ -79,36 +78,34 @@ namespace ImageProcessorCore.Processors endY, y => { - if (y >= sourceY && y < sourceBottom) + for (int x = startX; x < endX; x++) { - for (int x = startX; x < endX; x++) - { - Color destination = new Color(); + Color destination = new Color(); - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelHeight; fy++) - { - int fyr = fy - radiusY; - int offsetY = y + fyr; + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelHeight; fy++) + { + int fyr = fy - radiusY; + int offsetY = y + fyr; - offsetY = offsetY.Clamp(0, maxY); + offsetY = offsetY.Clamp(0, maxY); - for (int fx = 0; fx < kernelWidth; fx++) - { - int fxr = fx - radiusX; - int offsetX = x + fxr; + for (int fx = 0; fx < kernelWidth; fx++) + { + int fxr = fx - radiusX; + int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + offsetX = offsetX.Clamp(0, maxX); - Color currentColor = sourcePixels[offsetX, offsetY]; - destination += kernel[fy, fx] * currentColor; - } + Color currentColor = sourcePixels[offsetX, offsetY]; + destination += kernel[fy, fx] * currentColor; } - - targetPixels[x, y] = destination; } - this.OnRowProcessed(); + + targetPixels[x, y] = destination; } + + this.OnRowProcessed(); }); } } diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs index 84884ff83..8ddcab4a5 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Processors /// /// Defines a filter that uses a 2 dimensional matrix to perform convolution against an image. /// - public abstract class ConvolutionFilter : ParallelImageProcessor + public abstract class ConvolutionFilter : ImageProcessor { /// /// Gets the 2d gradient operator. diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs index 904de5994..6288ea963 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs @@ -76,9 +76,6 @@ namespace ImageProcessorCore.Processors /// public override float[,] KernelY => this.kernelY; - /// - public override int Parallelism => 1; - /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs index 8d95eaf82..9d70732d2 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs @@ -78,9 +78,6 @@ namespace ImageProcessorCore.Processors /// public override float[,] KernelY => this.kernelY; - /// - public override int Parallelism => 1; - /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { diff --git a/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs b/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs index 63c18e9eb..e046be6a9 100644 --- a/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// Creates a glow effect on the image /// - public class GlowProcessor : ParallelImageProcessor + public class GlowProcessor : ImageProcessor { /// /// Gets or sets the glow color to apply. diff --git a/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs b/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs index 31ef3ceb8..1f319b92b 100644 --- a/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors /// /// An to invert the colors of an . /// - public class InvertProcessor : ParallelImageProcessor + public class InvertProcessor : ImageProcessor { /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) diff --git a/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs b/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs index 6c129bbee..4769799b5 100644 --- a/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// An to invert the colors of an . /// - public class PixelateProcessor : ParallelImageProcessor + public class PixelateProcessor : ImageProcessor { /// /// Initializes a new instance of the class. @@ -27,9 +27,6 @@ namespace ImageProcessorCore.Processors this.Value = size; } - /// - public override int Parallelism { get; set; } = 1; - /// /// Gets or the pixel size. /// @@ -59,7 +56,6 @@ namespace ImageProcessorCore.Processors { for (int x = startX; x < endX; x += size) { - int offsetX = offset; int offsetY = offset; @@ -88,6 +84,7 @@ namespace ImageProcessorCore.Processors } } } + this.OnRowProcessed(); } }); diff --git a/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs b/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs index 6a1bbc684..1fd4630ed 100644 --- a/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// Creates a vignette effect on the image /// - public class VignetteProcessor : ParallelImageProcessor + public class VignetteProcessor : ImageProcessor { /// /// Gets or sets the vignette color to apply. diff --git a/src/ImageProcessorCore/ParallelImageProcessor.cs b/src/ImageProcessorCore/ImageProcessor.cs similarity index 69% rename from src/ImageProcessorCore/ParallelImageProcessor.cs rename to src/ImageProcessorCore/ImageProcessor.cs index a6b9fc354..ea76ebbf8 100644 --- a/src/ImageProcessorCore/ParallelImageProcessor.cs +++ b/src/ImageProcessorCore/ImageProcessor.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -7,21 +7,15 @@ namespace ImageProcessorCore.Processors { using System; using System.Threading; - using System.Threading.Tasks; /// - /// Allows the application of processors using parallel processing. + /// Allows the application of processors to images. /// - public abstract class ParallelImageProcessor : IImageProcessor + public abstract class ImageProcessor : IImageProcessor { /// public event ProgressEventHandler OnProgress; - /// - /// Gets or sets the count of workers to run the process in parallel. - /// - public virtual int Parallelism { get; set; } = Environment.ProcessorCount * 2; - /// /// The number of rows processed by a derived class. /// @@ -42,34 +36,7 @@ namespace ImageProcessorCore.Processors this.numRowsProcessed = 0; this.totalRows = sourceRectangle.Height; - if (this.Parallelism > 1) - { - int partitionCount = this.Parallelism; - - Task[] tasks = new Task[partitionCount]; - - for (int p = 0; p < partitionCount; p++) - { - int current = p; - tasks[p] = Task.Run( - () => - { - int batchSize = sourceRectangle.Height / partitionCount; - int yStart = sourceRectangle.Y + (current * batchSize); - int yEnd = current == partitionCount - 1 - ? sourceRectangle.Bottom - : yStart + batchSize; - - this.Apply(target, source, target.Bounds, sourceRectangle, yStart, yEnd); - }); - } - - Task.WaitAll(tasks); - } - else - { - this.Apply(target, source, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom); - } + this.Apply(target, source, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom); this.AfterApply(target, source, target.Bounds, sourceRectangle); } @@ -88,6 +55,7 @@ namespace ImageProcessorCore.Processors float[] pixels = new float[width * height * 4]; target.SetPixels(width, height, pixels); + // Ensure we always have bounds. if (sourceRectangle == Rectangle.Empty) { sourceRectangle = source.Bounds; @@ -101,33 +69,9 @@ namespace ImageProcessorCore.Processors this.OnApply(target, source, targetRectangle, sourceRectangle); this.numRowsProcessed = 0; - this.totalRows = targetRectangle.Bottom; + this.totalRows = targetRectangle.Height; - if (this.Parallelism > 1) - { - int partitionCount = this.Parallelism; - - Task[] tasks = new Task[partitionCount]; - - for (int p = 0; p < partitionCount; p++) - { - int current = p; - tasks[p] = Task.Run(() => - { - int batchSize = targetRectangle.Bottom / partitionCount; - int yStart = current * batchSize; - int yEnd = current == partitionCount - 1 ? targetRectangle.Bottom : yStart + batchSize; - - this.Apply(target, source, targetRectangle, sourceRectangle, yStart, yEnd); - }); - } - - Task.WaitAll(tasks); - } - else - { - this.Apply(target, source, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom); - } + this.Apply(target, source, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom); this.AfterApply(target, source, target.Bounds, sourceRectangle); } diff --git a/src/ImageProcessorCore/PixelAccessor.cs b/src/ImageProcessorCore/PixelAccessor.cs index 23749769b..2553b2536 100644 --- a/src/ImageProcessorCore/PixelAccessor.cs +++ b/src/ImageProcessorCore/PixelAccessor.cs @@ -16,7 +16,7 @@ namespace ImageProcessorCore /// /// The position of the first pixel in the bitmap. /// - private float* pixelsBase; + private Color* pixelsBase; /// /// Provides a way to access the pixels from unmanaged memory. @@ -48,24 +48,12 @@ namespace ImageProcessorCore Guard.MustBeGreaterThan(image.Width, 0, "image width"); Guard.MustBeGreaterThan(image.Height, 0, "image height"); - int size = image.Pixels.Length; this.Width = image.Width; this.Height = image.Height; // Assign the pointer. - // If buffer is allocated on Large Object Heap i.e > 85Kb, then we are going to pin it instead of making a copy. - if (size > 87040) - { - this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned); - this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); - } - else - { - fixed (float* pbuffer = image.Pixels) - { - this.pixelsBase = pbuffer; - } - } + this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned); + this.pixelsBase = (Color*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); } /// @@ -113,7 +101,7 @@ namespace ImageProcessorCore throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height."); } #endif - return *((Color*)(this.pixelsBase + ((y * this.Width) + x) * 4)); + return *(this.pixelsBase + ((y * this.Width) + x)); } set @@ -129,7 +117,7 @@ namespace ImageProcessorCore throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height."); } #endif - *(Color*)(this.pixelsBase + (((y * this.Width) + x) * 4)) = value; + *(this.pixelsBase + ((y * this.Width) + x)) = value; } } diff --git a/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs index e6707afa0..adcd3c180 100644 --- a/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs @@ -15,8 +15,6 @@ namespace ImageProcessorCore.Processors /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - int targetY = targetRectangle.Y; - int targetBottom = targetRectangle.Bottom; int startX = targetRectangle.X; int endX = targetRectangle.Right; int sourceX = sourceRectangle.X; @@ -30,15 +28,12 @@ namespace ImageProcessorCore.Processors endY, y => { - if (y >= targetY && y < targetBottom) + for (int x = startX; x < endX; x++) { - for (int x = startX; x < endX; x++) - { - targetPixels[x, y] = sourcePixels[x + sourceX, y + sourceY]; - } - - this.OnRowProcessed(); + targetPixels[x, y] = sourcePixels[x + sourceX, y + sourceY]; } + + this.OnRowProcessed(); }); } } diff --git a/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs index e0ffb29e1..f9f8601f5 100644 --- a/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs @@ -92,7 +92,7 @@ namespace ImageProcessorCore.Processors } /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { // Copy the pixels over. if (source.Bounds == target.Bounds) diff --git a/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs b/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs index cc8bfe4cc..adfe77432 100644 --- a/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs +++ b/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs @@ -9,7 +9,7 @@ namespace ImageProcessorCore.Processors /// Applies sampling methods to an image. /// All processors requiring resampling or resizing should inherit from this. /// - public abstract class ImageSampler : ParallelImageProcessor, IImageSampler + public abstract class ImageSampler : ImageProcessor, IImageSampler { /// public virtual bool Compand { get; set; } = false; diff --git a/src/ImageProcessorCore/Samplers/Processors/Matrix3x2Processor.cs b/src/ImageProcessorCore/Samplers/Processors/Matrix3x2Processor.cs new file mode 100644 index 000000000..e9b0441e3 --- /dev/null +++ b/src/ImageProcessorCore/Samplers/Processors/Matrix3x2Processor.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System.Numerics; + + /// + /// Provides methods to transform an image using a . + /// + public abstract class Matrix3x2Processor : ImageSampler + { + /// + /// Creates a new target to contain the results of the matrix transform. + /// + /// Target image to apply the process to. + /// The source rectangle. + /// The processing matrix. + protected static void CreateNewTarget(ImageBase target, Rectangle sourceRectangle, Matrix3x2 processMatrix) + { + Matrix3x2 sizeMatrix; + if (Matrix3x2.Invert(processMatrix, out sizeMatrix)) + { + Rectangle rectangle = ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix); + target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]); + } + } + + /// + /// Gets a transform matrix adjusted to center upon the target image bounds. + /// + /// Target image to apply the process to. + /// The source image. + /// The transform matrix. + /// + /// The . + /// + protected static Matrix3x2 GetCenteredMatrix(ImageBase target, ImageBase source, Matrix3x2 matrix) + { + Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(-target.Width / 2f, -target.Height / 2f); + Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width / 2f, source.Height / 2f); + return (translationToTargetCenter * matrix) * translateToSourceCenter; + } + } +} diff --git a/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs index 82b07d40b..95ead2bf0 100644 --- a/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs @@ -31,9 +31,6 @@ namespace ImageProcessorCore.Processors this.Sampler = sampler; } - /// - public override int Parallelism { get; set; } = 1; - /// /// Gets the sampler to perform the resize operation. /// @@ -62,13 +59,7 @@ namespace ImageProcessorCore.Processors } /// - protected override void Apply( - ImageBase target, - ImageBase source, - Rectangle targetRectangle, - Rectangle sourceRectangle, - int startY, - int endY) + protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { // Jump out, we'll deal with that later. if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle) @@ -217,7 +208,7 @@ namespace ImageProcessorCore.Processors } /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { // Copy the pixels over. if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle) diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs index 263242fc1..6ef8866ba 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs @@ -34,9 +34,6 @@ namespace ImageProcessorCore.Processors /// public RotateType RotateType { get; } - /// - public override int Parallelism { get; set; } = 1; - /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs index 59d665d2e..7aafe0e08 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs @@ -11,85 +11,57 @@ namespace ImageProcessorCore.Processors /// /// Provides methods that allow the rotating of images. /// - public class RotateProcessor : ImageSampler + public class RotateProcessor : Matrix3x2Processor { - /// - public override int Parallelism { get; set; } = 1; - /// - /// Gets or sets the angle of rotation in degrees. + /// The tranform matrix to apply. /// - public float Angle { get; set; } + private Matrix3x2 processMatrix; /// - /// Gets or sets the center point. + /// Gets or sets the angle of processMatrix in degrees. /// - public Point Center { get; set; } + public float Angle { get; set; } /// /// Gets or sets a value indicating whether to expand the canvas to fit the rotated image. /// - public bool Expand { get; set; } + public bool Expand { get; set; } = true; /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { + processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle); if (this.Expand) { - Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center; - Matrix3x2 rotation = Point.CreateRotation(centre, -this.Angle); - Matrix3x2 invertedRotation; - Matrix3x2.Invert(rotation, out invertedRotation); - Rectangle bounds = ImageMaths.GetBoundingRectangle(source.Bounds, invertedRotation); - target.SetPixels(bounds.Width, bounds.Height, new float[bounds.Width * bounds.Height * 4]); + CreateNewTarget(target, sourceRectangle, processMatrix); } } /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - int height = target.Height; - int startX = 0; - int endX = target.Width; - Point centre = this.Center == Point.Empty ? Rectangle.Center(target.Bounds) : this.Center; - - //Matrix3x2 invertedRotation; - Matrix3x2 rotation = Point.CreateRotation(centre, -this.Angle); - //Matrix3x2.Invert(rotation, out invertedRotation); - //Vector2 rightTop = Vector2.Transform(new Vector2(source.Width, 0), invertedRotation); - //Vector2 leftBottom = Vector2.Transform(new Vector2(0, source.Height), invertedRotation); - - //if (this.Angle < 0) - //{ - // rotation = Point.CreateRotation(new Point((int)-leftBottom.X, (int)leftBottom.Y), -this.Angle); - //} - - //if (this.Angle > 0) - //{ - // rotation = Point.CreateRotation(new Point((int)rightTop.X, (int)-rightTop.Y), -this.Angle); - //} + Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix); - // Since we are not working in parallel we use full height and width - // of the first pass image. using (PixelAccessor sourcePixels = source.Lock()) using (PixelAccessor targetPixels = target.Lock()) { Parallel.For( 0, - height, + target.Height, y => + { + for (int x = 0; x < target.Width; x++) { - for (int x = startX; x < endX; x++) + Point transformedPoint = Point.Rotate(new Point(x, y), matrix); + if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) { - Point rotated = Point.Rotate(new Point(x, y), rotation); - if (source.Bounds.Contains(rotated.X, rotated.Y)) - { - targetPixels[x, y] = sourcePixels[rotated.X, rotated.Y]; - } + targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; } + } - this.OnRowProcessed(); - }); + OnRowProcessed(); + }); } } } diff --git a/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs index 07760cf2a..02d5dd9e4 100644 --- a/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs @@ -11,10 +11,12 @@ namespace ImageProcessorCore.Processors /// /// Provides methods that allow the skewing of images. /// - public class SkewProcessor : ImageSampler + public class SkewProcessor : Matrix3x2Processor { - /// - public override int Parallelism { get; set; } = 1; + /// + /// The tranform matrix to apply. + /// + private Matrix3x2 processMatrix; /// /// Gets or sets the angle of rotation along the x-axis in degrees. @@ -26,77 +28,45 @@ namespace ImageProcessorCore.Processors /// public float AngleY { get; set; } - /// - /// Gets or sets the center point. - /// - public Point Center { get; set; } - /// /// Gets or sets a value indicating whether to expand the canvas to fit the skewed image. /// - public bool Expand { get; set; } + public bool Expand { get; set; } = true; /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { + this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY); if (this.Expand) { - Point centre = this.Center; - Matrix3x2 skew = Point.CreateSkew(centre, -this.AngleX, -this.AngleY); - Matrix3x2 invertedSkew; - Matrix3x2.Invert(skew, out invertedSkew); - Rectangle bounds = ImageMaths.GetBoundingRectangle(source.Bounds, invertedSkew); - target.SetPixels(bounds.Width, bounds.Height, new float[bounds.Width * bounds.Height * 4]); + CreateNewTarget(target, sourceRectangle, this.processMatrix); } } /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - int height = target.Height; - int startX = 0; - int endX = target.Width; - Point centre = this.Center; - - Matrix3x2 invertedSkew; - Matrix3x2 skew = Point.CreateSkew(centre, -this.AngleX, -this.AngleY); - Matrix3x2.Invert(skew, out invertedSkew); - Vector2 rightTop = Vector2.Transform(new Vector2(source.Width, 0), invertedSkew); - Vector2 leftBottom = Vector2.Transform(new Vector2(0, source.Height), invertedSkew); - - if (this.AngleX < 0 && this.AngleY > 0) - { - skew = Point.CreateSkew(new Point((int)-leftBottom.X, (int)leftBottom.Y), -this.AngleX, -this.AngleY); - } - - if (this.AngleX > 0 && this.AngleY < 0) - { - skew = Point.CreateSkew(new Point((int)rightTop.X, (int)-rightTop.Y), -this.AngleX, -this.AngleY); - } - - if (this.AngleX < 0 && this.AngleY < 0) - { - skew = Point.CreateSkew(new Point(target.Width - 1, target.Height - 1), -this.AngleX, -this.AngleY); - } + Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix); using (PixelAccessor sourcePixels = source.Lock()) using (PixelAccessor targetPixels = target.Lock()) { Parallel.For( 0, - height, + target.Height, y => - { - for (int x = startX; x < endX; x++) { - Point skewed = Point.Skew(new Point(x, y), skew); - if (source.Bounds.Contains(skewed.X, skewed.Y)) + for (int x = 0; x < target.Width; x++) { - targetPixels[x, y] = sourcePixels[skewed.X, skewed.Y]; + Point transformedPoint = Point.Skew(new Point(x, y), matrix); + if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) + { + targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; + } } - } - this.OnRowProcessed(); - }); + + OnRowProcessed(); + }); } } } diff --git a/src/ImageProcessorCore/Samplers/Resamplers/CatmullRomResampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/CatmullRomResampler.cs index 03af3272f..0b5031df8 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/CatmullRomResampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/CatmullRomResampler.cs @@ -6,7 +6,9 @@ namespace ImageProcessorCore { /// - /// The function implements the Catmull-Rom algorithm. + /// The Catmull-Rom filter is a well known standard Cubic Filter often used as a interpolation function. + /// This filter produces a reasonably sharp edge, but without a the pronounced gradient change on large + /// scale image enlargements that a 'Lagrange' filter can produce. /// /// public class CatmullRomResampler : IResampler diff --git a/src/ImageProcessorCore/Samplers/Resamplers/HermiteResampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/HermiteResampler.cs index 6c1540a19..49193a3de 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/HermiteResampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/HermiteResampler.cs @@ -6,7 +6,8 @@ namespace ImageProcessorCore.Processors { /// - /// The function implements the hermite algorithm. + /// The Hermite filter is type of smoothed triangular interpolation Filter, + /// This filter rounds off strong edges while preserving flat 'color levels' in the original image. /// /// public class HermiteResampler : IResampler diff --git a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos3Resampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos3Resampler.cs index 9bc842f61..a78b6c066 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos3Resampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos3Resampler.cs @@ -8,6 +8,7 @@ namespace ImageProcessorCore /// /// The function implements the Lanczos kernel algorithm as described on /// Wikipedia + /// with a radius of 3 pixels. /// public class Lanczos3Resampler : IResampler { diff --git a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos5Resampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos5Resampler.cs index 67b704fc4..05af2dd7f 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos5Resampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos5Resampler.cs @@ -8,6 +8,7 @@ namespace ImageProcessorCore /// /// The function implements the Lanczos kernel algorithm as described on /// Wikipedia + /// with a radius of 5 pixels. /// public class Lanczos5Resampler : IResampler { diff --git a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos8Resampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos8Resampler.cs index 28a305c65..8c9a9237d 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos8Resampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos8Resampler.cs @@ -8,6 +8,7 @@ namespace ImageProcessorCore /// /// The function implements the Lanczos kernel algorithm as described on /// Wikipedia + /// with a radius of 8 pixels. /// public class Lanczos8Resampler : IResampler { diff --git a/src/ImageProcessorCore/Samplers/Rotate.cs b/src/ImageProcessorCore/Samplers/Rotate.cs index ea24f9650..fa30d9d34 100644 --- a/src/ImageProcessorCore/Samplers/Rotate.cs +++ b/src/ImageProcessorCore/Samplers/Rotate.cs @@ -1,7 +1,7 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// ------------------------------------------------------------------------------------------------------------------- +// namespace ImageProcessorCore { @@ -21,21 +21,20 @@ namespace ImageProcessorCore /// The public static Image Rotate(this Image source, float degrees, ProgressEventHandler progressHandler = null) { - return Rotate(source, degrees, Point.Empty, true, progressHandler); + return Rotate(source, degrees, true, progressHandler); } /// - /// Rotates an image by the given angle in degrees around the given center point. + /// Rotates an image by the given angle in degrees. /// /// The image to rotate. /// The angle in degrees to perform the rotation. - /// The center point at which to rotate the image. /// Whether to expand the image to fit the rotated result. /// A delegate which is called as progress is made processing the image. /// The - public static Image Rotate(this Image source, float degrees, Point center, bool expand, ProgressEventHandler progressHandler = null) + public static Image Rotate(this Image source, float degrees, bool expand, ProgressEventHandler progressHandler = null) { - RotateProcessor processor = new RotateProcessor { Angle = degrees, Center = center, Expand = expand }; + RotateProcessor processor = new RotateProcessor { Angle = degrees, Expand = expand }; processor.OnProgress += progressHandler; try diff --git a/src/ImageProcessorCore/Samplers/Skew.cs b/src/ImageProcessorCore/Samplers/Skew.cs index 7b0587a52..904f1d89d 100644 --- a/src/ImageProcessorCore/Samplers/Skew.cs +++ b/src/ImageProcessorCore/Samplers/Skew.cs @@ -1,7 +1,7 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// ------------------------------------------------------------------------------------------------------------------- +// namespace ImageProcessorCore { @@ -22,22 +22,21 @@ namespace ImageProcessorCore /// The public static Image Skew(this Image source, float degreesX, float degreesY, ProgressEventHandler progressHandler = null) { - return Skew(source, degreesX, degreesY, Point.Empty, true, progressHandler); + return Skew(source, degreesX, degreesY, true, progressHandler); } /// - /// Skews an image by the given angles in degrees around the given center point. + /// Skews an image by the given angles in degrees. /// /// The image to skew. /// The angle in degrees to perform the rotation along the x-axis. /// The angle in degrees to perform the rotation along the y-axis. - /// The center point at which to skew the image. /// Whether to expand the image to fit the skewed result. /// A delegate which is called as progress is made processing the image. /// The - public static Image Skew(this Image source, float degreesX, float degreesY, Point center, bool expand, ProgressEventHandler progressHandler = null) + public static Image Skew(this Image source, float degreesX, float degreesY, bool expand, ProgressEventHandler progressHandler = null) { - SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Center = center, Expand = expand }; + SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand }; processor.OnProgress += progressHandler; try diff --git a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs index 8c12e14f1..e81607b58 100644 --- a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs +++ b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs @@ -429,7 +429,7 @@ namespace ImageProcessorCore.Tests Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}")) { - image.Rotate(63, this.ProgressUpdate) + image.Rotate(-170, this.ProgressUpdate) .Save(output); } } @@ -454,7 +454,7 @@ namespace ImageProcessorCore.Tests Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/Skew/{filename}")) { - image.Skew(-15, 12, this.ProgressUpdate) + image.Skew(-20, -10, this.ProgressUpdate) .Save(output); } }