diff --git a/README.md b/README.md index 1432317c1..0ea2325cc 100644 --- a/README.md +++ b/README.md @@ -166,8 +166,8 @@ Here's an example of the code required to resize an image using the default Bicu ```csharp using (FileStream stream = File.OpenRead("foo.jpg")) using (FileStream output = File.OpenWrite("bar.jpg")) -using (Image image = new Image(stream)) { + Image image = new Image(stream); image.Resize(image.Width / 2, image.Height / 2) .Greyscale() .Save(output); @@ -179,8 +179,8 @@ It will also be possible to pass collections of processors as params to manipula ```csharp using (FileStream stream = File.OpenRead("foo.jpg")) using (FileStream output = File.OpenWrite("bar.jpg")) -using (Image image = new Image(stream)) { + Image image = new Image(stream); List processors = new List() { new GuassianBlur(5), diff --git a/src/ImageProcessorCore/Common/Helpers/ImageMaths.cs b/src/ImageProcessorCore/Common/Helpers/ImageMaths.cs index c85499081..706970835 100644 --- a/src/ImageProcessorCore/Common/Helpers/ImageMaths.cs +++ b/src/ImageProcessorCore/Common/Helpers/ImageMaths.cs @@ -171,35 +171,35 @@ namespace ImageProcessorCore Point topLeft = new Point(); Point bottomRight = new Point(); - Func delegateFunc; + Func delegateFunc; // Determine which channel to check against switch (channel) { case RgbaComponent.R: - delegateFunc = (imageBase, x, y, b) => Math.Abs(imageBase[x, y].R - b) > Epsilon; + delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].R - b) > Epsilon; break; case RgbaComponent.G: - delegateFunc = (imageBase, x, y, b) => Math.Abs(imageBase[x, y].G - b) > Epsilon; + delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].G - b) > Epsilon; break; case RgbaComponent.A: - delegateFunc = (imageBase, x, y, b) => Math.Abs(imageBase[x, y].A - b) > Epsilon; + delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].A - b) > Epsilon; break; default: - delegateFunc = (imageBase, x, y, b) => Math.Abs(imageBase[x, y].B - b) > Epsilon; + delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].B - b) > Epsilon; break; } - Func getMinY = imageBase => + Func getMinY = pixels => { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - if (delegateFunc(imageBase, x, y, componentValue)) + if (delegateFunc(pixels, x, y, componentValue)) { return y; } @@ -209,13 +209,13 @@ namespace ImageProcessorCore return 0; }; - Func getMaxY = imageBase => + Func getMaxY = pixels => { for (int y = height - 1; y > -1; y--) { for (int x = 0; x < width; x++) { - if (delegateFunc(imageBase, x, y, componentValue)) + if (delegateFunc(pixels, x, y, componentValue)) { return y; } @@ -225,13 +225,13 @@ namespace ImageProcessorCore return height; }; - Func getMinX = imageBase => + Func getMinX = pixels => { for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { - if (delegateFunc(imageBase, x, y, componentValue)) + if (delegateFunc(pixels, x, y, componentValue)) { return x; } @@ -241,13 +241,13 @@ namespace ImageProcessorCore return 0; }; - Func getMaxX = imageBase => + Func getMaxX = pixels => { for (int x = width - 1; x > -1; x--) { for (int y = 0; y < height; y++) { - if (delegateFunc(imageBase, x, y, componentValue)) + if (delegateFunc(pixels, x, y, componentValue)) { return x; } @@ -257,10 +257,13 @@ namespace ImageProcessorCore return height; }; - topLeft.Y = getMinY(bitmap); - topLeft.X = getMinX(bitmap); - bottomRight.Y = (getMaxY(bitmap) + 1).Clamp(0, height); - bottomRight.X = (getMaxX(bitmap) + 1).Clamp(0, width); + using (PixelAccessor bitmapPixels = bitmap.Lock()) + { + topLeft.Y = getMinY(bitmapPixels); + topLeft.X = getMinX(bitmapPixels); + bottomRight.Y = (getMaxY(bitmapPixels) + 1).Clamp(0, height); + bottomRight.X = (getMaxX(bitmapPixels) + 1).Clamp(0, width); + } return GetBoundingRectangle(topLeft, bottomRight); } diff --git a/src/ImageProcessorCore/Filters/Alpha.cs b/src/ImageProcessorCore/Filters/Alpha.cs index efdd63ca7..3532bb675 100644 --- a/src/ImageProcessorCore/Filters/Alpha.cs +++ b/src/ImageProcessorCore/Filters/Alpha.cs @@ -1,63 +1,52 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// +// ------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore { - using System; - using System.Numerics; - using System.Threading.Tasks; + using Processors; /// - /// An to change the Alpha of an . + /// Extension methods for the type. /// - public class Alpha : ParallelImageProcessor + public static partial class ImageExtensions { /// - /// Initializes a new instance of the class. + /// Alters the alpha component of the image. /// - /// The percentage to adjust the opacity of the image. Must be between 0 and 100. - /// - /// is less than 0 or is greater than 100. - /// - public Alpha(int percent) + /// The image this method extends. + /// The new opacity of the image. Must be between 0 and 100. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Alpha(this Image source, int percent, ProgressEventHandler progressHandler = null) { - Guard.MustBeBetweenOrEqualTo(percent, 0, 100, nameof(percent)); - this.Value = percent; + return Alpha(source, percent, source.Bounds, progressHandler); } /// - /// Gets the alpha value. + /// Alters the alpha component of the image. /// - public int Value { get; } - - /// - protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + /// The image this method extends. + /// The new opacity of the image. Must be between 0 and 100. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Alpha(this Image source, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null) { - float alpha = this.Value / 100f; - int sourceY = sourceRectangle.Y; - int sourceBottom = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - Vector4 alphaVector = new Vector4(1, 1, 1, alpha); + AlphaProcessor processor = new AlphaProcessor(percent); + processor.OnProgress += progressHandler; - Parallel.For( - startY, - endY, - y => - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) - { - Vector4 color = Color.ToNonPremultiplied(source[x, y]).ToVector4(); - color *= alphaVector; - target[x, y] = Color.FromNonPremultiplied(new Color(color)); - } - this.OnRowProcessed(); - } - }); + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } } } } diff --git a/src/ImageProcessorCore/Filters/BackgroundColor.cs b/src/ImageProcessorCore/Filters/BackgroundColor.cs index f0ad41d2e..98709cb32 100644 --- a/src/ImageProcessorCore/Filters/BackgroundColor.cs +++ b/src/ImageProcessorCore/Filters/BackgroundColor.cs @@ -1,73 +1,37 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// +// ------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore { - using System; - using System.Threading.Tasks; + using Processors; /// - /// Sets the background color of the image. + /// Extension methods for the type. /// - public class BackgroundColor : ParallelImageProcessor + public static partial class ImageExtensions { /// - /// The epsilon for comparing floating point numbers. + /// Combines the given image together with the current one by blending their pixels. /// - private const float Epsilon = 0.001f; - - /// - /// Initializes a new instance of the class. - /// - /// The to set the background color to. - public BackgroundColor(Color color) + /// The image this method extends. + /// The color to set as the background. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image BackgroundColor(this Image source, Color color, ProgressEventHandler progressHandler = null) { - this.Value = Color.FromNonPremultiplied(color); - } - - /// - /// Gets the background color value. - /// - public Color Value { get; } - - /// - 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; - Color backgroundColor = this.Value; - - Parallel.For( - startY, - endY, - y => - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) - { - Color color = source[x, y]; - float a = color.A; - - if (a < 1 && a > 0) - { - color = Color.Lerp(color, backgroundColor, .5f); - } - - if (Math.Abs(a) < Epsilon) - { - color = backgroundColor; - } - - target[x, y] = color; - } - this.OnRowProcessed(); - } - }); + BackgroundColorProcessor processor = new BackgroundColorProcessor(color); + processor.OnProgress += progressHandler; + + try + { + return source.Process(source.Bounds, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } } } } diff --git a/src/ImageProcessorCore/Filters/BlackWhite.cs b/src/ImageProcessorCore/Filters/BlackWhite.cs new file mode 100644 index 000000000..98c3b7744 --- /dev/null +++ b/src/ImageProcessorCore/Filters/BlackWhite.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Applies black and white toning to the image. + /// + /// The image this method extends. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image BlackWhite(this Image source, ProgressEventHandler progressHandler = null) + { + return BlackWhite(source, source.Bounds, progressHandler); + } + + /// + /// Applies black and white toning to the image. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image BlackWhite(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + BlackWhiteProcessor processor = new BlackWhiteProcessor(); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Blend.cs b/src/ImageProcessorCore/Filters/Blend.cs index 8c36c94f2..77a94e3fa 100644 --- a/src/ImageProcessorCore/Filters/Blend.cs +++ b/src/ImageProcessorCore/Filters/Blend.cs @@ -1,81 +1,60 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// +// ------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore { - using System.Threading.Tasks; + using Processors; /// - /// Combines two images together by blending the pixels. + /// Extension methods for the type. /// - public class Blend : ParallelImageProcessor + public static partial class ImageExtensions { /// - /// The image to blend. - /// - private readonly ImageBase toBlend; - - /// - /// Initializes a new instance of the class. + /// Combines the given image together with the current one by blending their pixels. /// + /// The image this method extends. /// /// The image to blend with the currently processing image. /// Disposal of this image is the responsibility of the developer. /// - /// The opacity of the image to blend. Between 0 and 100. - public Blend(ImageBase image, int alpha = 100) + /// The opacity of the image image to blend. Must be between 0 and 100. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Blend(this Image source, ImageBase image, int percent = 50, ProgressEventHandler progressHandler = null) { - Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha)); - this.toBlend = image; - this.Value = alpha; + return Blend(source, image, percent, source.Bounds, progressHandler); } /// - /// Gets the alpha percentage value. + /// Combines the given image together with the current one by blending their pixels. /// - public int Value { get; } - - /// - protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + /// The image this method extends. + /// + /// The image to blend with the currently processing image. + /// Disposal of this image is the responsibility of the developer. + /// + /// The opacity of the image image to blend. Must be between 0 and 100. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Blend(this Image source, ImageBase image, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null) { - int sourceY = sourceRectangle.Y; - int sourceBottom = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - Rectangle bounds = this.toBlend.Bounds; - float alpha = this.Value / 100f; - - Parallel.For( - startY, - endY, - y => - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) - { - Color color = source[x, y]; - - if (bounds.Contains(x, y)) - { - Color blendedColor = this.toBlend[x, y]; - - if (blendedColor.A > 0) - { - // Lerping colors is dependent on the alpha of the blended color - float alphaFactor = alpha > 0 ? alpha : blendedColor.A; - color = Color.Lerp(color, blendedColor, alphaFactor); - } - } - - target[x, y] = color; - } - - this.OnRowProcessed(); - } - }); + BlendProcessor processor = new BlendProcessor(image, percent); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } } } } diff --git a/src/ImageProcessorCore/Filters/BoxBlur.cs b/src/ImageProcessorCore/Filters/BoxBlur.cs new file mode 100644 index 000000000..474f0d6e7 --- /dev/null +++ b/src/ImageProcessorCore/Filters/BoxBlur.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Applies a box blur to the image. + /// + /// The image this method extends. + /// The 'radius' value representing the size of the area to sample. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image BoxBlur(this Image source, int radius = 7, ProgressEventHandler progressHandler = null) + { + return BoxBlur(source, radius, source.Bounds, progressHandler); + } + + /// + /// Applies a box blur to the image. + /// + /// The image this method extends. + /// The 'radius' value representing the size of the area to sample. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image BoxBlur(this Image source, int radius, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + BoxBlurProcessor processor = new BoxBlurProcessor(radius); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Brightness.cs b/src/ImageProcessorCore/Filters/Brightness.cs index 4c21d50b5..52944c801 100644 --- a/src/ImageProcessorCore/Filters/Brightness.cs +++ b/src/ImageProcessorCore/Filters/Brightness.cs @@ -1,65 +1,52 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// +// ------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore { - using System; - using System.Numerics; - using System.Threading.Tasks; + using Processors; /// - /// An to change the brightness of an . + /// Extension methods for the type. /// - public class Brightness : ParallelImageProcessor + public static partial class ImageExtensions { /// - /// Initializes a new instance of the class. + /// Alters the brightness component of the image. /// - /// The new brightness of the image. Must be between -100 and 100. - /// - /// is less than -100 or is greater than 100. - /// - public Brightness(int brightness) + /// The image this method extends. + /// The new brightness of the image. Must be between -100 and 100. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Brightness(this Image source, int amount, ProgressEventHandler progressHandler = null) { - Guard.MustBeBetweenOrEqualTo(brightness, -100, 100, nameof(brightness)); - this.Value = brightness; + return Brightness(source, amount, source.Bounds, progressHandler); } /// - /// Gets the brightness value. + /// Alters the brightness component of the image. /// - public int Value { get; } - - /// - protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + /// The image this method extends. + /// The new brightness of the image. Must be between -100 and 100. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Brightness(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null) { - float brightness = this.Value / 100f; - int sourceY = sourceRectangle.Y; - int sourceBottom = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - Parallel.For( - startY, - endY, - y => - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) - { - Color color = Color.Expand(source[x, y]); - - Vector3 vector3 = color.ToVector3(); - vector3 += new Vector3(brightness); + BrightnessProcessor processor = new BrightnessProcessor(amount); + processor.OnProgress += progressHandler; - target[x, y] = Color.Compress(new Color(vector3, color.A)); - } - this.OnRowProcessed(); - } - }); + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } } } } diff --git a/src/ImageProcessorCore/Filters/ColorBlindness.cs b/src/ImageProcessorCore/Filters/ColorBlindness.cs new file mode 100644 index 000000000..78267b05e --- /dev/null +++ b/src/ImageProcessorCore/Filters/ColorBlindness.cs @@ -0,0 +1,88 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Applies the given colorblindness simulator to the image. + /// + /// The image this method extends. + /// The type of color blindness simulator to apply. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, ProgressEventHandler progressHandler = null) + { + return ColorBlindness(source, colorBlindness, source.Bounds, progressHandler); + } + + /// + /// Applies the given colorblindness simulator to the image. + /// + /// The image this method extends. + /// The type of color blindness simulator to apply. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + IImageProcessor processor; + + switch (colorBlindness) + { + case ImageProcessorCore.ColorBlindness.Achromatomaly: + processor = new AchromatomalyProcessor(); + break; + + case ImageProcessorCore.ColorBlindness.Achromatopsia: + processor = new AchromatopsiaProcessor(); + break; + + case ImageProcessorCore.ColorBlindness.Deuteranomaly: + processor = new DeuteranomalyProcessor(); + break; + + case ImageProcessorCore.ColorBlindness.Deuteranopia: + processor = new DeuteranopiaProcessor(); + break; + + case ImageProcessorCore.ColorBlindness.Protanomaly: + processor = new ProtanomalyProcessor(); + break; + + case ImageProcessorCore.ColorBlindness.Protanopia: + processor = new ProtanopiaProcessor(); + break; + + case ImageProcessorCore.ColorBlindness.Tritanomaly: + processor = new TritanomalyProcessor(); + break; + + default: + processor = new TritanopiaProcessor(); + break; + } + + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Contrast.cs b/src/ImageProcessorCore/Filters/Contrast.cs index 80c422c83..cab12ca4e 100644 --- a/src/ImageProcessorCore/Filters/Contrast.cs +++ b/src/ImageProcessorCore/Filters/Contrast.cs @@ -1,65 +1,52 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// +// ------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore { - using System; - using System.Numerics; - using System.Threading.Tasks; + using Processors; /// - /// An to change the contrast of an . + /// Extension methods for the type. /// - public class Contrast : ParallelImageProcessor + public static partial class ImageExtensions { /// - /// Initializes a new instance of the class. + /// Alters the contrast component of the image. /// - /// The new contrast of the image. Must be between -100 and 100. - /// - /// is less than -100 or is greater than 100. - /// - public Contrast(int contrast) + /// The image this method extends. + /// The new contrast of the image. Must be between -100 and 100. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Contrast(this Image source, int amount, ProgressEventHandler progressHandler = null) { - Guard.MustBeBetweenOrEqualTo(contrast, -100, 100, nameof(contrast)); - this.Value = contrast; + return Contrast(source, amount, source.Bounds, progressHandler); } /// - /// Gets the contrast value. + /// Alters the contrast component of the image. /// - public int Value { get; } - - /// - protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + /// The image this method extends. + /// The new contrast of the image. Must be between -100 and 100. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Contrast(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null) { - float contrast = (100f + this.Value) / 100f; - int sourceY = sourceRectangle.Y; - int sourceBottom = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - Vector4 contrastVector = new Vector4(contrast, contrast, contrast, 1); - Vector4 shiftVector = new Vector4(.5f, .5f, .5f, 1); - Parallel.For( - startY, - endY, - y => - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) - { - Vector4 color = Color.Expand(source[x, y]).ToVector4(); - color -= shiftVector; - color *= contrastVector; - color += shiftVector; - target[x, y] = Color.Compress(new Color(color)); - } - this.OnRowProcessed(); - } - }); + ContrastProcessor processor = new ContrastProcessor(amount); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } } } } diff --git a/src/ImageProcessorCore/Filters/DetectEdges.cs b/src/ImageProcessorCore/Filters/DetectEdges.cs new file mode 100644 index 000000000..ac3a0282d --- /dev/null +++ b/src/ImageProcessorCore/Filters/DetectEdges.cs @@ -0,0 +1,63 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Detects any edges within the image. Uses the filter + /// operating in greyscale mode. + /// + /// The image this method extends. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image DetectEdges(this Image source, ProgressEventHandler progressHandler = null) + { + return DetectEdges(source, source.Bounds, new SobelProcessor { Greyscale = true }, progressHandler); + } + + /// + /// Detects any edges within the image. + /// + /// The image this method extends. + /// The filter for detecting edges. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image DetectEdges(this Image source, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null) + { + return DetectEdges(source, source.Bounds, filter, progressHandler); + } + + /// + /// Detects any edges within the image. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The filter for detecting edges. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null) + { + filter.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, filter); + } + finally + { + filter.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Greyscale.cs b/src/ImageProcessorCore/Filters/Greyscale.cs new file mode 100644 index 000000000..786a93576 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Greyscale.cs @@ -0,0 +1,55 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Applies greyscale toning to the image. + /// + /// The image this method extends. + /// The formula to apply to perform the operation. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Greyscale(this Image source, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null) + { + return Greyscale(source, source.Bounds, mode, progressHandler); + } + + /// + /// Applies greyscale toning to the image. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The formula to apply to perform the operation. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Greyscale(this Image source, Rectangle rectangle, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null) + { + IImageProcessor processor = mode == GreyscaleMode.Bt709 + ? (IImageProcessor)new GreyscaleBt709Processor() + : new GreyscaleBt601Processor(); + + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/GuassianBlur.cs b/src/ImageProcessorCore/Filters/GuassianBlur.cs new file mode 100644 index 000000000..646c6bdc0 --- /dev/null +++ b/src/ImageProcessorCore/Filters/GuassianBlur.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Applies a Guassian blur to the image. + /// + /// The image this method extends. + /// The 'sigma' value representing the weight of the blur. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image GuassianBlur(this Image source, float sigma = 3f, ProgressEventHandler progressHandler = null) + { + return GuassianBlur(source, sigma, source.Bounds, progressHandler); + } + + /// + /// Applies a Guassian blur to the image. + /// + /// The image this method extends. + /// The 'sigma' value representing the weight of the blur. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image GuassianBlur(this Image source, float sigma, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + GuassianBlurProcessor processor = new GuassianBlurProcessor(sigma); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/GuassianSharpen.cs b/src/ImageProcessorCore/Filters/GuassianSharpen.cs new file mode 100644 index 000000000..06993070d --- /dev/null +++ b/src/ImageProcessorCore/Filters/GuassianSharpen.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Applies a Guassian sharpening filter to the image. + /// + /// The image this method extends. + /// The 'sigma' value representing the weight of the blur. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image GuassianSharpen(this Image source, float sigma = 3f, ProgressEventHandler progressHandler = null) + { + return GuassianSharpen(source, sigma, source.Bounds, progressHandler); + } + + /// + /// Applies a Guassian sharpening filter to the image. + /// + /// The image this method extends. + /// The 'sigma' value representing the weight of the blur. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image GuassianSharpen(this Image source, float sigma, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + GuassianSharpenProcessor processor = new GuassianSharpenProcessor(sigma); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Hue.cs b/src/ImageProcessorCore/Filters/Hue.cs new file mode 100644 index 000000000..45a995c30 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Hue.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the hue component of the image. + /// + /// The image this method extends. + /// The angle in degrees to adjust the image. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Hue(this Image source, float degrees, ProgressEventHandler progressHandler = null) + { + return Hue(source, degrees, source.Bounds, progressHandler); + } + + /// + /// Alters the hue component of the image. + /// + /// The image this method extends. + /// The angle in degrees to adjust the image. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Hue(this Image source, float degrees, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + HueProcessor processor = new HueProcessor(degrees); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/ImageFilterExtensions.cs b/src/ImageProcessorCore/Filters/ImageFilterExtensions.cs deleted file mode 100644 index b0baffa2c..000000000 --- a/src/ImageProcessorCore/Filters/ImageFilterExtensions.cs +++ /dev/null @@ -1,784 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessorCore.Filters -{ - /// - /// Extensions methods for to apply filters to the image. - /// - public static class ImageFilterExtensions - { - /// - /// Alters the alpha component of the image. - /// - /// The image this method extends. - /// The new opacity of the image. Must be between 0 and 100. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Alpha(this Image source, int percent, ProgressEventHandler progressHandler = null) - { - return Alpha(source, percent, source.Bounds, progressHandler); - } - - /// - /// Alters the alpha component of the image. - /// - /// The image this method extends. - /// The new opacity of the image. Must be between 0 and 100. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Alpha(this Image source, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Alpha processor = new Alpha(percent); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Combines the given image together with the current one by blending their pixels. - /// - /// The image this method extends. - /// The color to set as the background. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image BackgroundColor(this Image source, Color color, ProgressEventHandler progressHandler = null) - { - BackgroundColor processor = new BackgroundColor(color); - processor.OnProgress += progressHandler; - - try - { - return source.Process(source.Bounds, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Combines the given image together with the current one by blending their pixels. - /// - /// The image this method extends. - /// - /// The image to blend with the currently processing image. - /// Disposal of this image is the responsibility of the developer. - /// - /// The opacity of the image image to blend. Must be between 0 and 100. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Blend(this Image source, ImageBase image, int percent = 50, ProgressEventHandler progressHandler = null) - { - return Blend(source, image, percent, source.Bounds, progressHandler); - } - - /// - /// Combines the given image together with the current one by blending their pixels. - /// - /// The image this method extends. - /// - /// The image to blend with the currently processing image. - /// Disposal of this image is the responsibility of the developer. - /// - /// The opacity of the image image to blend. Must be between 0 and 100. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Blend(this Image source, ImageBase image, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Blend processor = new Blend(image, percent); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Applies black and white toning to the image. - /// - /// The image this method extends. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image BlackWhite(this Image source, ProgressEventHandler progressHandler = null) - { - return BlackWhite(source, source.Bounds, progressHandler); - } - - /// - /// Applies black and white toning to the image. - /// - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image BlackWhite(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - BlackWhite processor = new BlackWhite(); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Applies a box blur to the image. - /// - /// The image this method extends. - /// The 'radius' value representing the size of the area to sample. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image BoxBlur(this Image source, int radius = 7, ProgressEventHandler progressHandler = null) - { - return BoxBlur(source, radius, source.Bounds, progressHandler); - } - - /// - /// Applies a box blur to the image. - /// - /// The image this method extends. - /// The 'radius' value representing the size of the area to sample. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image BoxBlur(this Image source, int radius, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - BoxBlur processor = new BoxBlur(radius); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Alters the brightness component of the image. - /// - /// The image this method extends. - /// The new brightness of the image. Must be between -100 and 100. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Brightness(this Image source, int amount, ProgressEventHandler progressHandler = null) - { - return Brightness(source, amount, source.Bounds, progressHandler); - } - - /// - /// Alters the brightness component of the image. - /// - /// The image this method extends. - /// The new brightness of the image. Must be between -100 and 100. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Brightness(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Brightness processor = new Brightness(amount); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Applies the given colorblindness simulator to the image. - /// - /// The image this method extends. - /// The type of color blindness simulator to apply. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, ProgressEventHandler progressHandler = null) - { - return ColorBlindness(source, colorBlindness, source.Bounds, progressHandler); - } - - /// - /// Applies the given colorblindness simulator to the image. - /// - /// The image this method extends. - /// The type of color blindness simulator to apply. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - IImageProcessor processor; - - switch (colorBlindness) - { - case Filters.ColorBlindness.Achromatomaly: - processor = new Achromatomaly(); - break; - - case Filters.ColorBlindness.Achromatopsia: - processor = new Achromatopsia(); - break; - - case Filters.ColorBlindness.Deuteranomaly: - processor = new Deuteranomaly(); - break; - - case Filters.ColorBlindness.Deuteranopia: - processor = new Deuteranopia(); - break; - - case Filters.ColorBlindness.Protanomaly: - processor = new Protanomaly(); - break; - - case Filters.ColorBlindness.Protanopia: - processor = new Protanopia(); - break; - - case Filters.ColorBlindness.Tritanomaly: - processor = new Tritanomaly(); - break; - - default: - processor = new Tritanopia(); - break; - } - - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Alters the contrast component of the image. - /// - /// The image this method extends. - /// The new contrast of the image. Must be between -100 and 100. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Contrast(this Image source, int amount, ProgressEventHandler progressHandler = null) - { - return Contrast(source, amount, source.Bounds, progressHandler); - } - - /// - /// 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 structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Contrast(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Contrast processor = new Contrast(amount); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Detects any edges within the image. Uses the filter - /// operating in greyscale mode. - /// - /// The image this method extends. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image DetectEdges(this Image source, ProgressEventHandler progressHandler = null) - { - return DetectEdges(source, source.Bounds, new Sobel { Greyscale = true }, progressHandler); - } - - /// - /// Detects any edges within the image. - /// - /// The image this method extends. - /// The filter for detecting edges. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image DetectEdges(this Image source, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null) - { - return DetectEdges(source, source.Bounds, filter, progressHandler); - } - - /// - /// Detects any edges within the image. - /// - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The filter for detecting edges. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null) - { - filter.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, filter); - } - finally - { - filter.OnProgress -= progressHandler; - } - } - - /// - /// Applies greyscale toning to the image. - /// - /// The image this method extends. - /// The formula to apply to perform the operation. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Greyscale(this Image source, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null) - { - return Greyscale(source, source.Bounds, mode, progressHandler); - } - - /// - /// Applies greyscale toning to the image. - /// - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The formula to apply to perform the operation. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Greyscale(this Image source, Rectangle rectangle, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null) - { - IImageProcessor processor = mode == GreyscaleMode.Bt709 - ? (IImageProcessor)new GreyscaleBt709() - : new GreyscaleBt601(); - - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Applies a Guassian blur to the image. - /// - /// The image this method extends. - /// The 'sigma' value representing the weight of the blur. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image GuassianBlur(this Image source, float sigma = 3f, ProgressEventHandler progressHandler = null) - { - return GuassianBlur(source, sigma, source.Bounds, progressHandler); - } - - /// - /// Applies a Guassian blur to the image. - /// - /// The image this method extends. - /// The 'sigma' value representing the weight of the blur. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image GuassianBlur(this Image source, float sigma, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - GuassianBlur processor = new GuassianBlur(sigma); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Applies a Guassian sharpening filter to the image. - /// - /// The image this method extends. - /// The 'sigma' value representing the weight of the blur. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image GuassianSharpen(this Image source, float sigma = 3f, ProgressEventHandler progressHandler = null) - { - return GuassianSharpen(source, sigma, source.Bounds, progressHandler); - } - - /// - /// Applies a Guassian sharpening filter to the image. - /// - /// The image this method extends. - /// The 'sigma' value representing the weight of the blur. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image GuassianSharpen(this Image source, float sigma, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - GuassianSharpen processor = new GuassianSharpen(sigma); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Alters the hue component of the image. - /// - /// The image this method extends. - /// The angle in degrees to adjust the image. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Hue(this Image source, float degrees, ProgressEventHandler progressHandler = null) - { - return Hue(source, degrees, source.Bounds, progressHandler); - } - - /// - /// Alters the hue component of the image. - /// - /// The image this method extends. - /// The angle in degrees to adjust the image. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Hue(this Image source, float degrees, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Hue processor = new Hue(degrees); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Inverts the colors of the image. - /// - /// The image this method extends. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Invert(this Image source, ProgressEventHandler progressHandler = null) - { - return Invert(source, source.Bounds, progressHandler); - } - - /// - /// Inverts the colors of the image. - /// - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Invert(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Invert processor = new Invert(); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Alters the colors of the image recreating an old Kodachrome camera effect. - /// - /// The image this method extends. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Kodachrome(this Image source, ProgressEventHandler progressHandler = null) - { - return Kodachrome(source, source.Bounds, progressHandler); - } - - /// - /// Alters the colors of the image recreating an old Kodachrome camera effect. - /// - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Kodachrome(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Kodachrome processor = new Kodachrome(); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Alters the colors of the image recreating an old Lomograph camera effect. - /// - /// The image this method extends. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Lomograph(this Image source, ProgressEventHandler progressHandler = null) - { - return Lomograph(source, source.Bounds, progressHandler); - } - - /// - /// Alters the colors of the image recreating an old Lomograph camera effect. - /// - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Lomograph(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Lomograph processor = new Lomograph(); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Alters the colors of the image recreating an old Polaroid camera effect. - /// - /// The image this method extends. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Polaroid(this Image source, ProgressEventHandler progressHandler = null) - { - return Polaroid(source, source.Bounds, progressHandler); - } - - /// - /// Alters the colors of the image recreating an old Polaroid camera effect. - /// - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Polaroid(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Polaroid processor = new Polaroid(); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Pixelates and image with the given pixel size. - /// - /// The image this method extends. - /// The size of the pixels. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Pixelate(this Image source, int size = 4, ProgressEventHandler progressHandler = null) - { - return Pixelate(source, size, source.Bounds, progressHandler); - } - - /// - /// Pixelates and image with the given pixel size. - /// - /// The image this method extends. - /// The size of the pixels. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Pixelate(this Image source, int size, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Pixelate processor = new Pixelate(size); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Alters the saturation component of the image. - /// - /// The image this method extends. - /// The new saturation of the image. Must be between -100 and 100. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Saturation(this Image source, int amount, ProgressEventHandler progressHandler = null) - { - return Saturation(source, amount, source.Bounds, progressHandler); - } - - /// - /// Alters the saturation component of the image. - /// - /// The image this method extends. - /// The new saturation of the image. Must be between -100 and 100. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Saturation(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Saturation processor = new Saturation(amount); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - - /// - /// Applies sepia toning to the image. - /// - /// The image this method extends. - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Sepia(this Image source, ProgressEventHandler progressHandler = null) - { - return Sepia(source, source.Bounds, progressHandler); - } - - /// - /// Applies sepia toning to the image. - /// - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// A delegate which is called as progress is made processing the image. - /// The . - public static Image Sepia(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) - { - Sepia processor = new Sepia(); - processor.OnProgress += progressHandler; - - try - { - return source.Process(rectangle, processor); - } - finally - { - processor.OnProgress -= progressHandler; - } - } - } -} diff --git a/src/ImageProcessorCore/Filters/Invert.cs b/src/ImageProcessorCore/Filters/Invert.cs index 249907abc..eaeddfc62 100644 --- a/src/ImageProcessorCore/Filters/Invert.cs +++ b/src/ImageProcessorCore/Filters/Invert.cs @@ -1,43 +1,50 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// +// ------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore { - using System.Numerics; - using System.Threading.Tasks; + using Processors; /// - /// An to invert the colors of an . + /// Extension methods for the type. /// - public class Invert : ParallelImageProcessor + public static partial class ImageExtensions { - /// - protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + /// + /// Inverts the colors of the image. + /// + /// The image this method extends. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Invert(this Image source, ProgressEventHandler progressHandler = null) { - int sourceY = sourceRectangle.Y; - int sourceBottom = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - Vector3 inverseVector = Vector3.One; + return Invert(source, source.Bounds, progressHandler); + } + + /// + /// Inverts the colors of the image. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Invert(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + InvertProcessor processor = new InvertProcessor(); + processor.OnProgress += progressHandler; - Parallel.For( - startY, - endY, - y => - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) - { - Color color = source[x, y]; - Vector3 vector = inverseVector - color.ToVector3(); - target[x, y] = new Color(vector, color.A); - } - this.OnRowProcessed(); - } - }); + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } } } } diff --git a/src/ImageProcessorCore/Filters/Kodachrome.cs b/src/ImageProcessorCore/Filters/Kodachrome.cs new file mode 100644 index 000000000..3b4e3f1c6 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Kodachrome.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the colors of the image recreating an old Kodachrome camera effect. + /// + /// The image this method extends. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Kodachrome(this Image source, ProgressEventHandler progressHandler = null) + { + return Kodachrome(source, source.Bounds, progressHandler); + } + + /// + /// Alters the colors of the image recreating an old Kodachrome camera effect. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Kodachrome(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + KodachromeProcessor processor = new KodachromeProcessor(); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Lomograph.cs b/src/ImageProcessorCore/Filters/Lomograph.cs new file mode 100644 index 000000000..5ecf0bcf4 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Lomograph.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the colors of the image recreating an old Lomograph camera effect. + /// + /// The image this method extends. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Lomograph(this Image source, ProgressEventHandler progressHandler = null) + { + return Lomograph(source, source.Bounds, progressHandler); + } + + /// + /// Alters the colors of the image recreating an old Lomograph camera effect. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Lomograph(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + LomographProcessor processor = new LomographProcessor(); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/ColorBlindness.cs b/src/ImageProcessorCore/Filters/Options/ColorBlindness.cs similarity index 96% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/ColorBlindness.cs rename to src/ImageProcessorCore/Filters/Options/ColorBlindness.cs index 7416a42d2..6d7fe849b 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/ColorBlindness.cs +++ b/src/ImageProcessorCore/Filters/Options/ColorBlindness.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore { /// /// Enumerates the various types of color blindness. diff --git a/src/ImageProcessorCore/Filters/Pixelate.cs b/src/ImageProcessorCore/Filters/Pixelate.cs index 00ef03349..6f86848d6 100644 --- a/src/ImageProcessorCore/Filters/Pixelate.cs +++ b/src/ImageProcessorCore/Filters/Pixelate.cs @@ -1,93 +1,52 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// +// ------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore { - using System; - using System.Collections.Generic; - using System.Threading.Tasks; + using Processors; /// - /// An to invert the colors of an . + /// Extension methods for the type. /// - public class Pixelate : ParallelImageProcessor + public static partial class ImageExtensions { /// - /// Initializes a new instance of the class. + /// Pixelates and image with the given pixel size. /// - /// The size of the pixels. Must be greater than 0. - /// - /// is less than 0 or equal to 0. - /// - public Pixelate(int size) + /// The image this method extends. + /// The size of the pixels. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Pixelate(this Image source, int size = 4, ProgressEventHandler progressHandler = null) { - Guard.MustBeGreaterThan(size, 0, nameof(size)); - this.Value = size; + return Pixelate(source, size, source.Bounds, progressHandler); } - /// - public override int Parallelism { get; set; } = 1; - /// - /// Gets or the pixel size. + /// Pixelates and image with the given pixel size. /// - public int Value { get; } - - /// - protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + /// The image this method extends. + /// The size of the pixels. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Pixelate(this Image source, int size, Rectangle rectangle, ProgressEventHandler progressHandler = null) { - int sourceY = sourceRectangle.Y; - int sourceBottom = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int size = this.Value; - int offset = this.Value / 2; - - // Get the range on the y-plane to choose from. - IEnumerable range = EnumerableExtensions.SteppedRange(startY, i => i < endY, size); - - Parallel.ForEach( - range, - y => - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x += size) - { - - int offsetX = offset; - int offsetY = offset; - - // Make sure that the offset is within the boundary of the - // image. - while (y + offsetY >= sourceBottom) - { - offsetY--; - } - - while (x + offsetX >= endX) - { - offsetX--; - } - - // Get the pixel color in the centre of the soon to be pixelated area. - // ReSharper disable AccessToDisposedClosure - Color pixel = source[x + offsetX, y + offsetY]; - - // For each pixel in the pixelate size, set it to the centre color. - for (int l = y; l < y + size && l < sourceBottom; l++) - { - for (int k = x; k < x + size && k < endX; k++) - { - target[k, l] = pixel; - } - } - } - this.OnRowProcessed(); - } - }); + PixelateProcessor processor = new PixelateProcessor(size); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } } } } diff --git a/src/ImageProcessorCore/Filters/Polaroid.cs b/src/ImageProcessorCore/Filters/Polaroid.cs new file mode 100644 index 000000000..165f1d59a --- /dev/null +++ b/src/ImageProcessorCore/Filters/Polaroid.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the colors of the image recreating an old Polaroid camera effect. + /// + /// The image this method extends. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Polaroid(this Image source, ProgressEventHandler progressHandler = null) + { + return Polaroid(source, source.Bounds, progressHandler); + } + + /// + /// Alters the colors of the image recreating an old Polaroid camera effect. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Polaroid(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + PolaroidProcessor processor = new PolaroidProcessor(); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs b/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs new file mode 100644 index 000000000..72ba79f9b --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs @@ -0,0 +1,69 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System; + using System.Numerics; + using System.Threading.Tasks; + + /// + /// An to change the Alpha of an . + /// + public class AlphaProcessor : ParallelImageProcessor + { + /// + /// Initializes a new instance of the class. + /// + /// The percentage to adjust the opacity of the image. Must be between 0 and 100. + /// + /// is less than 0 or is greater than 100. + /// + public AlphaProcessor(int percent) + { + Guard.MustBeBetweenOrEqualTo(percent, 0, 100, nameof(percent)); + this.Value = percent; + } + + /// + /// Gets the alpha value. + /// + public int Value { get; } + + /// + protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + { + float alpha = this.Value / 100f; + int sourceY = sourceRectangle.Y; + int sourceBottom = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + Vector4 alphaVector = new Vector4(1, 1, 1, alpha); + + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => + { + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x++) + { + Vector4 color = Color.ToNonPremultiplied(sourcePixels[x, y]).ToVector4(); + color *= alphaVector; + targetPixels[x, y] = Color.FromNonPremultiplied(new Color(color)); + } + + this.OnRowProcessed(); + } + }); + + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs new file mode 100644 index 000000000..ef451a89d --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs @@ -0,0 +1,78 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System; + using System.Threading.Tasks; + + /// + /// Sets the background color of the image. + /// + public class BackgroundColorProcessor : ParallelImageProcessor + { + /// + /// The epsilon for comparing floating point numbers. + /// + private const float Epsilon = 0.001f; + + /// + /// Initializes a new instance of the class. + /// + /// The to set the background color to. + public BackgroundColorProcessor(Color color) + { + this.Value = Color.FromNonPremultiplied(color); + } + + /// + /// Gets the background color value. + /// + public Color Value { get; } + + /// + 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; + Color backgroundColor = this.Value; + + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => + { + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x++) + { + Color color = sourcePixels[x, y]; + float a = color.A; + + if (a < 1 && a > 0) + { + color = Color.Lerp(color, backgroundColor, .5f); + } + + if (Math.Abs(a) < Epsilon) + { + color = backgroundColor; + } + + targetPixels[x, y] = color; + } + + this.OnRowProcessed(); + } + }); + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Binarization/Threshold.cs b/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs similarity index 79% rename from src/ImageProcessorCore/Filters/Binarization/Threshold.cs rename to src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs index 6c71834d5..dc3816a03 100644 --- a/src/ImageProcessorCore/Filters/Binarization/Threshold.cs +++ b/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs @@ -1,9 +1,9 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System; using System.Threading.Tasks; @@ -13,16 +13,16 @@ namespace ImageProcessorCore.Filters /// . The image will be converted to greyscale before thresholding /// occurs. /// - public class Threshold : ParallelImageProcessor + public class ThresholdProcessor : ParallelImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The threshold to split the image. Must be between 0 and 1. /// /// is less than 0 or is greater than 1. /// - public Threshold(float threshold) + public ThresholdProcessor(float threshold) { Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); this.Value = threshold; @@ -46,7 +46,7 @@ namespace ImageProcessorCore.Filters /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { - new GreyscaleBt709().Apply(source, source, sourceRectangle); + new GreyscaleBt709Processor().Apply(source, source, sourceRectangle); } /// @@ -60,7 +60,10 @@ namespace ImageProcessorCore.Filters int startX = sourceRectangle.X; int endX = sourceRectangle.Right; - Parallel.For( + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( startY, endY, y => @@ -69,14 +72,15 @@ namespace ImageProcessorCore.Filters { for (int x = startX; x < endX; x++) { - Color color = source[x, y]; + Color color = sourcePixels[x, y]; // Any channel will do since it's greyscale. - target[x, y] = color.B >= threshold ? upper : lower; + targetPixels[x, y] = color.B >= threshold ? upper : lower; } this.OnRowProcessed(); } }); + } } } } diff --git a/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs new file mode 100644 index 000000000..cbf966a7c --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs @@ -0,0 +1,86 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System.Threading.Tasks; + + /// + /// Combines two images together by blending the pixels. + /// + public class BlendProcessor : ParallelImageProcessor + { + /// + /// The image to blend. + /// + private readonly ImageBase blend; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The image to blend with the currently processing image. + /// Disposal of this image is the responsibility of the developer. + /// + /// The opacity of the image to blend. Between 0 and 100. + public BlendProcessor(ImageBase image, int alpha = 100) + { + Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha)); + this.blend = image; + this.Value = alpha; + } + + /// + /// Gets the alpha percentage value. + /// + public int Value { get; } + + /// + 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; + Rectangle bounds = this.blend.Bounds; + float alpha = this.Value / 100f; + + using (PixelAccessor toBlendPixels = this.blend.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => + { + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x++) + { + Color color = sourcePixels[x, y]; + + if (bounds.Contains(x, y)) + { + Color blendedColor = toBlendPixels[x, y]; + + if (blendedColor.A > 0) + { + // Lerping colors is dependent on the alpha of the blended color + float alphaFactor = alpha > 0 ? alpha : blendedColor.A; + color = Color.Lerp(color, blendedColor, alphaFactor); + } + } + + targetPixels[x, y] = color; + } + + this.OnRowProcessed(); + } + }); + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs new file mode 100644 index 000000000..a100e5717 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs @@ -0,0 +1,69 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System; + using System.Numerics; + using System.Threading.Tasks; + + /// + /// An to change the brightness of an . + /// + public class BrightnessProcessor : ParallelImageProcessor + { + /// + /// Initializes a new instance of the class. + /// + /// The new brightness of the image. Must be between -100 and 100. + /// + /// is less than -100 or is greater than 100. + /// + public BrightnessProcessor(int brightness) + { + Guard.MustBeBetweenOrEqualTo(brightness, -100, 100, nameof(brightness)); + this.Value = brightness; + } + + /// + /// Gets the brightness value. + /// + public int Value { get; } + + /// + protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + { + float brightness = this.Value / 100f; + int sourceY = sourceRectangle.Y; + int sourceBottom = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => + { + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x++) + { + Color color = Color.Expand(sourcePixels[x, y]); + + Vector3 vector3 = color.ToVector3(); + vector3 += new Vector3(brightness); + + targetPixels[x, y] = Color.Compress(new Color(vector3, color.A)); + } + this.OnRowProcessed(); + } + }); + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/BlackWhite.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs similarity index 79% rename from src/ImageProcessorCore/Filters/ColorMatrix/BlackWhite.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs index f0d2a366a..7fc3240d0 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/BlackWhite.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image to their black and white equivalent. /// - public class BlackWhite : ColorMatrixFilter + public class BlackWhiteProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Achromatomaly.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs similarity index 79% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Achromatomaly.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs index b649fcba5..d5740ec28 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Achromatomaly.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. /// - public class Achromatomaly : ColorMatrixFilter + public class AchromatomalyProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Achromatopsia.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs similarity index 79% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Achromatopsia.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs index 794e71ca3..6f2f7c269 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Achromatopsia.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. /// - public class Achromatopsia : ColorMatrixFilter + public class AchromatopsiaProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Deuteranomaly.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs similarity index 77% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Deuteranomaly.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs index a8f7d5f2c..fed09991f 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Deuteranomaly.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// - public class Deuteranomaly : ColorMatrixFilter + public class DeuteranomalyProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Deuteranopia.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs similarity index 77% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Deuteranopia.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs index 630c2fa3c..0ef190861 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Deuteranopia.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// - public class Deuteranopia : ColorMatrixFilter + public class DeuteranopiaProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Protanomaly.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs similarity index 78% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Protanomaly.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs index 73d3bdcaa..b7152a68e 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Protanomaly.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness. /// - public class Protanomaly : ColorMatrixFilter + public class ProtanomalyProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Protanopia.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs similarity index 78% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Protanopia.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs index a6cfef24e..7984be139 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Protanopia.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// - public class Protanopia : ColorMatrixFilter + public class ProtanopiaProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/README.md b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/README.md similarity index 100% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/README.md rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/README.md diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Tritanomaly.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs similarity index 78% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Tritanomaly.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs index 851b17915..618da36bb 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Tritanomaly.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// - public class Tritanomaly : ColorMatrixFilter + public class TritanomalyProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Tritanopia.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs similarity index 78% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Tritanopia.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs index 338f0877c..e53de7a69 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Tritanopia.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// - public class Tritanopia : ColorMatrixFilter + public class TritanopiaProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/ColorMatrixFilter.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs similarity index 86% rename from src/ImageProcessorCore/Filters/ColorMatrix/ColorMatrixFilter.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs index 7de6fcac4..eb01c3c22 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/ColorMatrixFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; using System.Threading.Tasks; @@ -28,7 +28,10 @@ namespace ImageProcessorCore.Filters int endX = sourceRectangle.Right; Matrix4x4 matrix = this.Matrix; - Parallel.For( + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( startY, endY, y => @@ -37,11 +40,13 @@ namespace ImageProcessorCore.Filters { for (int x = startX; x < endX; x++) { - target[x, y] = this.ApplyMatrix(source[x, y], matrix); + targetPixels[x, y] = this.ApplyMatrix(sourcePixels[x, y], matrix); } + this.OnRowProcessed(); } }); + } } /// diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleBt601.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt601Processor.cs similarity index 80% rename from src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleBt601.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt601Processor.cs index 49ee2642e..b9a6daa6e 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleBt601.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt601Processor.cs @@ -1,9 +1,9 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Filters /// Converts the colors of the image to greyscale applying the formula as specified by /// ITU-R Recommendation BT.601 . /// - public class GreyscaleBt601 : ColorMatrixFilter + public class GreyscaleBt601Processor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleBt709.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt709Processor.cs similarity index 80% rename from src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleBt709.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt709Processor.cs index da0533d74..949e51e6a 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleBt709.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt709Processor.cs @@ -1,9 +1,9 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Filters /// Converts the colors of the image to greyscale applying the formula as specified by /// ITU-R Recommendation BT.709 . /// - public class GreyscaleBt709 : ColorMatrixFilter + public class GreyscaleBt709Processor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleMode.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleMode.cs similarity index 93% rename from src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleMode.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleMode.cs index 27ddad951..269c1179e 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleMode.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleMode.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// Provides enumeration over the various greyscale methods available. diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/Hue.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs similarity index 86% rename from src/ImageProcessorCore/Filters/ColorMatrix/Hue.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs index 8d79a204a..da7d4631c 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/Hue.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs @@ -1,9 +1,14 @@ -namespace ImageProcessorCore.Filters +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors { using System; using System.Numerics; - public class Hue : ColorMatrixFilter + public class HueProcessor : ColorMatrixFilter { /// /// The used to alter the image. @@ -11,10 +16,10 @@ private Matrix4x4 matrix; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The new brightness of the image. Must be between -100 and 100. - public Hue(float angle) + public HueProcessor(float angle) { // Wrap the angle round at 360. angle = angle % 360; diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/IColorMatrixFilter.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs similarity index 80% rename from src/ImageProcessorCore/Filters/ColorMatrix/IColorMatrixFilter.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs index 0845b521b..5a0c76684 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/IColorMatrixFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; @@ -19,7 +19,8 @@ namespace ImageProcessorCore.Filters Matrix4x4 Matrix { get; } /// - /// Gets a value indicating whether to compand the value on processing. + /// Gets a value indicating whether to compress + /// or expand individual pixel colors the value on processing. /// bool Compand { get; } } diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/Kodachrome.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/KodachromeProcessor.cs similarity index 76% rename from src/ImageProcessorCore/Filters/ColorMatrix/Kodachrome.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/KodachromeProcessor.cs index 03f248fd9..91c436460 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/Kodachrome.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/KodachromeProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating an old Kodachrome camera effect. /// - public class Kodachrome : ColorMatrixFilter + public class KodachromeProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/Lomograph.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs similarity index 71% rename from src/ImageProcessorCore/Filters/ColorMatrix/Lomograph.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs index 9567a8056..4b507fcd1 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/Lomograph.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating an old Lomograph effect. /// - public class Lomograph : ColorMatrixFilter + public class LomographProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() @@ -26,7 +26,7 @@ namespace ImageProcessorCore.Filters /// protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) { - new Vignette { Color = new Color(0, 10 / 255f, 0) }.Apply(target, target, targetRectangle); + new VignetteProcessor { Color = new Color(0, 10 / 255f, 0) }.Apply(target, target, targetRectangle); } } } diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/Polaroid.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs similarity index 77% rename from src/ImageProcessorCore/Filters/ColorMatrix/Polaroid.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs index dfd877ca2..dce2609e5 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/Polaroid.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; /// /// Converts the colors of the image recreating an old Polaroid effect. /// - public class Polaroid : ColorMatrixFilter + public class PolaroidProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() @@ -32,8 +32,8 @@ namespace ImageProcessorCore.Filters /// protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) { - new Vignette { Color = new Color(102 / 255f, 34 / 255f, 0) }.Apply(target, target, targetRectangle); - new Glow + new VignetteProcessor { Color = new Color(102 / 255f, 34 / 255f, 0) }.Apply(target, target, targetRectangle); + new GlowProcessor { Color = new Color(1, 153 / 255f, 102 / 255f, .7f), RadiusX = target.Width / 4f, diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/Saturation.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs similarity index 91% rename from src/ImageProcessorCore/Filters/ColorMatrix/Saturation.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs index 113afcb0c..abc00bfb6 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/Saturation.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs @@ -1,9 +1,9 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System; using System.Numerics; @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Filters /// /// An to change the saturation of an . /// - public class Saturation : ColorMatrixFilter + public class SaturationProcessor : ColorMatrixFilter { /// /// The saturation to be applied to the image. @@ -24,13 +24,13 @@ namespace ImageProcessorCore.Filters private Matrix4x4 matrix; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The new saturation of the image. Must be between -100 and 100. /// /// is less than -100 or is greater than 100. /// - public Saturation(int saturation) + public SaturationProcessor(int saturation) { Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation)); this.saturation = saturation; diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/Sepia.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs similarity index 82% rename from src/ImageProcessorCore/Filters/ColorMatrix/Sepia.cs rename to src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs index 2898e7b0c..da6801311 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/Sepia.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs @@ -1,9 +1,9 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Numerics; @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Filters /// Converts the colors of the image to their sepia equivalent. /// The formula used matches the svg specification. /// - public class Sepia : ColorMatrixFilter + public class SepiaProcessor : ColorMatrixFilter { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs new file mode 100644 index 000000000..920a6fb01 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs @@ -0,0 +1,70 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System; + using System.Numerics; + using System.Threading.Tasks; + + /// + /// An to change the contrast of an . + /// + public class ContrastProcessor : ParallelImageProcessor + { + /// + /// Initializes a new instance of the class. + /// + /// The new contrast of the image. Must be between -100 and 100. + /// + /// is less than -100 or is greater than 100. + /// + public ContrastProcessor(int contrast) + { + Guard.MustBeBetweenOrEqualTo(contrast, -100, 100, nameof(contrast)); + this.Value = contrast; + } + + /// + /// Gets the contrast value. + /// + public int Value { get; } + + /// + protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + { + float contrast = (100f + this.Value) / 100f; + int sourceY = sourceRectangle.Y; + int sourceBottom = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + Vector4 contrastVector = new Vector4(contrast, contrast, contrast, 1); + Vector4 shiftVector = new Vector4(.5f, .5f, .5f, 1); + + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => + { + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x++) + { + Vector4 color = Color.Expand(sourcePixels[x, y]).ToVector4(); + color -= shiftVector; + color *= contrastVector; + color += shiftVector; + targetPixels[x, y] = Color.Compress(new Color(color)); + } + this.OnRowProcessed(); + } + }); + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Convolution/BoxBlur.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs similarity index 92% rename from src/ImageProcessorCore/Filters/Convolution/BoxBlur.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs index 5da2d2be6..4ad60d65b 100644 --- a/src/ImageProcessorCore/Filters/Convolution/BoxBlur.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs @@ -1,14 +1,14 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// Applies a Box blur filter to the image. /// - public class BoxBlur : Convolution2PassFilter + public class BoxBlurProcessor : Convolution2PassFilter { /// /// The maximum size of the kernal in either direction. @@ -26,12 +26,12 @@ namespace ImageProcessorCore.Filters private float[,] kernelX; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. /// - public BoxBlur(int radius = 7) + public BoxBlurProcessor(int radius = 7) { this.kernelSize = (radius * 2) + 1; } diff --git a/src/ImageProcessorCore/Filters/Convolution/Convolution2DFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs similarity index 85% rename from src/ImageProcessorCore/Filters/Convolution/Convolution2DFilter.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs index 49537ac75..0dad68485 100644 --- a/src/ImageProcessorCore/Filters/Convolution/Convolution2DFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System; using System.Threading.Tasks; @@ -42,7 +42,10 @@ namespace ImageProcessorCore.Filters int maxY = sourceBottom - 1; int maxX = endX - 1; - Parallel.For( + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( startY, endY, y => @@ -58,8 +61,8 @@ namespace ImageProcessorCore.Filters float gY = 0; float bY = 0; - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelYHeight; fy++) + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelYHeight; fy++) { int fyr = fy - radiusY; int offsetY = y + fyr; @@ -73,7 +76,7 @@ namespace ImageProcessorCore.Filters offsetX = offsetX.Clamp(0, maxX); - Color currentColor = source[offsetX, offsetY]; + Color currentColor = sourcePixels[offsetX, offsetY]; float r = currentColor.R; float g = currentColor.G; float b = currentColor.B; @@ -98,12 +101,13 @@ namespace ImageProcessorCore.Filters float green = (float)Math.Sqrt((gX * gX) + (gY * gY)); float blue = (float)Math.Sqrt((bX * bX) + (bY * bY)); - Color targetColor = target[x, y]; - target[x, y] = new Color(red, green, blue, targetColor.A); + Color targetColor = targetPixels[x, y]; + targetPixels[x, y] = new Color(red, green, blue, targetColor.A); } this.OnRowProcessed(); } }); + } } } } diff --git a/src/ImageProcessorCore/Filters/Convolution/Convolution2PassFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs similarity index 91% rename from src/ImageProcessorCore/Filters/Convolution/Convolution2PassFilter.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs index 8fbdb5e2b..66e8cac98 100644 --- a/src/ImageProcessorCore/Filters/Convolution/Convolution2PassFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Threading.Tasks; @@ -71,7 +71,10 @@ namespace ImageProcessorCore.Filters int maxY = sourceBottom - 1; int maxX = endX - 1; - Parallel.For( + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( startY, endY, y => @@ -97,16 +100,17 @@ namespace ImageProcessorCore.Filters offsetX = offsetX.Clamp(0, maxX); - Color currentColor = source[offsetX, offsetY]; + Color currentColor = sourcePixels[offsetX, offsetY]; destination += kernel[fy, fx] * currentColor; } } - target[x, y] = destination; + targetPixels[x, y] = destination; } this.OnRowProcessed(); } }); + } } } } \ No newline at end of file diff --git a/src/ImageProcessorCore/Filters/Convolution/ConvolutionFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs similarity index 88% rename from src/ImageProcessorCore/Filters/Convolution/ConvolutionFilter.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs index 0c34ab902..84884ff83 100644 --- a/src/ImageProcessorCore/Filters/Convolution/ConvolutionFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System.Threading.Tasks; @@ -31,7 +31,10 @@ namespace ImageProcessorCore.Filters int maxY = sourceBottom - 1; int maxX = endX - 1; - Parallel.For( + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( startY, endY, y => @@ -59,7 +62,7 @@ namespace ImageProcessorCore.Filters offsetX = offsetX.Clamp(0, maxX); - Color currentColor = source[offsetX, offsetY]; + Color currentColor = sourcePixels[offsetX, offsetY]; float r = currentColor.R; float g = currentColor.G; float b = currentColor.B; @@ -74,11 +77,12 @@ namespace ImageProcessorCore.Filters float green = gX; float blue = bX; - target[x, y] = new Color(red, green, blue); + targetPixels[x, y] = new Color(red, green, blue); } this.OnRowProcessed(); } }); + } } } } diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/EdgeDetector2DFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs similarity index 85% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/EdgeDetector2DFilter.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs index c37e6f23c..ce4aa0b2a 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/EdgeDetector2DFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// Defines a filter that detects edges within an image using two @@ -19,7 +19,7 @@ namespace ImageProcessorCore.Filters { if (this.Greyscale) { - new GreyscaleBt709().Apply(source, source, sourceRectangle); + new GreyscaleBt709Processor().Apply(source, source, sourceRectangle); } } } diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/EdgeDetectorFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs similarity index 85% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/EdgeDetectorFilter.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs index 3795bb78c..7a18bb96b 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/EdgeDetectorFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// Defines a filter that detects edges within an image using a single @@ -19,7 +19,7 @@ namespace ImageProcessorCore.Filters { if (this.Greyscale) { - new GreyscaleBt709().Apply(source, source, sourceRectangle); + new GreyscaleBt709Processor().Apply(source, source, sourceRectangle); } } } diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/IEdgeDetectorFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs similarity index 93% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/IEdgeDetectorFilter.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs index 0016dad66..b90f0d2ea 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/IEdgeDetectorFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// Provides properties and methods allowing the detection of edges within an image. diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Kayyali.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs similarity index 78% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Kayyali.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs index 2a8a193cd..5af818241 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Kayyali.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs @@ -1,15 +1,15 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// The Kayyali operator filter. /// /// - public class Kayyali : EdgeDetector2DFilter + public class KayyaliProcessor : EdgeDetector2DFilter { /// public override float[,] KernelX => new float[,] diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Kirsch.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs similarity index 79% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Kirsch.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs index 88904494e..24a561572 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Kirsch.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs @@ -1,15 +1,15 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// The Kirsch operator filter. /// /// - public class Kirsch : EdgeDetector2DFilter + public class KirschProcessor : EdgeDetector2DFilter { /// public override float[,] KernelX => new float[,] diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Laplacian3X3.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs similarity index 72% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Laplacian3X3.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs index d4d1824aa..a66d69253 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Laplacian3X3.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs @@ -1,15 +1,15 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// The Laplacian 3 x 3 operator filter. /// /// - public class Laplacian3X3 : EdgeDetectorFilter + public class Laplacian3X3Processor : EdgeDetectorFilter { /// public override float[,] KernelXY => new float[,] diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Laplacian5X5.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs similarity index 76% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Laplacian5X5.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs index 1456206b0..f2d3d885e 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Laplacian5X5.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs @@ -1,15 +1,15 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// The Laplacian 5 x 5 operator filter. /// /// - public class Laplacian5X5 : EdgeDetectorFilter + public class Laplacian5X5Processor : EdgeDetectorFilter { /// public override float[,] KernelXY => new float[,] diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/LaplacianOfGaussian.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs similarity index 75% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/LaplacianOfGaussian.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs index 3a7e4c2e3..0aae3020a 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/LaplacianOfGaussian.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs @@ -1,15 +1,15 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// The Laplacian of Gaussian operator filter. /// /// - public class LaplacianOfGaussian : EdgeDetectorFilter + public class LaplacianOfGaussianProcessor : EdgeDetectorFilter { /// public override float[,] KernelXY => new float[,] diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Prewitt.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs similarity index 78% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Prewitt.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs index 0f0a99cc5..3a5bbe986 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Prewitt.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs @@ -1,15 +1,15 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// The Prewitt operator filter. /// /// - public class Prewitt : EdgeDetector2DFilter + public class PrewittProcessor : EdgeDetector2DFilter { /// public override float[,] KernelX => new float[,] diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/RobertsCross.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs similarity index 76% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/RobertsCross.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs index 6b8069091..0a6166418 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/RobertsCross.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs @@ -1,15 +1,15 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// The Roberts Cross operator filter. /// /// - public class RobertsCross : EdgeDetector2DFilter + public class RobertsCrossProcessor : EdgeDetector2DFilter { /// public override float[,] KernelX => new float[,] diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Scharr.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs similarity index 79% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Scharr.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs index bca9587fc..80308a140 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Scharr.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs @@ -1,15 +1,15 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// The Scharr operator filter. /// /// - public class Scharr : EdgeDetector2DFilter + public class ScharrProcessor : EdgeDetector2DFilter { /// public override float[,] KernelX => new float[,] diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Sobel.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs similarity index 79% rename from src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Sobel.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs index 4cf0b7149..39f7d350d 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Sobel.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs @@ -1,15 +1,15 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { /// /// The Sobel operator filter. /// /// - public class Sobel : EdgeDetector2DFilter + public class SobelProcessor : EdgeDetector2DFilter { /// public override float[,] KernelX => new float[,] diff --git a/src/ImageProcessorCore/Filters/Convolution/GuassianBlur.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs similarity index 90% rename from src/ImageProcessorCore/Filters/Convolution/GuassianBlur.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs index ed4cb6d4f..904de5994 100644 --- a/src/ImageProcessorCore/Filters/Convolution/GuassianBlur.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System; /// /// Applies a Gaussian blur filter to the image. /// - public class GuassianBlur : Convolution2PassFilter + public class GuassianBlurProcessor : Convolution2PassFilter { /// /// The maximum size of the kernal in either direction. @@ -33,29 +33,29 @@ namespace ImageProcessorCore.Filters private float[,] kernelX; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The 'sigma' value representing the weight of the blur. - public GuassianBlur(float sigma = 3f) + public GuassianBlurProcessor(float sigma = 3f) { this.kernelSize = ((int)Math.Ceiling(sigma) * 2) + 1; this.sigma = sigma; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. /// - public GuassianBlur(int radius) + public GuassianBlurProcessor(int radius) { this.kernelSize = (radius * 2) + 1; this.sigma = radius; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the blur. @@ -64,7 +64,7 @@ namespace ImageProcessorCore.Filters /// The 'radius' value representing the size of the area to sample. /// This should be at least twice the sigma value. /// - public GuassianBlur(float sigma, int radius) + public GuassianBlurProcessor(float sigma, int radius) { this.kernelSize = (radius * 2) + 1; this.sigma = sigma; diff --git a/src/ImageProcessorCore/Filters/Convolution/GuassianSharpen.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs similarity index 92% rename from src/ImageProcessorCore/Filters/Convolution/GuassianSharpen.cs rename to src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs index 7e64383cf..8d95eaf82 100644 --- a/src/ImageProcessorCore/Filters/Convolution/GuassianSharpen.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs @@ -1,16 +1,16 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System; /// /// Applies a Gaussian sharpening filter to the image. /// - public class GuassianSharpen : Convolution2PassFilter + public class GuassianSharpenProcessor : Convolution2PassFilter { /// /// The maximum size of the kernal in either direction. @@ -33,31 +33,31 @@ namespace ImageProcessorCore.Filters private float[,] kernelX; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the sharpening. /// - public GuassianSharpen(float sigma = 3f) + public GuassianSharpenProcessor(float sigma = 3f) { this.kernelSize = ((int)Math.Ceiling(sigma) * 2) + 1; this.sigma = sigma; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. /// - public GuassianSharpen(int radius) + public GuassianSharpenProcessor(int radius) { this.kernelSize = (radius * 2) + 1; this.sigma = radius; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the sharpen. @@ -66,7 +66,7 @@ namespace ImageProcessorCore.Filters /// The 'radius' value representing the size of the area to sample. /// This should be at least twice the sigma value. /// - public GuassianSharpen(float sigma, int radius) + public GuassianSharpenProcessor(float sigma, int radius) { this.kernelSize = (radius * 2) + 1; this.sigma = sigma; diff --git a/src/ImageProcessorCore/Filters/Glow.cs b/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs similarity index 57% rename from src/ImageProcessorCore/Filters/Glow.cs rename to src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs index 2ad7a383a..63c18e9eb 100644 --- a/src/ImageProcessorCore/Filters/Glow.cs +++ b/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs @@ -1,9 +1,9 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System; using System.Numerics; @@ -12,10 +12,10 @@ namespace ImageProcessorCore.Filters /// /// Creates a glow effect on the image /// - public class Glow : ParallelImageProcessor + public class GlowProcessor : ParallelImageProcessor { /// - /// Gets or sets the vignette color to apply. + /// Gets or sets the glow color to apply. /// public Color Color { get; set; } = Color.White; @@ -40,19 +40,24 @@ namespace ImageProcessorCore.Filters float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f; float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY); - Parallel.For( - startY, - endY, - y => - { - for (int x = startX; x < endX; x++) + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => { - float distance = Vector2.Distance(centre, new Vector2(x, y)); - Color sourceColor = target[x, y]; - target[x, y] = Color.Lerp(glowColor, sourceColor, .5f * (distance / maxDistance)); - } - this.OnRowProcessed(); - }); + for (int x = startX; x < endX; x++) + { + float distance = Vector2.Distance(centre, new Vector2(x, y)); + Color sourceColor = sourcePixels[x, y]; + targetPixels[x, y] = Color.Lerp(glowColor, sourceColor, .5f * (distance / maxDistance)); + } + + this.OnRowProcessed(); + }); + } } } } diff --git a/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs b/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs new file mode 100644 index 000000000..31ef3ceb8 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs @@ -0,0 +1,48 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System.Numerics; + using System.Threading.Tasks; + + /// + /// An to invert the colors of an . + /// + public class InvertProcessor : ParallelImageProcessor + { + /// + 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; + Vector3 inverseVector = Vector3.One; + + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => + { + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x++) + { + Color color = sourcePixels[x, y]; + Vector3 vector = inverseVector - color.ToVector3(); + targetPixels[x, y] = new Color(vector, color.A); + } + + this.OnRowProcessed(); + } + }); + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs b/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs new file mode 100644 index 000000000..6c129bbee --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs @@ -0,0 +1,97 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + /// + /// An to invert the colors of an . + /// + public class PixelateProcessor : ParallelImageProcessor + { + /// + /// Initializes a new instance of the class. + /// + /// The size of the pixels. Must be greater than 0. + /// + /// is less than 0 or equal to 0. + /// + public PixelateProcessor(int size) + { + Guard.MustBeGreaterThan(size, 0, nameof(size)); + this.Value = size; + } + + /// + public override int Parallelism { get; set; } = 1; + + /// + /// Gets or the pixel size. + /// + public int Value { get; } + + /// + 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; + int size = this.Value; + int offset = this.Value / 2; + + // Get the range on the y-plane to choose from. + IEnumerable range = EnumerableExtensions.SteppedRange(startY, i => i < endY, size); + + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.ForEach( + range, + y => + { + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x += size) + { + + int offsetX = offset; + int offsetY = offset; + + // Make sure that the offset is within the boundary of the + // image. + while (y + offsetY >= sourceBottom) + { + offsetY--; + } + + while (x + offsetX >= endX) + { + offsetX--; + } + + // Get the pixel color in the centre of the soon to be pixelated area. + // ReSharper disable AccessToDisposedClosure + Color pixel = sourcePixels[x + offsetX, y + offsetY]; + + // For each pixel in the pixelate size, set it to the centre color. + for (int l = y; l < y + size && l < sourceBottom; l++) + { + for (int k = x; k < x + size && k < endX; k++) + { + targetPixels[k, l] = pixel; + } + } + } + this.OnRowProcessed(); + } + }); + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Vignette.cs b/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs similarity index 59% rename from src/ImageProcessorCore/Filters/Vignette.cs rename to src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs index af9c806b6..6a1bbc684 100644 --- a/src/ImageProcessorCore/Filters/Vignette.cs +++ b/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs @@ -1,9 +1,9 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore.Filters +namespace ImageProcessorCore.Processors { using System; using System.Numerics; @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Filters /// /// Creates a vignette effect on the image /// - public class Vignette : ParallelImageProcessor + public class VignetteProcessor : ParallelImageProcessor { /// /// Gets or sets the vignette color to apply. @@ -40,19 +40,23 @@ namespace ImageProcessorCore.Filters float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f; float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY); - Parallel.For( - startY, - endY, - y => - { - for (int x = startX; x < endX; x++) + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => { - float distance = Vector2.Distance(centre, new Vector2(x, y)); - Color sourceColor = target[x, y]; - target[x, y] = Color.Lerp(vignetteColor, sourceColor, 1 - .9f * distance / maxDistance); - } - this.OnRowProcessed(); - }); + for (int x = startX; x < endX; x++) + { + float distance = Vector2.Distance(centre, new Vector2(x, y)); + Color sourceColor = sourcePixels[x, y]; + targetPixels[x, y] = Color.Lerp(vignetteColor, sourceColor, 1 - .9f * distance / maxDistance); + } + this.OnRowProcessed(); + }); + } } } } diff --git a/src/ImageProcessorCore/Filters/Saturation.cs b/src/ImageProcessorCore/Filters/Saturation.cs new file mode 100644 index 000000000..93c69fe3e --- /dev/null +++ b/src/ImageProcessorCore/Filters/Saturation.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the saturation component of the image. + /// + /// The image this method extends. + /// The new saturation of the image. Must be between -100 and 100. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Saturation(this Image source, int amount, ProgressEventHandler progressHandler = null) + { + return Saturation(source, amount, source.Bounds, progressHandler); + } + + /// + /// Alters the saturation component of the image. + /// + /// The image this method extends. + /// The new saturation of the image. Must be between -100 and 100. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Saturation(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + SaturationProcessor processor = new SaturationProcessor(amount); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Sepia.cs b/src/ImageProcessorCore/Filters/Sepia.cs new file mode 100644 index 000000000..27d19b319 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Sepia.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Applies sepia toning to the image. + /// + /// The image this method extends. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Sepia(this Image source, ProgressEventHandler progressHandler = null) + { + return Sepia(source, source.Bounds, progressHandler); + } + + /// + /// Applies sepia toning to the image. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Sepia(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) + { + SepiaProcessor processor = new SepiaProcessor(); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs b/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs index 2c4ffd694..8c16b4e40 100644 --- a/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs @@ -132,15 +132,18 @@ namespace ImageProcessorCore.Formats amount = 4 - amount; } - switch (this.bmpBitsPerPixel) + using (PixelAccessor pixels = image.Lock()) { - case BmpBitsPerPixel.Pixel32: - this.Write32bit(writer, image, amount); - break; + switch (this.bmpBitsPerPixel) + { + case BmpBitsPerPixel.Pixel32: + this.Write32bit(writer, pixels, amount); + break; - case BmpBitsPerPixel.Pixel24: - this.Write24bit(writer, image, amount); - break; + case BmpBitsPerPixel.Pixel24: + this.Write24bit(writer, pixels, amount); + break; + } } } @@ -148,18 +151,18 @@ namespace ImageProcessorCore.Formats /// Writes the 32bit color palette to the stream. /// /// The containing the stream to write to. - /// The containing pixel data. + /// The containing pixel data. /// The amount to pad each row by. - private void Write32bit(EndianBinaryWriter writer, ImageBase image, int amount) + private void Write32bit(EndianBinaryWriter writer, PixelAccessor pixels, int amount) { - for (int y = image.Height - 1; y >= 0; y--) + for (int y = pixels.Height - 1; y >= 0; y--) { - for (int x = 0; x < image.Width; x++) + for (int x = 0; x < pixels.Width; x++) { // Limit the output range and multiply out from our floating point. // Convert back to b-> g-> r-> a order. // Convert to non-premultiplied color. - Bgra32 color = Color.ToNonPremultiplied(image[x, y]); + Bgra32 color = Color.ToNonPremultiplied(pixels[x, y]); // We can take advantage of BGRA here writer.Write(color.Bgra); @@ -177,18 +180,18 @@ namespace ImageProcessorCore.Formats /// Writes the 24bit color palette to the stream. /// /// The containing the stream to write to. - /// The containing pixel data. + /// The containing pixel data. /// The amount to pad each row by. - private void Write24bit(EndianBinaryWriter writer, ImageBase image, int amount) + private void Write24bit(EndianBinaryWriter writer, PixelAccessor pixels, int amount) { - for (int y = image.Height - 1; y >= 0; y--) + for (int y = pixels.Height - 1; y >= 0; y--) { - for (int x = 0; x < image.Width; x++) + for (int x = 0; x < pixels.Width; x++) { // Limit the output range and multiply out from our floating point. // Convert back to b-> g-> r-> a order. // Convert to non-premultiplied color. - Bgra32 color = Color.ToNonPremultiplied(image[x, y]); + Bgra32 color = Color.ToNonPremultiplied(pixels[x, y]); // Allocate 1 array instead of allocating 3. writer.Write(new[] { color.B, color.G, color.R }); diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs index 87d9d6be1..32c4d5f28 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs +++ b/src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs @@ -90,7 +90,7 @@ namespace ImageProcessorCore.Formats } else { - encode.Encode(stream, image, this.Quality, this.Quality >= 80 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420); + encode.Encode(stream, image, this.Quality, this.Quality >= 80 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420); } } } diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs b/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs index a8c18d264..a1f49a0c8 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs +++ b/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs @@ -423,15 +423,15 @@ namespace ImageProcessorCore.Formats // toYCbCr converts the 8x8 region of m whose top-left corner is p to its // YCbCr values. - private void toYCbCr(ImageBase m, int x, int y, Block yBlock, Block cbBlock, Block crBlock) + private void toYCbCr(PixelAccessor pixels, int x, int y, Block yBlock, Block cbBlock, Block crBlock) { - int xmax = m.Width - 1; - int ymax = m.Height - 1; + int xmax = pixels.Width - 1; + int ymax = pixels.Height - 1; for (int j = 0; j < 8; j++) { for (int i = 0; i < 8; i++) { - YCbCr color = m[Math.Min(x + i, xmax), Math.Min(y + j, ymax)]; + YCbCr color = pixels[Math.Min(x + i, xmax), Math.Min(y + j, ymax)]; int index = (8 * j) + i; yBlock[index] = (int)color.Y; cbBlock[index] = (int)color.Cb; @@ -486,17 +486,17 @@ namespace ImageProcessorCore.Formats // writeSOS writes the StartOfScan marker. - private void writeSOS(ImageBase m) + private void writeSOS(PixelAccessor pixels) { outputStream.Write(sosHeaderYCbCr, 0, sosHeaderYCbCr.Length); switch (subsample) { case JpegSubsample.Ratio444: - encode444(m); + encode444(pixels); break; case JpegSubsample.Ratio420: - encode420(m); + encode420(pixels); break; } @@ -504,18 +504,18 @@ namespace ImageProcessorCore.Formats emit(0x7f, 7); } - private void encode444(ImageBase m) + private void encode444(PixelAccessor pixels) { Block b = new Block(); Block cb = new Block(); Block cr = new Block(); int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; - for (int y = 0; y < m.Height; y += 8) + for (int y = 0; y < pixels.Height; y += 8) { - for (int x = 0; x < m.Width; x += 8) + for (int x = 0; x < pixels.Width; x += 8) { - toYCbCr(m, x, y, b, cb, cr); + toYCbCr(pixels, x, y, b, cb, cr); prevDCY = writeBlock(b, (quantIndex)0, prevDCY); prevDCCb = writeBlock(cb, (quantIndex)1, prevDCCb); prevDCCr = writeBlock(cr, (quantIndex)1, prevDCCr); @@ -523,7 +523,7 @@ namespace ImageProcessorCore.Formats } } - private void encode420(ImageBase m) + private void encode420(PixelAccessor pixels) { Block b = new Block(); Block[] cb = new Block[4]; @@ -533,16 +533,16 @@ namespace ImageProcessorCore.Formats for (int i = 0; i < 4; i++) cb[i] = new Block(); for (int i = 0; i < 4; i++) cr[i] = new Block(); - for (int y = 0; y < m.Height; y += 16) + for (int y = 0; y < pixels.Height; y += 16) { - for (int x = 0; x < m.Width; x += 16) + for (int x = 0; x < pixels.Width; x += 16) { for (int i = 0; i < 4; i++) { int xOff = (i & 1) * 8; int yOff = (i & 2) * 4; - toYCbCr(m, x + xOff, y + yOff, b, cb[i], cr[i]); + toYCbCr(pixels, x + xOff, y + yOff, b, cb[i], cr[i]); prevDCY = writeBlock(b, (quantIndex)0, prevDCY); } scale_16x16_8x8(b, cb); @@ -555,7 +555,7 @@ namespace ImageProcessorCore.Formats // Encode writes the Image m to w in JPEG 4:2:0 baseline format with the given // options. Default parameters are used if a nil *Options is passed. - public void Encode(Stream stream, ImageBase m, int quality, JpegSubsample subsample) + public void Encode(Stream stream, ImageBase image, int quality, JpegSubsample subsample) { this.outputStream = stream; this.subsample = subsample; @@ -570,9 +570,9 @@ namespace ImageProcessorCore.Formats quant[i] = new byte[Block.blockSize]; } - if (m.Width >= (1 << 16) || m.Height >= (1 << 16)) + if (image.Width >= (1 << 16) || image.Height >= (1 << 16)) { - throw new ImageFormatException($"Image is too large to encode at {m.Width}x{m.Height}."); + throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}."); } if (quality < 1) quality = 1; @@ -614,13 +614,16 @@ namespace ImageProcessorCore.Formats writeDQT(); // Write the image dimensions. - writeSOF0(m.Width, m.Height, nComponent); + writeSOF0(image.Width, image.Height, nComponent); // Write the Huffman tables. writeDHT(nComponent); // Write the image data. - writeSOS(m); + using (PixelAccessor pixels = image.Lock()) + { + writeSOS(pixels); + } // Write the End Of Image marker. buf[0] = 0xff; diff --git a/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs b/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs index b2fdab051..7e61f736e 100644 --- a/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs +++ b/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs @@ -123,7 +123,12 @@ namespace ImageProcessorCore.Formats this.WritePaletteChunk(stream, header, image); this.WritePhysicalChunk(stream, image); this.WriteGammaChunk(stream); - this.WriteDataChunks(stream, image); + + using (PixelAccessor pixels = image.Lock()) + { + this.WriteDataChunks(stream, pixels); + } + this.WriteEndChunk(stream); stream.Flush(); } @@ -292,12 +297,12 @@ namespace ImageProcessorCore.Formats /// Writes the pixel information to the stream. /// /// The containing image data. - /// The image base. - private void WriteDataChunks(Stream stream, ImageBase image) + /// The image pixels. + private void WriteDataChunks(Stream stream, PixelAccessor pixels) { byte[] data; - int imageWidth = image.Width; - int imageHeight = image.Height; + int imageWidth = pixels.Width; + int imageHeight = pixels.Height; // Indexed image. if (this.Quality <= 256) @@ -327,7 +332,7 @@ namespace ImageProcessorCore.Formats else { // TrueColor image. - data = new byte[(imageWidth * imageHeight * 4) + image.Height]; + data = new byte[(imageWidth * imageHeight * 4) + pixels.Height]; int rowLength = (imageWidth * 4) + 1; @@ -343,7 +348,7 @@ namespace ImageProcessorCore.Formats for (int x = 0; x < imageWidth; x++) { - Bgra32 color = Color.ToNonPremultiplied(image[x, y]); + Bgra32 color = Color.ToNonPremultiplied(pixels[x, y]); // Calculate the offset for the new array. int dataOffset = (y * rowLength) + (x * 4) + 1; @@ -354,7 +359,7 @@ namespace ImageProcessorCore.Formats if (y > 0) { - color = Color.ToNonPremultiplied(image[x, y - 1]); + color = Color.ToNonPremultiplied(pixels[x, y - 1]); data[dataOffset] -= color.R; data[dataOffset + 1] -= color.G; diff --git a/src/ImageProcessorCore/IImage.cs b/src/ImageProcessorCore/IImage.cs deleted file mode 100644 index 008be3ddb..000000000 --- a/src/ImageProcessorCore/IImage.cs +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessorCore -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageProcessorCore.Formats; - - /// - /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. - /// - public interface IImage : IImageBase - { - /// - /// Gets or sets the resolution of the image in x- direction. It is defined as - /// number of dots per inch and should be an positive value. - /// - /// The density of the image in x- direction. - double HorizontalResolution { get; set; } - - /// - /// Gets or sets the resolution of the image in y- direction. It is defined as - /// number of dots per inch and should be an positive value. - /// - /// The density of the image in y- direction. - double VerticalResolution { get; set; } - - /// - /// Gets the width of the image in inches. It is calculated as the width of the image - /// in pixels multiplied with the density. When the density is equals or less than zero - /// the default value is used. - /// - /// The width of the image in inches. - double InchWidth { get; } - - /// - /// Gets the height of the image in inches. It is calculated as the height of the image - /// in pixels multiplied with the density. When the density is equals or less than zero - /// the default value is used. - /// - /// The height of the image in inches. - double InchHeight { get; } - - /// - /// Gets a value indicating whether this image is animated. - /// - /// - /// True if this image is animated; otherwise, false. - /// - bool IsAnimated { get; } - - /// - /// Gets or sets the number of times any animation is repeated. - /// 0 means to repeat indefinitely. - /// - ushort RepeatCount { get; set; } - - /// - /// Gets the currently loaded image format. - /// - IImageFormat CurrentImageFormat { get; } - - /// - /// Gets the other frames for the animation. - /// - /// The list of frame images. - IList Frames { get; } - - /// - /// Gets the list of properties for storing meta information about this image. - /// - /// A list of image properties. - IList Properties { get; } - - /// - /// Saves the image to the given stream using the currently loaded image format. - /// - /// The stream to save the image to. - /// Thrown if the stream is null. - void Save(Stream stream); - - /// - /// 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. - /// Thrown if the stream is null. - void Save(Stream stream, IImageFormat format); - - /// - /// Saves the image to the given stream using the given image encoder. - /// - /// The stream to save the image to. - /// The encoder to save the image with. - /// Thrown if the stream is null. - void Save(Stream stream, IImageEncoder encoder); - } -} \ No newline at end of file diff --git a/src/ImageProcessorCore/IImageBase.cs b/src/ImageProcessorCore/IImageBase.cs deleted file mode 100644 index c9bd8d958..000000000 --- a/src/ImageProcessorCore/IImageBase.cs +++ /dev/null @@ -1,106 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessorCore -{ - using System; - using System.Runtime.CompilerServices; - - /// - /// Encapsulates the basic properties and methods required to manipulate images. - /// - public interface IImageBase - { - /// - /// Gets the image pixels as byte array. - /// - /// - /// The returned array has a length of Width * Height * 4 bytes - /// and stores the red, the green, the blue, and the alpha value for - /// each pixel in this order. - /// - float[] Pixels { get; } - - /// - /// Gets the width in pixels. - /// - int Width { get; } - - /// - /// Gets the height in pixels. - /// - int Height { get; } - - /// - /// Gets the pixel ratio made up of the width and height. - /// - double PixelRatio { get; } - - /// - /// Gets the representing the bounds of the image. - /// - Rectangle Bounds { get; } - - /// - /// Gets or sets th quality of the image. This affects the output quality of lossy image formats. - /// - int Quality { get; set; } - - /// - /// Gets or sets the frame delay for animated images. - /// If not 0, this field specifies the number of hundredths (1/100) of a second to - /// wait before continuing with the processing of the Data Stream. - /// The clock starts ticking immediately after the graphic is rendered. - /// - int FrameDelay { get; set; } - - /// - /// Gets or sets the color of a pixel at the specified position. - /// - /// - /// The x-coordinate of the pixel. Must be greater - /// than zero and smaller than the width of the pixel. - /// - /// - /// The y-coordinate of the pixel. Must be greater - /// than zero and smaller than the width of the pixel. - /// - /// The at the specified position. - Color this[int x, int y] { get; set; } - - /// - /// Sets the pixel array of the image to the given value. - /// - /// The new width of the image. Must be greater than zero. - /// The new height of the image. Must be greater than zero. - /// - /// The array with colors. Must be a multiple of four times the width and height. - /// - /// - /// Thrown if either or are less than or equal to 0. - /// - /// - /// Thrown if the length is not equal to Width * Height * 4. - /// - void SetPixels(int width, int height, float[] pixels); - - /// - /// Sets the pixel array of the image to the given value, creating a copy of - /// the original pixels. - /// - /// The new width of the image. Must be greater than zero. - /// The new height of the image. Must be greater than zero. - /// - /// The array with colors. Must be a multiple of four times the width and height. - /// - /// - /// Thrown if either or are less than or equal to 0. - /// - /// - /// Thrown if the length is not equal to Width * Height * 4. - /// - void ClonePixels(int width, int height, float[] pixels); - } -} diff --git a/src/ImageProcessorCore/IImageProcessor.cs b/src/ImageProcessorCore/IImageProcessor.cs index 6eb649e84..e71762b10 100644 --- a/src/ImageProcessorCore/IImageProcessor.cs +++ b/src/ImageProcessorCore/IImageProcessor.cs @@ -1,9 +1,9 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore +namespace ImageProcessorCore.Processors { /// /// A delegate which is called as progress is made processing an image. diff --git a/src/ImageProcessorCore/Image.cs b/src/ImageProcessorCore/Image.cs index fce2543a8..3cde69543 100644 --- a/src/ImageProcessorCore/Image.cs +++ b/src/ImageProcessorCore/Image.cs @@ -19,10 +19,10 @@ namespace ImageProcessorCore /// /// /// The image data is always stored in RGBA format, where the red, green, blue, and - /// alpha values are floats. + /// alpha values are floating point numbers. /// [DebuggerDisplay("Image: {Width}x{Height}")] - public class Image : ImageBase, IImage + public class Image : ImageBase { /// /// The default horizontal resolution value (dots per inch) in x direction. @@ -97,13 +97,26 @@ namespace ImageProcessorCore /// public IReadOnlyCollection Formats { get; } = Bootstrapper.Instance.ImageFormats; - /// + /// + /// Gets or sets the resolution of the image in x- direction. It is defined as + /// number of dots per inch and should be an positive value. + /// + /// The density of the image in x- direction. public double HorizontalResolution { get; set; } = DefaultHorizontalResolution; - /// + /// + /// Gets or sets the resolution of the image in y- direction. It is defined as + /// number of dots per inch and should be an positive value. + /// + /// The density of the image in y- direction. public double VerticalResolution { get; set; } = DefaultVerticalResolution; - /// + /// + /// Gets the width of the image in inches. It is calculated as the width of the image + /// in pixels multiplied with the density. When the density is equals or less than zero + /// the default value is used. + /// + /// The width of the image in inches. public double InchWidth { get @@ -119,7 +132,12 @@ namespace ImageProcessorCore } } - /// + /// + /// Gets the height of the image in inches. It is calculated as the height of the image + /// in pixels multiplied with the density. When the density is equals or less than zero + /// the default value is used. + /// + /// The height of the image in inches. public double InchHeight { get @@ -135,36 +153,66 @@ namespace ImageProcessorCore } } - /// + /// + /// Gets a value indicating whether this image is animated. + /// + /// + /// True if this image is animated; otherwise, false. + /// public bool IsAnimated => this.Frames.Count > 0; - /// + /// + /// Gets or sets the number of times any animation is repeated. + /// 0 means to repeat indefinitely. + /// public ushort RepeatCount { get; set; } - /// + /// + /// Gets the other frames for the animation. + /// + /// The list of frame images. public IList Frames { get; } = new List(); - /// + /// + /// Gets the list of properties for storing meta information about this image. + /// + /// A list of image properties. public IList Properties { get; } = new List(); - /// + /// + /// Gets the currently loaded image format. + /// public IImageFormat CurrentImageFormat { get; internal set; } - /// + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The stream to save the image to. + /// Thrown if the stream is null. public void Save(Stream stream) { Guard.NotNull(stream, nameof(stream)); this.CurrentImageFormat.Encoder.Encode(this, stream); } - /// + /// + /// 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. + /// Thrown if the stream is null. public void Save(Stream stream, IImageFormat format) { Guard.NotNull(stream, nameof(stream)); format.Encoder.Encode(this, stream); } - /// + /// + /// Saves the image to the given stream using the given image encoder. + /// + /// The stream to save the image to. + /// The encoder to save the image with. + /// Thrown if the stream is null. public void Save(Stream stream, IImageEncoder encoder) { Guard.NotNull(stream, nameof(stream)); @@ -186,27 +234,6 @@ namespace ImageProcessorCore } } - /// - protected override void Dispose(bool disposing) - { - if (this.IsDisposed) - { - return; - } - - // Dispose of the unmanaged resources for each frame here. - if (this.Frames.Any()) - { - foreach (ImageFrame frame in this.Frames) - { - frame.Dispose(); - } - this.Frames.Clear(); - } - - base.Dispose(disposing); - } - /// /// Loads the image from the given stream. /// diff --git a/src/ImageProcessorCore/ImageBase.cs b/src/ImageProcessorCore/ImageBase.cs index 42fd85fd0..5b7ab65f8 100644 --- a/src/ImageProcessorCore/ImageBase.cs +++ b/src/ImageProcessorCore/ImageBase.cs @@ -6,43 +6,18 @@ namespace ImageProcessorCore { using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; /// /// The base class of all images. Encapsulates the basic properties and methods /// required to manipulate images. /// - public abstract unsafe class ImageBase : IImageBase, IDisposable + public abstract class ImageBase { - /// - /// The position of the first pixel in the bitmap. - /// - private float* pixelsBase; - /// /// The array of pixels. /// private float[] pixelsArray; - /// - /// Provides a way to access the pixels from unmanaged memory. - /// - private GCHandle pixelsHandle; - - /// - /// A value indicating whether this instance of the given entity has been disposed. - /// - /// if this instance has been disposed; otherwise, . - /// - /// If the entity is disposed, it must not be disposed a second - /// time. The isDisposed field is set the first time the entity - /// is disposed. If the isDisposed field is true, then the Dispose() - /// method will not dispose again. This help not to prolong the entity's - /// life in the Garbage Collector. - /// - internal bool IsDisposed; - /// /// Initializes a new instance of the class. /// @@ -68,8 +43,6 @@ namespace ImageProcessorCore // Assign the pointer and pixels. this.pixelsArray = new float[width * height * 4]; - this.pixelsHandle = GCHandle.Alloc(this.pixelsArray, GCHandleType.Pinned); - this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); } /// @@ -90,19 +63,11 @@ namespace ImageProcessorCore this.Quality = other.Quality; this.FrameDelay = other.FrameDelay; - // Assign the pointer and copy the pixels. + // Copy the pixels. this.pixelsArray = new float[this.Width * this.Height * 4]; - this.pixelsHandle = GCHandle.Alloc(this.pixelsArray, GCHandleType.Pinned); - this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); Array.Copy(other.pixelsArray, this.pixelsArray, other.pixelsArray.Length); } - /// - ~ImageBase() - { - this.Dispose(false); - } - /// /// Gets or sets the maximum allowable width in pixels. /// @@ -113,67 +78,65 @@ namespace ImageProcessorCore /// public static int MaxHeight { get; set; } = int.MaxValue; - /// + /// + /// Gets the image pixels as byte array. + /// + /// + /// The returned array has a length of Width * Height * 4 bytes + /// and stores the red, the green, the blue, and the alpha value for + /// each pixel in this order. + /// public float[] Pixels => this.pixelsArray; - /// + /// + /// Gets the width in pixels. + /// public int Width { get; private set; } - /// + /// + /// Gets the height in pixels. + /// public int Height { get; private set; } - /// + /// + /// Gets the pixel ratio made up of the width and height. + /// public double PixelRatio => (double)this.Width / this.Height; - /// + /// + /// Gets the representing the bounds of the image. + /// public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height); - /// + /// + /// Gets or sets th quality of the image. This affects the output quality of lossy image formats. + /// public int Quality { get; set; } - /// + /// + /// Gets or sets the frame delay for animated images. + /// If not 0, this field specifies the number of hundredths (1/100) of a second to + /// wait before continuing with the processing of the Data Stream. + /// The clock starts ticking immediately after the graphic is rendered. + /// public int FrameDelay { get; set; } - /// - public Color this[int x, int y] - { - get - { -#if DEBUG - if ((x < 0) || (x >= this.Width)) - { - throw new ArgumentOutOfRangeException(nameof(x), "Value cannot be less than zero or greater than the bitmap width."); - } - - if ((y < 0) || (y >= this.Height)) - { - 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)); - } - - set - { -#if DEBUG - if ((x < 0) || (x >= this.Width)) - { - throw new ArgumentOutOfRangeException(nameof(x), "Value cannot be less than zero or greater than the bitmap width."); - } - - if ((y < 0) || (y >= this.Height)) - { - 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; - } - } - - /// + /// + /// Sets the pixel array of the image to the given value. + /// + /// The new width of the image. Must be greater than zero. + /// The new height of the image. Must be greater than zero. + /// + /// The array with colors. Must be a multiple of four times the width and height. + /// + /// + /// Thrown if either or are less than or equal to 0. + /// + /// + /// Thrown if the length is not equal to Width * Height * 4. + /// public void SetPixels(int width, int height, float[] pixels) { -#if DEBUG if (width <= 0) { throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than or equals than zero."); @@ -188,28 +151,30 @@ namespace ImageProcessorCore { throw new ArgumentException("Pixel array must have the length of Width * Height * 4."); } -#endif + this.Width = width; this.Height = height; - - // Ensure nothing is preserved if previously allocated. - if (this.pixelsHandle.IsAllocated) - { - this.pixelsArray = null; - this.pixelsHandle.Free(); - this.pixelsBase = null; - } - this.pixelsArray = pixels; - this.pixelsHandle = GCHandle.Alloc(this.pixelsArray, GCHandleType.Pinned); - this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); } - /// + /// + /// Sets the pixel array of the image to the given value, creating a copy of + /// the original pixels. + /// + /// The new width of the image. Must be greater than zero. + /// The new height of the image. Must be greater than zero. + /// + /// The array with colors. Must be a multiple of four times the width and height. + /// + /// + /// Thrown if either or are less than or equal to 0. + /// + /// + /// Thrown if the length is not equal to Width * Height * 4. + /// public void ClonePixels(int width, int height, float[] pixels) { -#if DEBUG - if (width <= 0) + if (width <= 0) { throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than or equals than zero."); } @@ -223,55 +188,25 @@ namespace ImageProcessorCore { throw new ArgumentException("Pixel array must have the length of Width * Height * 4."); } -#endif + this.Width = width; this.Height = height; - // Assign the pointer and copy the pixels. + // Copy the pixels. this.pixelsArray = new float[pixels.Length]; - this.pixelsHandle = GCHandle.Alloc(this.pixelsArray, GCHandleType.Pinned); - this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); Array.Copy(pixels, this.pixelsArray, pixels.Length); } - /// - public void Dispose() - { - this.Dispose(true); - - // This object will be cleaned up by the Dispose method. - // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue - // and prevent finalization code for this object - // from executing a second time. - GC.SuppressFinalize(this); - } - /// - /// Disposes the object and frees resources for the Garbage Collector. + /// Locks the image providing access to the pixels. + /// + /// It is imperative that the accessor is correctly disposed off after use. + /// /// - /// If true, the object gets disposed. - protected virtual void Dispose(bool disposing) + /// The + public PixelAccessor Lock() { - if (this.IsDisposed) - { - return; - } - - if (disposing) - { - // Dispose of any managed resources here. - this.pixelsArray = null; - } - - if (this.pixelsHandle.IsAllocated) - { - this.pixelsHandle.Free(); - this.pixelsBase = null; - } - - // Note disposing is done. - this.IsDisposed = true; + return new PixelAccessor(this); } } } diff --git a/src/ImageProcessorCore/ImageExtensions.cs b/src/ImageProcessorCore/ImageExtensions.cs index 7379f20c8..e7b261d60 100644 --- a/src/ImageProcessorCore/ImageExtensions.cs +++ b/src/ImageProcessorCore/ImageExtensions.cs @@ -9,6 +9,7 @@ namespace ImageProcessorCore using System.IO; using Formats; + using Processors; /// /// Extension methods for the type. @@ -128,11 +129,6 @@ namespace ImageProcessorCore /// Thrown if the has been disposed. private static Image PerformAction(Image source, bool clone, Action action) { - if (source.IsDisposed) - { - throw new ObjectDisposedException("Image"); - } - Image transformedImage = clone ? new Image(source) : new Image @@ -163,9 +159,6 @@ namespace ImageProcessorCore } } - // According to http://stackoverflow.com/questions/37921815/idisposable-unmanaged-fields-reference-types-and-assignments - // There's no need to dispose of the original image as the GC will get around to cleaning it up now there are no references to the original data. - // TODO: Investigate how long this is held onto and try to keep that to a minimum. source = transformedImage; return source; } diff --git a/src/ImageProcessorCore/ParallelImageProcessor.cs b/src/ImageProcessorCore/ParallelImageProcessor.cs index fd47b2357..a6b9fc354 100644 --- a/src/ImageProcessorCore/ParallelImageProcessor.cs +++ b/src/ImageProcessorCore/ParallelImageProcessor.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore +namespace ImageProcessorCore.Processors { using System; using System.Threading; diff --git a/src/ImageProcessorCore/PixelAccessor.cs b/src/ImageProcessorCore/PixelAccessor.cs new file mode 100644 index 000000000..23749769b --- /dev/null +++ b/src/ImageProcessorCore/PixelAccessor.cs @@ -0,0 +1,164 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Provides per-pixel access to an images pixels. + /// + public sealed unsafe class PixelAccessor : IDisposable + { + /// + /// The position of the first pixel in the bitmap. + /// + private float* pixelsBase; + + /// + /// Provides a way to access the pixels from unmanaged memory. + /// + private GCHandle pixelsHandle; + + /// + /// A value indicating whether this instance of the given entity has been disposed. + /// + /// if this instance has been disposed; otherwise, . + /// + /// If the entity is disposed, it must not be disposed a second + /// time. The isDisposed field is set the first time the entity + /// is disposed. If the isDisposed field is true, then the Dispose() + /// method will not dispose again. This help not to prolong the entity's + /// life in the Garbage Collector. + /// + private bool isDisposed; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The image to provide pixel access for. + /// + public PixelAccessor(ImageBase image) + { + Guard.NotNull(image, nameof(image)); + 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; + } + } + } + + /// + /// Finalizes an instance of the class. + /// + ~PixelAccessor() + { + this.Dispose(); + } + + /// + /// Gets the width of the image. + /// + public int Width { get; } + + /// + /// Gets the height of the image. + /// + public int Height { get; } + + /// + /// Gets or sets the color of a pixel at the specified position. + /// + /// + /// The x-coordinate of the pixel. Must be greater + /// than zero and smaller than the width of the pixel. + /// + /// + /// The y-coordinate of the pixel. Must be greater + /// than zero and smaller than the width of the pixel. + /// + /// The at the specified position. + public Color this[int x, int y] + { + get + { +#if DEBUG + if ((x < 0) || (x >= this.Width)) + { + throw new ArgumentOutOfRangeException(nameof(x), "Value cannot be less than zero or greater than the bitmap width."); + } + + if ((y < 0) || (y >= this.Height)) + { + 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)); + } + + set + { +#if DEBUG + if ((x < 0) || (x >= this.Width)) + { + throw new ArgumentOutOfRangeException(nameof(x), "Value cannot be less than zero or greater than the bitmap width."); + } + + if ((y < 0) || (y >= this.Height)) + { + 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; + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + if (this.isDisposed) + { + return; + } + + if (this.pixelsHandle.IsAllocated) + { + this.pixelsHandle.Free(); + } + + this.pixelsBase = null; + + // Note disposing is done. + this.isDisposed = true; + + // This object will be cleaned up by the Dispose method. + // Therefore, you should call GC.SuppressFinalize to + // take this object off the finalization queue + // and prevent finalization code for this object + // from executing a second time. + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs b/src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs index 268772cb8..40d281015 100644 --- a/src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs +++ b/src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs @@ -50,21 +50,24 @@ namespace ImageProcessorCore.Quantizers // Get the size of the source image int height = image.Height; int width = image.Width; - - // Call the FirstPass function if not a single pass algorithm. - // For something like an Octree quantizer, this will run through - // all image pixels, build a data structure, and create a palette. - if (!this.singlePass) - { - this.FirstPass(image, width, height); - } - byte[] quantizedPixels = new byte[width * height]; + List palette; - // Get the palette - List palette = this.GetPalette(); + using (PixelAccessor pixels = image.Lock()) + { + // Call the FirstPass function if not a single pass algorithm. + // For something like an Octree quantizer, this will run through + // all image pixels, build a data structure, and create a palette. + if (!this.singlePass) + { + this.FirstPass(pixels, width, height); + } + + // Get the palette + palette = this.GetPalette(); - this.SecondPass(image, quantizedPixels, width, height); + this.SecondPass(pixels, quantizedPixels, width, height); + } return new QuantizedImage(width, height, palette.ToArray(), quantizedPixels, this.TransparentIndex); } @@ -75,7 +78,7 @@ namespace ImageProcessorCore.Quantizers /// The source data /// The width in pixels of the image. /// The height in pixels of the image. - protected virtual void FirstPass(ImageBase source, int width, int height) + protected virtual void FirstPass(PixelAccessor source, int width, int height) { // Loop through each row for (int y = 0; y < height; y++) @@ -96,7 +99,7 @@ namespace ImageProcessorCore.Quantizers /// The output pixel array /// The width in pixels of the image /// The height in pixels of the image - protected virtual void SecondPass(ImageBase source, byte[] output, int width, int height) + protected virtual void SecondPass(PixelAccessor source, byte[] output, int width, int height) { Parallel.For( 0, diff --git a/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs b/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs index 55e0bdcc9..c06262503 100644 --- a/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs +++ b/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs @@ -123,13 +123,16 @@ namespace ImageProcessorCore.Quantizers this.Clear(); - this.Build3DHistogram(image); - this.Get3DMoments(); + using (PixelAccessor imagePixels = image.Lock()) + { + this.Build3DHistogram(imagePixels); + this.Get3DMoments(); - Box[] cube; - this.BuildCube(out cube, ref colorCount); + Box[] cube; + this.BuildCube(out cube, ref colorCount); - return this.GenerateResult(image, colorCount, cube); + return this.GenerateResult(imagePixels, colorCount, cube); + } } /// @@ -318,7 +321,7 @@ namespace ImageProcessorCore.Quantizers /// Builds a 3-D color histogram of counts, r/g/b, c^2. /// /// The image. - private void Build3DHistogram(ImageBase image) + private void Build3DHistogram(PixelAccessor image) { for (int y = 0; y < image.Height; y++) { @@ -711,15 +714,17 @@ namespace ImageProcessorCore.Quantizers /// /// Generates the quantized result. /// - /// The image. + /// The image pixels. /// The color count. /// The cube. /// The result. - private QuantizedImage GenerateResult(ImageBase image, int colorCount, Box[] cube) + private QuantizedImage GenerateResult(PixelAccessor imagePixels, int colorCount, Box[] cube) { List pallette = new List(); - byte[] pixels = new byte[image.Width * image.Height]; + byte[] pixels = new byte[imagePixels.Width * imagePixels.Height]; int transparentIndex = -1; + int width = imagePixels.Width; + int height = imagePixels.Height; for (int k = 0; k < colorCount; k++) { @@ -752,12 +757,12 @@ namespace ImageProcessorCore.Quantizers Parallel.For( 0, - image.Height, + height, y => { - for (int x = 0; x < image.Width; x++) + for (int x = 0; x < width; x++) { - Bgra32 color = image[x, y]; + Bgra32 color = imagePixels[x, y]; int a = color.A >> (8 - IndexAlphaBits); int r = color.R >> (8 - IndexBits); int g = color.G >> (8 - IndexBits); @@ -765,16 +770,17 @@ namespace ImageProcessorCore.Quantizers if (transparentIndex > -1 && color.A <= this.Threshold) { - pixels[(y * image.Width) + x] = (byte)transparentIndex; + pixels[(y * width) + x] = (byte)transparentIndex; continue; } int ind = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); - pixels[(y * image.Width) + x] = this.tag[ind]; + pixels[(y * width) + x] = this.tag[ind]; } }); - return new QuantizedImage(image.Width, image.Height, pallette.ToArray(), pixels, transparentIndex); + + return new QuantizedImage(width, height, pallette.ToArray(), pixels, transparentIndex); } } } \ No newline at end of file diff --git a/src/ImageProcessorCore/Samplers/Crop.cs b/src/ImageProcessorCore/Samplers/Crop.cs index 23f4d18f5..239fd63f1 100644 --- a/src/ImageProcessorCore/Samplers/Crop.cs +++ b/src/ImageProcessorCore/Samplers/Crop.cs @@ -5,6 +5,8 @@ namespace ImageProcessorCore { + using Processors; + /// /// Extension methods for the type. /// diff --git a/src/ImageProcessorCore/Samplers/EntropyCrop.cs b/src/ImageProcessorCore/Samplers/EntropyCrop.cs index 8a6cafc75..55284668c 100644 --- a/src/ImageProcessorCore/Samplers/EntropyCrop.cs +++ b/src/ImageProcessorCore/Samplers/EntropyCrop.cs @@ -5,6 +5,8 @@ namespace ImageProcessorCore { + using Processors; + /// /// Extension methods for the type. /// diff --git a/src/ImageProcessorCore/Samplers/Pad.cs b/src/ImageProcessorCore/Samplers/Pad.cs index fcdac9200..de973d345 100644 --- a/src/ImageProcessorCore/Samplers/Pad.cs +++ b/src/ImageProcessorCore/Samplers/Pad.cs @@ -5,6 +5,8 @@ namespace ImageProcessorCore { + using Processors; + /// /// Extension methods for the type. /// diff --git a/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs index d6347ea8a..e6707afa0 100644 --- a/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore +namespace ImageProcessorCore.Processors { using System.Threading.Tasks; @@ -22,21 +22,25 @@ namespace ImageProcessorCore int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; - Parallel.For( - startY, - endY, - y => + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) { - if (y >= targetY && y < targetBottom) - { - for (int x = startX; x < endX; x++) - { - target[x, y] = source[x + sourceX, y + sourceY]; - } + Parallel.For( + startY, + endY, + y => + { + if (y >= targetY && y < targetBottom) + { + for (int x = startX; x < endX; x++) + { + targetPixels[x, y] = sourcePixels[x + sourceX, y + sourceY]; + } - this.OnRowProcessed(); - } - }); + this.OnRowProcessed(); + } + }); + } } } } diff --git a/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs index 697148f09..e0ffb29e1 100644 --- a/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs @@ -3,13 +3,11 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore +namespace ImageProcessorCore.Processors { using System; using System.Threading.Tasks; - using ImageProcessorCore.Filters; - /// /// Provides methods to allow the cropping of an image to preserve areas of highest /// entropy. @@ -42,21 +40,20 @@ namespace ImageProcessorCore /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { - using (ImageBase temp = new Image(source.Width, source.Height)) - { - // Detect the edges. - new Sobel().Apply(temp, source, sourceRectangle); + ImageBase temp = new Image(source.Width, source.Height); - // Apply threshold binarization filter. - new Threshold(.5f).Apply(temp, temp, sourceRectangle); + // Detect the edges. + new SobelProcessor().Apply(temp, source, sourceRectangle); - // Search for the first white pixels - Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); + // Apply threshold binarization filter. + new ThresholdProcessor(.5f).Apply(temp, temp, sourceRectangle); - // Reset the target pixel to the correct size. - target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]); - this.cropRectangle = rectangle; - } + // Search for the first white pixels + Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); + + // Reset the target pixel to the correct size. + target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]); + this.cropRectangle = rectangle; } /// @@ -73,21 +70,25 @@ namespace ImageProcessorCore int startX = this.cropRectangle.X; int endX = this.cropRectangle.Right; - Parallel.For( - startY, - endY, - y => + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) { - if (y >= targetY && y < targetBottom) - { - for (int x = startX; x < endX; x++) - { - target[x - startX, y - targetY] = source[x, y]; - } - } + Parallel.For( + startY, + endY, + y => + { + if (y >= targetY && y < targetBottom) + { + for (int x = startX; x < endX; x++) + { + targetPixels[x - startX, y - targetY] = sourcePixels[x, y]; + } + } - this.OnRowProcessed(); - }); + this.OnRowProcessed(); + }); + } } /// diff --git a/src/ImageProcessorCore/Samplers/Processors/IImageSampler.cs b/src/ImageProcessorCore/Samplers/Processors/IImageSampler.cs index 69e017c52..76a2c5a4d 100644 --- a/src/ImageProcessorCore/Samplers/Processors/IImageSampler.cs +++ b/src/ImageProcessorCore/Samplers/Processors/IImageSampler.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore +namespace ImageProcessorCore.Processors { /// /// Acts as a marker for generic parameters that require an image sampler. diff --git a/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs b/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs index ee1a7dd7e..cc8bfe4cc 100644 --- a/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs +++ b/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore +namespace ImageProcessorCore.Processors { /// /// Applies sampling methods to an image. diff --git a/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs index e72e303e6..82b07d40b 100644 --- a/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore +namespace ImageProcessorCore.Processors { using System; using System.Threading.Tasks; @@ -62,7 +62,13 @@ namespace ImageProcessorCore } /// - 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) @@ -87,30 +93,34 @@ namespace ImageProcessorCore float widthFactor = sourceRectangle.Width / (float)targetRectangle.Width; float heightFactor = sourceRectangle.Height / (float)targetRectangle.Height; - Parallel.For( - startY, - endY, - y => - { - if (targetY <= y && y < targetBottom) - { - // Y coordinates of source points - int originY = (int)((y - startY) * heightFactor); - - for (int x = startX; x < endX; x++) + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => { - if (targetX <= x && x < targetRight) + if (targetY <= y && y < targetBottom) { - // X coordinates of source points - int originX = (int)((x - startX) * widthFactor); + // Y coordinates of source points + int originY = (int)((y - startY) * heightFactor); - target[x, y] = source[originX, originY]; - } - } + for (int x = startX; x < endX; x++) + { + if (targetX <= x && x < targetRight) + { + // X coordinates of source points + int originX = (int)((x - startX) * widthFactor); - this.OnRowProcessed(); - } - }); + targetPixels[x, y] = sourcePixels[originX, originY]; + } + } + + this.OnRowProcessed(); + } + }); + } // Break out now. return; @@ -120,78 +130,90 @@ namespace ImageProcessorCore // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. - Parallel.For( - 0, - sourceHeight, - y => - { - for (int x = startX; x < endX; x++) + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor firstPassPixels = this.firstPass.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + 0, + sourceHeight, + y => { - if (x >= 0 && x < width) + for (int x = startX; x < endX; x++) { - // Ensure offsets are normalised for cropping and padding. - int offsetX = x - startX; - float sum = this.HorizontalWeights[offsetX].Sum; - Weight[] horizontalValues = this.HorizontalWeights[offsetX].Values; - - // Destination color components - Color destination = new Color(); - - for (int i = 0; i < sum; i++) - { - Weight xw = horizontalValues[i]; - int originX = xw.Index; - Color sourceColor = compand ? Color.Expand(source[originX, y]) : source[originX, y]; - destination += sourceColor * xw.Value; - } - - if (compand) + if (x >= 0 && x < width) { - destination = Color.Compress(destination); + // Ensure offsets are normalised for cropping and padding. + int offsetX = x - startX; + float sum = this.HorizontalWeights[offsetX].Sum; + Weight[] horizontalValues = this.HorizontalWeights[offsetX].Values; + + // Destination color components + Color destination = new Color(); + + for (int i = 0; i < sum; i++) + { + Weight xw = horizontalValues[i]; + int originX = xw.Index; + Color sourceColor = compand + ? Color.Expand(sourcePixels[originX, y]) + : sourcePixels[originX, y]; + + destination += sourceColor * xw.Value; + } + + if (compand) + { + destination = Color.Compress(destination); + } + + firstPassPixels[x, y] = destination; } - - this.firstPass[x, y] = destination; } - } - }); + }); - // Now process the rows. - Parallel.For( - startY, - endY, - y => - { - if (y >= 0 && y < height) + // Now process the rows. + Parallel.For( + startY, + endY, + y => { - // Ensure offsets are normalised for cropping and padding. - int offsetY = y - startY; - float sum = this.VerticalWeights[offsetY].Sum; - Weight[] verticalValues = this.VerticalWeights[offsetY].Values; - - for (int x = 0; x < width; x++) + if (y >= 0 && y < height) { - // Destination color components - Color destination = new Color(); - - for (int i = 0; i < sum; i++) - { - Weight yw = verticalValues[i]; - int originY = yw.Index; - Color sourceColor = compand ? Color.Expand(this.firstPass[x, originY]) : this.firstPass[x, originY]; - destination += sourceColor * yw.Value; - } + // Ensure offsets are normalised for cropping and padding. + int offsetY = y - startY; + float sum = this.VerticalWeights[offsetY].Sum; + Weight[] verticalValues = this.VerticalWeights[offsetY].Values; - if (compand) + for (int x = 0; x < width; x++) { - destination = Color.Compress(destination); + // Destination color components + Color destination = new Color(); + + for (int i = 0; i < sum; i++) + { + Weight yw = verticalValues[i]; + int originY = yw.Index; + Color sourceColor = compand + ? Color.Expand(firstPassPixels[x, originY]) + : firstPassPixels[x, originY]; + + destination += sourceColor * yw.Value; + } + + if (compand) + { + destination = Color.Compress(destination); + } + + targetPixels[x, y] = destination; } - - target[x, y] = destination; } - } - this.OnRowProcessed(); - }); + this.OnRowProcessed(); + }); + + } } /// @@ -202,9 +224,6 @@ namespace ImageProcessorCore { target.ClonePixels(target.Width, target.Height, source.Pixels); } - - // Clean up - this.firstPass?.Dispose(); } /// diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateFlip.cs b/src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs similarity index 59% rename from src/ImageProcessorCore/Samplers/Processors/RotateFlip.cs rename to src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs index d732a8bb1..263242fc1 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateFlip.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs @@ -2,7 +2,8 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore + +namespace ImageProcessorCore.Processors { using System; using System.Threading.Tasks; @@ -78,20 +79,26 @@ namespace ImageProcessorCore int height = source.Height; Image temp = new Image(height, width); - Parallel.For(0, height, - y => - { - for (int x = 0; x < width; x++) - { - int newX = height - y - 1; - newX = height - newX - 1; - int newY = width - x - 1; - newY = width - newY - 1; - temp[newX, newY] = source[x, y]; - } - - this.OnRowProcessed(); - }); + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor tempPixels = temp.Lock()) + { + Parallel.For( + 0, + height, + y => + { + for (int x = 0; x < width; x++) + { + int newX = height - y - 1; + newX = height - newX - 1; + int newY = width - x - 1; + newY = width - newY - 1; + tempPixels[newX, newY] = sourcePixels[x, y]; + } + + this.OnRowProcessed(); + }); + } target.SetPixels(height, width, temp.Pixels); } @@ -106,18 +113,24 @@ namespace ImageProcessorCore int width = source.Width; int height = source.Height; - Parallel.For(0, height, - y => - { - for (int x = 0; x < width; x++) - { - int newX = width - x - 1; - int newY = height - y - 1; - target[newX, newY] = source[x, y]; - } - - this.OnRowProcessed(); - }); + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + 0, + height, + y => + { + for (int x = 0; x < width; x++) + { + int newX = width - x - 1; + int newY = height - y - 1; + targetPixels[newX, newY] = sourcePixels[x, y]; + } + + this.OnRowProcessed(); + }); + } } /// @@ -131,17 +144,23 @@ namespace ImageProcessorCore int height = source.Height; Image temp = new Image(height, width); - Parallel.For(0, height, - y => - { - for (int x = 0; x < width; x++) - { - int newX = height - y - 1; - temp[newX, x] = source[x, y]; - } - - this.OnRowProcessed(); - }); + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor tempPixels = temp.Lock()) + { + Parallel.For( + 0, + height, + y => + { + for (int x = 0; x < width; x++) + { + int newX = height - y - 1; + tempPixels[newX, x] = sourcePixels[x, y]; + } + + this.OnRowProcessed(); + }); + } target.SetPixels(height, width, temp.Pixels); } @@ -159,18 +178,24 @@ namespace ImageProcessorCore ImageBase temp = new Image(width, height); temp.ClonePixels(width, height, target.Pixels); - Parallel.For(0, halfHeight, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor targetPixels = target.Lock()) + using (PixelAccessor tempPixels = temp.Lock()) + { + Parallel.For( + 0, + halfHeight, + y => { - int newY = height - y - 1; - target[x, y] = temp[x, newY]; - target[x, newY] = temp[x, y]; - } - - this.OnRowProcessed(); - }); + for (int x = 0; x < width; x++) + { + int newY = height - y - 1; + targetPixels[x, y] = tempPixels[x, newY]; + targetPixels[x, newY] = tempPixels[x, y]; + } + + this.OnRowProcessed(); + }); + } } /// @@ -186,18 +211,24 @@ namespace ImageProcessorCore ImageBase temp = new Image(width, height); temp.ClonePixels(width, height, target.Pixels); - Parallel.For(0, height, - y => - { - for (int x = 0; x < halfWidth; x++) - { - int newX = width - x - 1; - target[x, y] = temp[newX, y]; - target[newX, y] = temp[x, y]; - } - - this.OnRowProcessed(); - }); + using (PixelAccessor targetPixels = target.Lock()) + using (PixelAccessor tempPixels = temp.Lock()) + { + Parallel.For( + 0, + height, + y => + { + for (int x = 0; x < halfWidth; x++) + { + int newX = width - x - 1; + targetPixels[x, y] = tempPixels[newX, y]; + targetPixels[newX, y] = tempPixels[x, y]; + } + + this.OnRowProcessed(); + }); + } } } } diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs index 33c31bf9c..c4fbdfd14 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore +namespace ImageProcessorCore.Processors { using System.Numerics; using System.Threading.Tasks; @@ -104,30 +104,27 @@ namespace ImageProcessorCore // Since we are not working in parallel we use full height and width // of the first pass image. - Parallel.For( - 0, - height, - y => - { - for (int x = startX; x < endX; x++) - { - // Rotate at the centre point - Point rotated = Point.Rotate(new Point(x, y), rotation); - if (this.firstPass.Bounds.Contains(rotated.X, rotated.Y)) + using (PixelAccessor firstPassPixels = this.firstPass.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + 0, + height, + y => { - target[x, y] = this.firstPass[rotated.X, rotated.Y]; - } - } - - this.OnRowProcessed(); - }); - } - - /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) - { - // Cleanup. - this.firstPass.Dispose(); + for (int x = startX; x < endX; x++) + { + // Rotate at the centre point + Point rotated = Point.Rotate(new Point(x, y), rotation); + if (this.firstPass.Bounds.Contains(rotated.X, rotated.Y)) + { + targetPixels[x, y] = firstPassPixels[rotated.X, rotated.Y]; + } + } + + this.OnRowProcessed(); + }); + } } } } \ No newline at end of file diff --git a/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs index 1bfcfb7e2..82a8d5cff 100644 --- a/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageProcessorCore +namespace ImageProcessorCore.Processors { using System.Numerics; using System.Threading.Tasks; @@ -135,30 +135,27 @@ namespace ImageProcessorCore // Since we are not working in parallel we use full height and width // of the first pass image. - Parallel.For( - 0, - height, - y => - { - for (int x = startX; x < endX; x++) - { - // Skew at the centre point - Point skewed = Point.Skew(new Point(x, y), skew); - if (this.firstPass.Bounds.Contains(skewed.X, skewed.Y)) + using (PixelAccessor firstPassPixels = this.firstPass.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + 0, + height, + y => { - target[x, y] = this.firstPass[skewed.X, skewed.Y]; - } - } - - this.OnRowProcessed(); - }); - } - - /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) - { - // Cleanup. - this.firstPass.Dispose(); + for (int x = startX; x < endX; x++) + { + // Skew at the centre point + Point skewed = Point.Skew(new Point(x, y), skew); + if (this.firstPass.Bounds.Contains(skewed.X, skewed.Y)) + { + targetPixels[x, y] = firstPassPixels[skewed.X, skewed.Y]; + } + } + + this.OnRowProcessed(); + }); + } } } } \ No newline at end of file diff --git a/src/ImageProcessorCore/Samplers/Resize.cs b/src/ImageProcessorCore/Samplers/Resize.cs index a90c4c105..2eadd7a11 100644 --- a/src/ImageProcessorCore/Samplers/Resize.cs +++ b/src/ImageProcessorCore/Samplers/Resize.cs @@ -5,6 +5,8 @@ namespace ImageProcessorCore { + using Processors; + /// /// Extension methods for the type. /// diff --git a/src/ImageProcessorCore/Samplers/Rotate.cs b/src/ImageProcessorCore/Samplers/Rotate.cs index 49441fbb5..ea24f9650 100644 --- a/src/ImageProcessorCore/Samplers/Rotate.cs +++ b/src/ImageProcessorCore/Samplers/Rotate.cs @@ -5,6 +5,8 @@ namespace ImageProcessorCore { + using Processors; + /// /// Extension methods for the type. /// diff --git a/src/ImageProcessorCore/Samplers/RotateFlip.cs b/src/ImageProcessorCore/Samplers/RotateFlip.cs index 279bd274b..93449bcd0 100644 --- a/src/ImageProcessorCore/Samplers/RotateFlip.cs +++ b/src/ImageProcessorCore/Samplers/RotateFlip.cs @@ -5,6 +5,8 @@ namespace ImageProcessorCore { + using Processors; + /// /// Extension methods for the type. /// diff --git a/src/ImageProcessorCore/Samplers/Skew.cs b/src/ImageProcessorCore/Samplers/Skew.cs index 7e9088ab9..7b0587a52 100644 --- a/src/ImageProcessorCore/Samplers/Skew.cs +++ b/src/ImageProcessorCore/Samplers/Skew.cs @@ -5,6 +5,8 @@ namespace ImageProcessorCore { + using Processors; + /// /// Extension methods for the type. /// diff --git a/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs b/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs index fce14beb7..64d789636 100644 --- a/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs +++ b/tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs @@ -23,10 +23,11 @@ [Benchmark(Description = "ImageProcessorCore GetSet Pixel")] public CoreColor ResizeCore() { - using (CoreImage image = new CoreImage(400, 400)) + CoreImage image = new CoreImage(400, 400); + using (PixelAccessor imagePixels = image.Lock()) { - image[200, 200] = CoreColor.White; - return image[200, 200]; + imagePixels[200, 200] = CoreColor.White; + return imagePixels[200, 200]; } } } diff --git a/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs b/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs index 3c0cab4ca..4ffce9d48 100644 --- a/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs +++ b/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs @@ -4,8 +4,6 @@ using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; - - using ImageProcessorCore.Processors; using CoreImage = ImageProcessorCore.Image; using CoreSize = ImageProcessorCore.Size; @@ -34,11 +32,9 @@ [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); - } + CoreImage image = new CoreImage(400, 400); + image.Crop(100, 100); + return new CoreSize(image.Width, image.Height); } } } diff --git a/tests/ImageProcessorCore.Benchmarks/Samplers/Resize.cs b/tests/ImageProcessorCore.Benchmarks/Samplers/Resize.cs index 97415f2fa..479392cc8 100644 --- a/tests/ImageProcessorCore.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageProcessorCore.Benchmarks/Samplers/Resize.cs @@ -4,8 +4,6 @@ using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; - - using ImageProcessorCore.Processors; using CoreImage = ImageProcessorCore.Image; using CoreSize = ImageProcessorCore.Size; @@ -34,11 +32,9 @@ [Benchmark(Description = "ImageProcessorCore Resize")] public CoreSize ResizeCore() { - using (CoreImage image = new CoreImage(400, 400)) - { - image.Resize(100, 100); - return new CoreSize(image.Width, image.Height); - } + CoreImage image = new CoreImage(400, 400); + image.Resize(100, 100); + return new CoreSize(image.Width, image.Height); } } } diff --git a/tests/ImageProcessorCore.Tests/Formats/BitmapTests.cs b/tests/ImageProcessorCore.Tests/Formats/BitmapTests.cs index 68b4ea2b0..aded05692 100644 --- a/tests/ImageProcessorCore.Tests/Formats/BitmapTests.cs +++ b/tests/ImageProcessorCore.Tests/Formats/BitmapTests.cs @@ -27,23 +27,23 @@ namespace ImageProcessorCore.Tests using (FileStream stream = File.OpenRead(file)) { Stopwatch watch = Stopwatch.StartNew(); - using (Image image = new Image(stream)) - { - string encodeFilename = "TestOutput/Encode/Bitmap/" + "24-" + Path.GetFileNameWithoutExtension(file) + ".bmp"; - using (FileStream output = File.OpenWrite(encodeFilename)) - { - image.Save(output, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel24 }); - } + Image image = new Image(stream); + string encodeFilename = "TestOutput/Encode/Bitmap/" + "24-" + Path.GetFileNameWithoutExtension(file) + ".bmp"; + + using (FileStream output = File.OpenWrite(encodeFilename)) + { + image.Save(output, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel24 }); + } - encodeFilename = "TestOutput/Encode/Bitmap/" + "32-" + Path.GetFileNameWithoutExtension(file) + ".bmp"; + encodeFilename = "TestOutput/Encode/Bitmap/" + "32-" + Path.GetFileNameWithoutExtension(file) + ".bmp"; - using (FileStream output = File.OpenWrite(encodeFilename)) - { - image.Save(output, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel32 }); - } + using (FileStream output = File.OpenWrite(encodeFilename)) + { + image.Save(output, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel32 }); } + Trace.WriteLine($"{file} : {watch.ElapsedMilliseconds}ms"); } } diff --git a/tests/ImageProcessorCore.Tests/Formats/EncoderDecoderTests.cs b/tests/ImageProcessorCore.Tests/Formats/EncoderDecoderTests.cs index 05585284b..0b2522d75 100644 --- a/tests/ImageProcessorCore.Tests/Formats/EncoderDecoderTests.cs +++ b/tests/ImageProcessorCore.Tests/Formats/EncoderDecoderTests.cs @@ -30,11 +30,10 @@ namespace ImageProcessorCore.Tests using (FileStream stream = File.OpenRead(file)) { Stopwatch watch = Stopwatch.StartNew(); - using (Image image = new Image(stream)) - { - string filename = "TestOutput/ToString/" + Path.GetFileNameWithoutExtension(file) + ".txt"; - File.WriteAllText(filename, image.ToString()); - } + + Image image = new Image(stream); + string filename = "TestOutput/ToString/" + Path.GetFileNameWithoutExtension(file) + ".txt"; + File.WriteAllText(filename, image.ToString()); Trace.WriteLine($"{watch.ElapsedMilliseconds}ms"); } @@ -54,14 +53,13 @@ namespace ImageProcessorCore.Tests using (FileStream stream = File.OpenRead(file)) { Stopwatch watch = Stopwatch.StartNew(); - using (Image image = new Image(stream)) - { - string encodeFilename = "TestOutput/Encode/" + Path.GetFileName(file); - using (FileStream output = File.OpenWrite(encodeFilename)) - { - image.Save(output); - } + Image image = new Image(stream); + string encodeFilename = "TestOutput/Encode/" + Path.GetFileName(file); + + using (FileStream output = File.OpenWrite(encodeFilename)) + { + image.Save(output); } Trace.WriteLine($"{file} : {watch.ElapsedMilliseconds}ms"); @@ -81,37 +79,32 @@ namespace ImageProcessorCore.Tests { using (FileStream stream = File.OpenRead(file)) { - using (Image image = new Image(stream)) + Image image = new Image(stream); + IQuantizer quantizer = new OctreeQuantizer(); + QuantizedImage quantizedImage = quantizer.Quantize(image, 256); + + using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Octree-{Path.GetFileName(file)}")) { - IQuantizer quantizer = new OctreeQuantizer(); - QuantizedImage quantizedImage = quantizer.Quantize(image, 256); + Image qi = quantizedImage.ToImage(); + qi.Save(output, image.CurrentImageFormat); - using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Octree-{Path.GetFileName(file)}")) - { - using (Image qi = quantizedImage.ToImage()) - { - qi.Save(output, image.CurrentImageFormat); - } - } + } - quantizer = new WuQuantizer(); - quantizedImage = quantizer.Quantize(image, 256); + quantizer = new WuQuantizer(); + quantizedImage = quantizer.Quantize(image, 256); - using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Wu-{Path.GetFileName(file)}")) - { - quantizedImage.ToImage().Save(output, image.CurrentImageFormat); - } + using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Wu-{Path.GetFileName(file)}")) + { + quantizedImage.ToImage().Save(output, image.CurrentImageFormat); + } - quantizer = new PaletteQuantizer(); - quantizedImage = quantizer.Quantize(image, 256); + quantizer = new PaletteQuantizer(); + quantizedImage = quantizer.Quantize(image, 256); - using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Palette-{Path.GetFileName(file)}")) - { - using (Image qi = quantizedImage.ToImage()) - { - qi.Save(output, image.CurrentImageFormat); - } - } + using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Palette-{Path.GetFileName(file)}")) + { + Image qi = quantizedImage.ToImage(); + qi.Save(output, image.CurrentImageFormat); } } } @@ -129,27 +122,25 @@ namespace ImageProcessorCore.Tests { using (FileStream stream = File.OpenRead(file)) { - using (Image image = new Image(stream)) + Image image = new Image(stream); + using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.gif")) { - using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.gif")) - { - image.SaveAsGif(output); - } + image.SaveAsGif(output); + } - using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.bmp")) - { - image.SaveAsBmp(output); - } + using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.bmp")) + { + image.SaveAsBmp(output); + } - using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.jpg")) - { - image.SaveAsJpeg(output); - } + using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.jpg")) + { + image.SaveAsJpeg(output); + } - using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.png")) - { - image.SaveAsPng(output); - } + using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.png")) + { + image.SaveAsPng(output); } } } @@ -167,25 +158,21 @@ namespace ImageProcessorCore.Tests { using (FileStream stream = File.OpenRead(file)) { - using (Image image = new Image(stream)) + Image image = new Image(stream); + byte[] serialized; + using (MemoryStream memoryStream = new MemoryStream()) { - byte[] serialized; - using (MemoryStream memoryStream = new MemoryStream()) - { - image.Save(memoryStream); - memoryStream.Flush(); - serialized = memoryStream.ToArray(); - } + image.Save(memoryStream); + memoryStream.Flush(); + serialized = memoryStream.ToArray(); + } - using (MemoryStream memoryStream = new MemoryStream(serialized)) + using (MemoryStream memoryStream = new MemoryStream(serialized)) + { + Image image2 = new Image(memoryStream); + using (FileStream output = File.OpenWrite($"TestOutput/Serialized/{Path.GetFileName(file)}")) { - using (Image image2 = new Image(memoryStream)) - { - using (FileStream output = File.OpenWrite($"TestOutput/Serialized/{Path.GetFileName(file)}")) - { - image2.Save(output); - } - } + image2.Save(output); } } } diff --git a/tests/ImageProcessorCore.Tests/Formats/PngTests.cs b/tests/ImageProcessorCore.Tests/Formats/PngTests.cs index 00e90b794..e824a62f9 100644 --- a/tests/ImageProcessorCore.Tests/Formats/PngTests.cs +++ b/tests/ImageProcessorCore.Tests/Formats/PngTests.cs @@ -25,13 +25,11 @@ namespace ImageProcessorCore.Tests { using (FileStream stream = File.OpenRead(file)) { - using (Image image = new Image(stream)) + Image image = new Image(stream); + using (FileStream output = File.OpenWrite($"TestOutput/Encode/Png/{Path.GetFileNameWithoutExtension(file)}.png")) { - using (FileStream output = File.OpenWrite($"TestOutput/Encode/Png/{Path.GetFileNameWithoutExtension(file)}.png")) - { - image.Quality = 256; - image.Save(output, new PngFormat()); - } + image.Quality = 256; + image.Save(output, new PngFormat()); } } } diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs index db48b7dd5..5632fe5a1 100644 --- a/tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs +++ b/tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs @@ -4,7 +4,7 @@ namespace ImageProcessorCore.Tests using System.Diagnostics; using System.IO; - using ImageProcessorCore.Filters; + using Processors; using Xunit; @@ -12,47 +12,47 @@ namespace ImageProcessorCore.Tests { public static readonly TheoryData Filters = new TheoryData { - { "Brightness-50", new Brightness(50) }, - { "Brightness--50", new Brightness(-50) }, - { "Contrast-50", new Contrast(50) }, - { "Contrast--50", new Contrast(-50) }, - { "BackgroundColor", new BackgroundColor(new Color(243 / 255f, 87 / 255f, 161 / 255f,.5f))}, - { "Blend", new Blend(new Image(File.OpenRead("TestImages/Formats/Bmp/Car.bmp")),50)}, - { "Saturation-50", new Saturation(50) }, - { "Saturation--50", new Saturation(-50) }, - { "Alpha--50", new Alpha(50) }, - { "Invert", new Invert() }, - { "Sepia", new Sepia() }, - { "BlackWhite", new BlackWhite() }, - { "Lomograph", new Lomograph() }, - { "Polaroid", new Polaroid() }, - { "Kodachrome", new Kodachrome() }, - { "GreyscaleBt709", new GreyscaleBt709() }, - { "GreyscaleBt601", new GreyscaleBt601() }, - { "Kayyali", new Kayyali() }, - { "Kirsch", new Kirsch() }, - { "Laplacian3X3", new Laplacian3X3() }, - { "Laplacian5X5", new Laplacian5X5() }, - { "LaplacianOfGaussian", new LaplacianOfGaussian() }, - { "Prewitt", new Prewitt() }, - { "RobertsCross", new RobertsCross() }, - { "Scharr", new Scharr() }, - { "Sobel", new Sobel {Greyscale = true} }, - { "Pixelate", new Pixelate(8) }, - { "GuassianBlur", new GuassianBlur(10) }, - { "GuassianSharpen", new GuassianSharpen(10) }, - { "Hue-180", new Hue(180) }, - { "Hue--180", new Hue(-180) }, - { "BoxBlur", new BoxBlur(10) }, - { "Vignette", new Vignette() }, - { "Protanopia", new Protanopia() }, - { "Protanomaly", new Protanomaly() }, - { "Deuteranopia", new Deuteranopia() }, - { "Deuteranomaly", new Deuteranomaly() }, - { "Tritanopia", new Tritanopia() }, - { "Tritanomaly", new Tritanomaly() }, - { "Achromatopsia", new Achromatopsia() }, - { "Achromatomaly", new Achromatomaly() } + { "Brightness-50", new BrightnessProcessor(50) }, + { "Brightness--50", new BrightnessProcessor(-50) }, + { "Contrast-50", new ContrastProcessor(50) }, + { "Contrast--50", new ContrastProcessor(-50) }, + { "BackgroundColor", new BackgroundColorProcessor(new Color(243 / 255f, 87 / 255f, 161 / 255f,.5f))}, + { "Blend", new BlendProcessor(new Image(File.OpenRead("TestImages/Formats/Bmp/Car.bmp")),50)}, + { "Saturation-50", new SaturationProcessor(50) }, + { "Saturation--50", new SaturationProcessor(-50) }, + { "Alpha--50", new AlphaProcessor(50) }, + { "Invert", new InvertProcessor() }, + { "Sepia", new SepiaProcessor() }, + { "BlackWhite", new BlackWhiteProcessor() }, + { "Lomograph", new LomographProcessor() }, + { "Polaroid", new PolaroidProcessor() }, + { "Kodachrome", new KodachromeProcessor() }, + { "GreyscaleBt709", new GreyscaleBt709Processor() }, + { "GreyscaleBt601", new GreyscaleBt601Processor() }, + { "Kayyali", new KayyaliProcessor() }, + { "Kirsch", new KirschProcessor() }, + { "Laplacian3X3", new Laplacian3X3Processor() }, + { "Laplacian5X5", new Laplacian5X5Processor() }, + { "LaplacianOfGaussian", new LaplacianOfGaussianProcessor() }, + { "Prewitt", new PrewittProcessor() }, + { "RobertsCross", new RobertsCrossProcessor() }, + { "Scharr", new ScharrProcessor() }, + { "Sobel", new SobelProcessor {Greyscale = true} }, + { "Pixelate", new PixelateProcessor(8) }, + { "GuassianBlur", new GuassianBlurProcessor(10) }, + { "GuassianSharpen", new GuassianSharpenProcessor(10) }, + { "Hue-180", new HueProcessor(180) }, + { "Hue--180", new HueProcessor(-180) }, + { "BoxBlur", new BoxBlurProcessor(10) }, + { "Vignette", new VignetteProcessor() }, + { "Protanopia", new ProtanopiaProcessor() }, + { "Protanomaly", new ProtanomalyProcessor() }, + { "Deuteranopia", new DeuteranopiaProcessor() }, + { "Deuteranomaly", new DeuteranomalyProcessor() }, + { "Tritanopia", new TritanopiaProcessor() }, + { "Tritanomaly", new TritanomalyProcessor() }, + { "Achromatopsia", new AchromatopsiaProcessor() }, + { "Achromatomaly", new AchromatomalyProcessor() } }; @@ -70,16 +70,16 @@ namespace ImageProcessorCore.Tests using (FileStream stream = File.OpenRead(file)) { Stopwatch watch = Stopwatch.StartNew(); - using (Image image = new Image(stream)) + + Image image = new Image(stream); + string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); + using (FileStream output = File.OpenWrite($"TestOutput/Filter/{Path.GetFileName(filename)}")) { - string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); - using (FileStream output = File.OpenWrite($"TestOutput/Filter/{Path.GetFileName(filename)}")) - { - processor.OnProgress += this.ProgressUpdate; - image.Process(processor).Save(output); - processor.OnProgress -= this.ProgressUpdate; - } + processor.OnProgress += this.ProgressUpdate; + image.Process(processor).Save(output); + processor.OnProgress -= this.ProgressUpdate; } + Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms"); } } diff --git a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs index ac610746e..d9a8608d1 100644 --- a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs +++ b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs @@ -3,10 +3,9 @@ using System.Diagnostics; using System.IO; - using ImageProcessorCore.Processors; + using Processors; using Xunit; - using Filters; public class SamplerTests : FileTestBase { @@ -70,7 +69,6 @@ processor.OnProgress -= this.ProgressUpdate; } - image.Dispose(); Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms"); } } @@ -92,7 +90,7 @@ string filename = Path.GetFileName(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/Pad/{filename}")) { image.Pad(image.Width + 50, image.Height + 50, this.ProgressUpdate) @@ -120,7 +118,7 @@ Stopwatch watch = Stopwatch.StartNew(); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}")) { image.Resize(image.Width / 2, image.Height / 2, sampler, false, this.ProgressUpdate) @@ -149,7 +147,7 @@ Stopwatch watch = Stopwatch.StartNew(); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}")) { image.Resize(image.Width / 3, 0, new TriangleResampler(), false, this.ProgressUpdate) @@ -178,7 +176,7 @@ Stopwatch watch = Stopwatch.StartNew(); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}")) { image.Resize(0, image.Height / 3, new TriangleResampler(), false, this.ProgressUpdate) @@ -205,7 +203,7 @@ Stopwatch watch = Stopwatch.StartNew(); string filename = Path.GetFileName(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/ResizeCrop/{filename}")) { ResizeOptions options = new ResizeOptions() @@ -237,7 +235,7 @@ Stopwatch watch = Stopwatch.StartNew(); string filename = Path.GetFileName(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/ResizePad/{filename}")) { ResizeOptions options = new ResizeOptions() @@ -270,7 +268,7 @@ Stopwatch watch = Stopwatch.StartNew(); string filename = Path.GetFileName(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/ResizeBoxPad/{filename}")) { ResizeOptions options = new ResizeOptions() @@ -303,7 +301,7 @@ Stopwatch watch = Stopwatch.StartNew(); string filename = Path.GetFileName(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/ResizeMax/{filename}")) { ResizeOptions options = new ResizeOptions() @@ -337,7 +335,7 @@ Stopwatch watch = Stopwatch.StartNew(); string filename = Path.GetFileName(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/ResizeMin/{filename}")) { ResizeOptions options = new ResizeOptions() @@ -370,7 +368,7 @@ Stopwatch watch = Stopwatch.StartNew(); string filename = Path.GetFileName(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/ResizeStretch/{filename}")) { ResizeOptions options = new ResizeOptions() @@ -417,8 +415,6 @@ .Save(output); } - image.Dispose(); - Trace.WriteLine($"{filename}: {watch.ElapsedMilliseconds}ms"); } } @@ -440,7 +436,7 @@ Stopwatch watch = Stopwatch.StartNew(); string filename = Path.GetFileNameWithoutExtension(file) + "-" + rotateType + flipType + Path.GetExtension(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/RotateFlip/{filename}")) { image.RotateFlip(rotateType, flipType, this.ProgressUpdate) @@ -468,7 +464,7 @@ string filename = Path.GetFileName(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}")) { image.Rotate(63, this.ProgressUpdate) @@ -497,7 +493,7 @@ string filename = Path.GetFileName(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/Skew/{filename}")) { image.Skew(20, 10, this.ProgressUpdate) @@ -523,7 +519,7 @@ { string filename = Path.GetFileNameWithoutExtension(file) + "-EntropyCrop" + Path.GetExtension(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/EntropyCrop/{filename}")) { image.EntropyCrop(.5f, this.ProgressUpdate).Save(output); @@ -547,7 +543,7 @@ string filename = Path.GetFileNameWithoutExtension(file) + "-Crop" + Path.GetExtension(file); - using (Image image = new Image(stream)) + Image image = new Image(stream); using (FileStream output = File.OpenWrite($"TestOutput/Crop/{filename}")) { image.Crop(image.Width / 2, image.Height / 2, this.ProgressUpdate).Save(output);