Browse Source

Merge remote-tracking branch 'refs/remotes/origin/Core-Lock' into Core

Former-commit-id: 4ea90489cba68da7b0ad07768a669db176d2bc2d
Former-commit-id: 91e96abdcb9a5efef4a2af02e3766d48be8e75e4
Former-commit-id: c9a452193efb87b72082943580513cbaa8247db0
pull/1/head
James Jackson-South 10 years ago
parent
commit
bab7387404
  1. 4
      README.md
  2. 37
      src/ImageProcessorCore/Common/Helpers/ImageMaths.cs
  3. 73
      src/ImageProcessorCore/Filters/Alpha.cs
  4. 80
      src/ImageProcessorCore/Filters/BackgroundColor.cs
  5. 50
      src/ImageProcessorCore/Filters/BlackWhite.cs
  6. 93
      src/ImageProcessorCore/Filters/Blend.cs
  7. 52
      src/ImageProcessorCore/Filters/BoxBlur.cs
  8. 75
      src/ImageProcessorCore/Filters/Brightness.cs
  9. 88
      src/ImageProcessorCore/Filters/ColorBlindness.cs
  10. 77
      src/ImageProcessorCore/Filters/Contrast.cs
  11. 63
      src/ImageProcessorCore/Filters/DetectEdges.cs
  12. 55
      src/ImageProcessorCore/Filters/Greyscale.cs
  13. 52
      src/ImageProcessorCore/Filters/GuassianBlur.cs
  14. 52
      src/ImageProcessorCore/Filters/GuassianSharpen.cs
  15. 52
      src/ImageProcessorCore/Filters/Hue.cs
  16. 784
      src/ImageProcessorCore/Filters/ImageFilterExtensions.cs
  17. 65
      src/ImageProcessorCore/Filters/Invert.cs
  18. 50
      src/ImageProcessorCore/Filters/Kodachrome.cs
  19. 50
      src/ImageProcessorCore/Filters/Lomograph.cs
  20. 2
      src/ImageProcessorCore/Filters/Options/ColorBlindness.cs
  21. 107
      src/ImageProcessorCore/Filters/Pixelate.cs
  22. 50
      src/ImageProcessorCore/Filters/Polaroid.cs
  23. 69
      src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs
  24. 78
      src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs
  25. 22
      src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs
  26. 86
      src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs
  27. 69
      src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs
  28. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs
  29. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs
  30. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs
  31. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs
  32. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs
  33. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs
  34. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs
  35. 0
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/README.md
  36. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs
  37. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs
  38. 11
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs
  39. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt601Processor.cs
  40. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt709Processor.cs
  41. 2
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleMode.cs
  42. 13
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs
  43. 5
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs
  44. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/KodachromeProcessor.cs
  45. 8
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs
  46. 10
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs
  47. 10
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs
  48. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs
  49. 70
      src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs
  50. 10
      src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs
  51. 18
      src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs
  52. 12
      src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs
  53. 12
      src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs
  54. 4
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs
  55. 4
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs
  56. 2
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs
  57. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs
  58. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs
  59. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs
  60. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs
  61. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs
  62. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs
  63. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs
  64. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs
  65. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs
  66. 18
      src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs
  67. 18
      src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs
  68. 37
      src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs
  69. 48
      src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs
  70. 97
      src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs
  71. 34
      src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs
  72. 52
      src/ImageProcessorCore/Filters/Saturation.cs
  73. 50
      src/ImageProcessorCore/Filters/Sepia.cs
  74. 37
      src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs
  75. 2
      src/ImageProcessorCore/Formats/Jpg/JpegEncoder.cs
  76. 43
      src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs
  77. 21
      src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs
  78. 103
      src/ImageProcessorCore/IImage.cs
  79. 106
      src/ImageProcessorCore/IImageBase.cs
  80. 4
      src/ImageProcessorCore/IImageProcessor.cs
  81. 97
      src/ImageProcessorCore/Image.cs
  82. 207
      src/ImageProcessorCore/ImageBase.cs
  83. 9
      src/ImageProcessorCore/ImageExtensions.cs
  84. 2
      src/ImageProcessorCore/ParallelImageProcessor.cs
  85. 164
      src/ImageProcessorCore/PixelAccessor.cs
  86. 31
      src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs
  87. 36
      src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs
  88. 2
      src/ImageProcessorCore/Samplers/Crop.cs
  89. 2
      src/ImageProcessorCore/Samplers/EntropyCrop.cs
  90. 2
      src/ImageProcessorCore/Samplers/Pad.cs
  91. 32
      src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs
  92. 57
      src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs
  93. 2
      src/ImageProcessorCore/Samplers/Processors/IImageSampler.cs
  94. 2
      src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs
  95. 187
      src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs
  96. 153
      src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs
  97. 45
      src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs
  98. 45
      src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs
  99. 2
      src/ImageProcessorCore/Samplers/Resize.cs
  100. 2
      src/ImageProcessorCore/Samplers/Rotate.cs

4
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<IImageProcessor> processors = new List<IImageProcessor>()
{
new GuassianBlur(5),

37
src/ImageProcessorCore/Common/Helpers/ImageMaths.cs

@ -171,35 +171,35 @@ namespace ImageProcessorCore
Point topLeft = new Point();
Point bottomRight = new Point();
Func<ImageBase, int, int, float, bool> delegateFunc;
Func<PixelAccessor, int, int, float, bool> 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<ImageBase, int> getMinY = imageBase =>
Func<PixelAccessor, int> 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<ImageBase, int> getMaxY = imageBase =>
Func<PixelAccessor, int> 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<ImageBase, int> getMinX = imageBase =>
Func<PixelAccessor, int> 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<ImageBase, int> getMaxX = imageBase =>
Func<PixelAccessor, int> 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);
}

73
src/ImageProcessorCore/Filters/Alpha.cs

@ -1,63 +1,52 @@
// <copyright file="Alpha.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore
{
using System;
using System.Numerics;
using System.Threading.Tasks;
using Processors;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the Alpha of an <see cref="Image"/>.
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public class Alpha : ParallelImageProcessor
public static partial class ImageExtensions
{
/// <summary>
/// Initializes a new instance of the <see cref="Alpha"/> class.
/// Alters the alpha component of the image.
/// </summary>
/// <param name="percent">The percentage to adjust the opacity of the image. Must be between 0 and 100.</param>
/// <exception cref="ArgumentException">
/// <paramref name="percent"/> is less than 0 or is greater than 100.
/// </exception>
public Alpha(int percent)
/// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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);
}
/// <summary>
/// Gets the alpha value.
/// Alters the alpha component of the image.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
/// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

80
src/ImageProcessorCore/Filters/BackgroundColor.cs

@ -1,73 +1,37 @@
// <copyright file="BackgroundColor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore
{
using System;
using System.Threading.Tasks;
using Processors;
/// <summary>
/// Sets the background color of the image.
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public class BackgroundColor : ParallelImageProcessor
public static partial class ImageExtensions
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// Combines the given image together with the current one by blending their pixels.
/// </summary>
private const float Epsilon = 0.001f;
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundColor"/> class.
/// </summary>
/// <param name="color">The <see cref="Color"/> to set the background color to.</param>
public BackgroundColor(Color color)
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color to set as the background.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image BackgroundColor(this Image source, Color color, ProgressEventHandler progressHandler = null)
{
this.Value = Color.FromNonPremultiplied(color);
}
/// <summary>
/// Gets the background color value.
/// </summary>
public Color Value { get; }
/// <inheritdoc/>
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;
}
}
}
}

50
src/ImageProcessorCore/Filters/BlackWhite.cs

@ -0,0 +1,50 @@
// <copyright file="BlackWhite.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies black and white toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image BlackWhite(this Image source, ProgressEventHandler progressHandler = null)
{
return BlackWhite(source, source.Bounds, progressHandler);
}
/// <summary>
/// Applies black and white toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

93
src/ImageProcessorCore/Filters/Blend.cs

@ -1,81 +1,60 @@
// <copyright file="Blend.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore
{
using System.Threading.Tasks;
using Processors;
/// <summary>
/// Combines two images together by blending the pixels.
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public class Blend : ParallelImageProcessor
public static partial class ImageExtensions
{
/// <summary>
/// The image to blend.
/// </summary>
private readonly ImageBase toBlend;
/// <summary>
/// Initializes a new instance of the <see cref="Blend"/> class.
/// Combines the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">
/// The image to blend with the currently processing image.
/// Disposal of this image is the responsibility of the developer.
/// </param>
/// <param name="alpha">The opacity of the image to blend. Between 0 and 100.</param>
public Blend(ImageBase image, int alpha = 100)
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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);
}
/// <summary>
/// Gets the alpha percentage value.
/// Combines the given image together with the current one by blending their pixels.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
/// <param name="source">The image this method extends.</param>
/// <param name="image">
/// The image to blend with the currently processing image.
/// Disposal of this image is the responsibility of the developer.
/// </param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

52
src/ImageProcessorCore/Filters/BoxBlur.cs

@ -0,0 +1,52 @@
// <copyright file="BoxBlur.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image BoxBlur(this Image source, int radius = 7, ProgressEventHandler progressHandler = null)
{
return BoxBlur(source, radius, source.Bounds, progressHandler);
}
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

75
src/ImageProcessorCore/Filters/Brightness.cs

@ -1,65 +1,52 @@
// <copyright file="Brightness.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore
{
using System;
using System.Numerics;
using System.Threading.Tasks;
using Processors;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the brightness of an <see cref="Image"/>.
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public class Brightness : ParallelImageProcessor
public static partial class ImageExtensions
{
/// <summary>
/// Initializes a new instance of the <see cref="Brightness"/> class.
/// Alters the brightness component of the image.
/// </summary>
/// <param name="brightness">The new brightness of the image. Must be between -100 and 100.</param>
/// <exception cref="ArgumentException">
/// <paramref name="brightness"/> is less than -100 or is greater than 100.
/// </exception>
public Brightness(int brightness)
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new brightness of the image. Must be between -100 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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);
}
/// <summary>
/// Gets the brightness value.
/// Alters the brightness component of the image.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new brightness of the image. Must be between -100 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

88
src/ImageProcessorCore/Filters/ColorBlindness.cs

@ -0,0 +1,88 @@
// <copyright file="ColorBlindness.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies the given colorblindness simulator to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="colorBlindness">The type of color blindness simulator to apply.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, ProgressEventHandler progressHandler = null)
{
return ColorBlindness(source, colorBlindness, source.Bounds, progressHandler);
}
/// <summary>
/// Applies the given colorblindness simulator to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="colorBlindness">The type of color blindness simulator to apply.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

77
src/ImageProcessorCore/Filters/Contrast.cs

@ -1,65 +1,52 @@
// <copyright file="Contrast.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore
{
using System;
using System.Numerics;
using System.Threading.Tasks;
using Processors;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the contrast of an <see cref="Image"/>.
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public class Contrast : ParallelImageProcessor
public static partial class ImageExtensions
{
/// <summary>
/// Initializes a new instance of the <see cref="Contrast"/> class.
/// Alters the contrast component of the image.
/// </summary>
/// <param name="contrast">The new contrast of the image. Must be between -100 and 100.</param>
/// <exception cref="ArgumentException">
/// <paramref name="contrast"/> is less than -100 or is greater than 100.
/// </exception>
public Contrast(int contrast)
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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);
}
/// <summary>
/// Gets the contrast value.
/// Alters the contrast component of the image.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

63
src/ImageProcessorCore/Filters/DetectEdges.cs

@ -0,0 +1,63 @@
// <copyright file="DetectEdges.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Detects any edges within the image. Uses the <see cref="SobelProcessor"/> filter
/// operating in greyscale mode.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image DetectEdges(this Image source, ProgressEventHandler progressHandler = null)
{
return DetectEdges(source, source.Bounds, new SobelProcessor { Greyscale = true }, progressHandler);
}
/// <summary>
/// Detects any edges within the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="filter">The filter for detecting edges.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image DetectEdges(this Image source, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null)
{
return DetectEdges(source, source.Bounds, filter, progressHandler);
}
/// <summary>
/// Detects any edges within the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="filter">The filter for detecting edges.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

55
src/ImageProcessorCore/Filters/Greyscale.cs

@ -0,0 +1,55 @@
// <copyright file="Greyscale.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies greyscale toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Greyscale(this Image source, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null)
{
return Greyscale(source, source.Bounds, mode, progressHandler);
}
/// <summary>
/// Applies greyscale toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

52
src/ImageProcessorCore/Filters/GuassianBlur.cs

@ -0,0 +1,52 @@
// <copyright file="GuassianBlur.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies a Guassian blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image GuassianBlur(this Image source, float sigma = 3f, ProgressEventHandler progressHandler = null)
{
return GuassianBlur(source, sigma, source.Bounds, progressHandler);
}
/// <summary>
/// Applies a Guassian blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

52
src/ImageProcessorCore/Filters/GuassianSharpen.cs

@ -0,0 +1,52 @@
// <copyright file="GuassianSharpen.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies a Guassian sharpening filter to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image GuassianSharpen(this Image source, float sigma = 3f, ProgressEventHandler progressHandler = null)
{
return GuassianSharpen(source, sigma, source.Bounds, progressHandler);
}
/// <summary>
/// Applies a Guassian sharpening filter to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

52
src/ImageProcessorCore/Filters/Hue.cs

@ -0,0 +1,52 @@
// <copyright file="Hue.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Alters the hue component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="degrees">The angle in degrees to adjust the image.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Hue(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{
return Hue(source, degrees, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the hue component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="degrees">The angle in degrees to adjust the image.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

784
src/ImageProcessorCore/Filters/ImageFilterExtensions.cs

@ -1,784 +0,0 @@
// <copyright file="ImageFilterExtensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
{
/// <summary>
/// Extensions methods for <see cref="Image"/> to apply filters to the image.
/// </summary>
public static class ImageFilterExtensions
{
/// <summary>
/// Alters the alpha component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Alpha(this Image source, int percent, ProgressEventHandler progressHandler = null)
{
return Alpha(source, percent, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the alpha component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Combines the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color to set as the background.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Combines the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">
/// The image to blend with the currently processing image.
/// Disposal of this image is the responsibility of the developer.
/// </param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Blend(this Image source, ImageBase image, int percent = 50, ProgressEventHandler progressHandler = null)
{
return Blend(source, image, percent, source.Bounds, progressHandler);
}
/// <summary>
/// Combines the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">
/// The image to blend with the currently processing image.
/// Disposal of this image is the responsibility of the developer.
/// </param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Applies black and white toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image BlackWhite(this Image source, ProgressEventHandler progressHandler = null)
{
return BlackWhite(source, source.Bounds, progressHandler);
}
/// <summary>
/// Applies black and white toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image BoxBlur(this Image source, int radius = 7, ProgressEventHandler progressHandler = null)
{
return BoxBlur(source, radius, source.Bounds, progressHandler);
}
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Alters the brightness component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new brightness of the image. Must be between -100 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Brightness(this Image source, int amount, ProgressEventHandler progressHandler = null)
{
return Brightness(source, amount, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the brightness component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new brightness of the image. Must be between -100 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Applies the given colorblindness simulator to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="colorBlindness">The type of color blindness simulator to apply.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, ProgressEventHandler progressHandler = null)
{
return ColorBlindness(source, colorBlindness, source.Bounds, progressHandler);
}
/// <summary>
/// Applies the given colorblindness simulator to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="colorBlindness">The type of color blindness simulator to apply.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Alters the contrast component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Contrast(this Image source, int amount, ProgressEventHandler progressHandler = null)
{
return Contrast(source, amount, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the contrast component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Detects any edges within the image. Uses the <see cref="Sobel"/> filter
/// operating in greyscale mode.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image DetectEdges(this Image source, ProgressEventHandler progressHandler = null)
{
return DetectEdges(source, source.Bounds, new Sobel { Greyscale = true }, progressHandler);
}
/// <summary>
/// Detects any edges within the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="filter">The filter for detecting edges.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image DetectEdges(this Image source, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null)
{
return DetectEdges(source, source.Bounds, filter, progressHandler);
}
/// <summary>
/// Detects any edges within the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="filter">The filter for detecting edges.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Applies greyscale toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Greyscale(this Image source, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null)
{
return Greyscale(source, source.Bounds, mode, progressHandler);
}
/// <summary>
/// Applies greyscale toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Applies a Guassian blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image GuassianBlur(this Image source, float sigma = 3f, ProgressEventHandler progressHandler = null)
{
return GuassianBlur(source, sigma, source.Bounds, progressHandler);
}
/// <summary>
/// Applies a Guassian blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Applies a Guassian sharpening filter to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image GuassianSharpen(this Image source, float sigma = 3f, ProgressEventHandler progressHandler = null)
{
return GuassianSharpen(source, sigma, source.Bounds, progressHandler);
}
/// <summary>
/// Applies a Guassian sharpening filter to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Alters the hue component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="degrees">The angle in degrees to adjust the image.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Hue(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{
return Hue(source, degrees, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the hue component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="degrees">The angle in degrees to adjust the image.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Inverts the colors of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Invert(this Image source, ProgressEventHandler progressHandler = null)
{
return Invert(source, source.Bounds, progressHandler);
}
/// <summary>
/// Inverts the colors of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Kodachrome(this Image source, ProgressEventHandler progressHandler = null)
{
return Kodachrome(source, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Lomograph(this Image source, ProgressEventHandler progressHandler = null)
{
return Lomograph(source, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Polaroid(this Image source, ProgressEventHandler progressHandler = null)
{
return Polaroid(source, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Pixelates and image with the given pixel size.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="size">The size of the pixels.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Pixelate(this Image source, int size = 4, ProgressEventHandler progressHandler = null)
{
return Pixelate(source, size, source.Bounds, progressHandler);
}
/// <summary>
/// Pixelates and image with the given pixel size.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="size">The size of the pixels.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Alters the saturation component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new saturation of the image. Must be between -100 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Saturation(this Image source, int amount, ProgressEventHandler progressHandler = null)
{
return Saturation(source, amount, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the saturation component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new saturation of the image. Must be between -100 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
/// <summary>
/// Applies sepia toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Sepia(this Image source, ProgressEventHandler progressHandler = null)
{
return Sepia(source, source.Bounds, progressHandler);
}
/// <summary>
/// Applies sepia toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

65
src/ImageProcessorCore/Filters/Invert.cs

@ -1,43 +1,50 @@
// <copyright file="Invert.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore
{
using System.Numerics;
using System.Threading.Tasks;
using Processors;
/// <summary>
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>.
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public class Invert : ParallelImageProcessor
public static partial class ImageExtensions
{
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
/// <summary>
/// Inverts the colors of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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);
}
/// <summary>
/// Inverts the colors of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

50
src/ImageProcessorCore/Filters/Kodachrome.cs

@ -0,0 +1,50 @@
// <copyright file="Kodachrome.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Kodachrome(this Image source, ProgressEventHandler progressHandler = null)
{
return Kodachrome(source, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

50
src/ImageProcessorCore/Filters/Lomograph.cs

@ -0,0 +1,50 @@
// <copyright file="Lomograph.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Lomograph(this Image source, ProgressEventHandler progressHandler = null)
{
return Lomograph(source, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

2
src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/ColorBlindness.cs → src/ImageProcessorCore/Filters/Options/ColorBlindness.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore
{
/// <summary>
/// Enumerates the various types of color blindness.

107
src/ImageProcessorCore/Filters/Pixelate.cs

@ -1,93 +1,52 @@
// <copyright file="Invert.cs" company="James Jackson-South">
// <copyright file="Pixelate.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Processors;
/// <summary>
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>.
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public class Pixelate : ParallelImageProcessor
public static partial class ImageExtensions
{
/// <summary>
/// Initializes a new instance of the <see cref="Pixelate"/> class.
/// Pixelates and image with the given pixel size.
/// </summary>
/// <param name="size">The size of the pixels. Must be greater than 0.</param>
/// <exception cref="ArgumentException">
/// <paramref name="size"/> is less than 0 or equal to 0.
/// </exception>
public Pixelate(int size)
/// <param name="source">The image this method extends.</param>
/// <param name="size">The size of the pixels.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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);
}
/// <inheritdoc/>
public override int Parallelism { get; set; } = 1;
/// <summary>
/// Gets or the pixel size.
/// Pixelates and image with the given pixel size.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
/// <param name="source">The image this method extends.</param>
/// <param name="size">The size of the pixels.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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<int> 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;
}
}
}
}

50
src/ImageProcessorCore/Filters/Polaroid.cs

@ -0,0 +1,50 @@
// <copyright file="Polaroid.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Polaroid(this Image source, ProgressEventHandler progressHandler = null)
{
return Polaroid(source, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

69
src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs

@ -0,0 +1,69 @@
// <copyright file="AlphaProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the Alpha of an <see cref="Image"/>.
/// </summary>
public class AlphaProcessor : ParallelImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="AlphaProcessor"/> class.
/// </summary>
/// <param name="percent">The percentage to adjust the opacity of the image. Must be between 0 and 100.</param>
/// <exception cref="ArgumentException">
/// <paramref name="percent"/> is less than 0 or is greater than 100.
/// </exception>
public AlphaProcessor(int percent)
{
Guard.MustBeBetweenOrEqualTo(percent, 0, 100, nameof(percent));
this.Value = percent;
}
/// <summary>
/// Gets the alpha value.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
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();
}
});
}
}
}
}

78
src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs

@ -0,0 +1,78 @@
// <copyright file="BackgroundColorProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System;
using System.Threading.Tasks;
/// <summary>
/// Sets the background color of the image.
/// </summary>
public class BackgroundColorProcessor : ParallelImageProcessor
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001f;
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundColorProcessor"/> class.
/// </summary>
/// <param name="color">The <see cref="Color"/> to set the background color to.</param>
public BackgroundColorProcessor(Color color)
{
this.Value = Color.FromNonPremultiplied(color);
}
/// <summary>
/// Gets the background color value.
/// </summary>
public Color Value { get; }
/// <inheritdoc/>
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();
}
});
}
}
}
}

22
src/ImageProcessorCore/Filters/Binarization/Threshold.cs → src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs

@ -1,9 +1,9 @@
// <copyright file="Threshold.cs" company="James Jackson-South">
// <copyright file="ThresholdProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System;
using System.Threading.Tasks;
@ -13,16 +13,16 @@ namespace ImageProcessorCore.Filters
/// <see cref="Image"/>. The image will be converted to greyscale before thresholding
/// occurs.
/// </summary>
public class Threshold : ParallelImageProcessor
public class ThresholdProcessor : ParallelImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="Threshold"/> class.
/// Initializes a new instance of the <see cref="ThresholdProcessor"/> class.
/// </summary>
/// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
/// <exception cref="ArgumentException">
/// <paramref name="threshold"/> is less than 0 or is greater than 1.
/// </exception>
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
/// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
new GreyscaleBt709().Apply(source, source, sourceRectangle);
new GreyscaleBt709Processor().Apply(source, source, sourceRectangle);
}
/// <inheritdoc/>
@ -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();
}
});
}
}
}
}

86
src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs

@ -0,0 +1,86 @@
// <copyright file="BlendProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System.Threading.Tasks;
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
public class BlendProcessor : ParallelImageProcessor
{
/// <summary>
/// The image to blend.
/// </summary>
private readonly ImageBase blend;
/// <summary>
/// Initializes a new instance of the <see cref="BlendProcessor"/> class.
/// </summary>
/// <param name="image">
/// The image to blend with the currently processing image.
/// Disposal of this image is the responsibility of the developer.
/// </param>
/// <param name="alpha">The opacity of the image to blend. Between 0 and 100.</param>
public BlendProcessor(ImageBase image, int alpha = 100)
{
Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha));
this.blend = image;
this.Value = alpha;
}
/// <summary>
/// Gets the alpha percentage value.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
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();
}
});
}
}
}
}

69
src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs

@ -0,0 +1,69 @@
// <copyright file="BrightnessProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the brightness of an <see cref="Image"/>.
/// </summary>
public class BrightnessProcessor : ParallelImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="BrightnessProcessor"/> class.
/// </summary>
/// <param name="brightness">The new brightness of the image. Must be between -100 and 100.</param>
/// <exception cref="ArgumentException">
/// <paramref name="brightness"/> is less than -100 or is greater than 100.
/// </exception>
public BrightnessProcessor(int brightness)
{
Guard.MustBeBetweenOrEqualTo(brightness, -100, 100, nameof(brightness));
this.Value = brightness;
}
/// <summary>
/// Gets the brightness value.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
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();
}
});
}
}
}
}

6
src/ImageProcessorCore/Filters/ColorMatrix/BlackWhite.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="BlackWhite.cs" company="James Jackson-South">
// <copyright file="BlackWhiteProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image to their black and white equivalent.
/// </summary>
public class BlackWhite : ColorMatrixFilter
public class BlackWhiteProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

6
src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Achromatomaly.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Achromatomaly.cs" company="James Jackson-South">
// <copyright file="AchromatomalyProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
/// </summary>
public class Achromatomaly : ColorMatrixFilter
public class AchromatomalyProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

6
src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Achromatopsia.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Achromatopsia.cs" company="James Jackson-South">
// <copyright file="AchromatopsiaProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
/// </summary>
public class Achromatopsia : ColorMatrixFilter
public class AchromatopsiaProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

6
src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Deuteranomaly.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Deuteranomaly.cs" company="James Jackson-South">
// <copyright file="DeuteranomalyProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness.
/// </summary>
public class Deuteranomaly : ColorMatrixFilter
public class DeuteranomalyProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

6
src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Deuteranopia.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Deuteranopia.cs" company="James Jackson-South">
// <copyright file="DeuteranopiaProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness.
/// </summary>
public class Deuteranopia : ColorMatrixFilter
public class DeuteranopiaProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

6
src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Protanomaly.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Protanomaly.cs" company="James Jackson-South">
// <copyright file="ProtanomalyProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness.
/// </summary>
public class Protanomaly : ColorMatrixFilter
public class ProtanomalyProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

6
src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Protanopia.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Protanopia.cs" company="James Jackson-South">
// <copyright file="ProtanopiaProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness.
/// </summary>
public class Protanopia : ColorMatrixFilter
public class ProtanopiaProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

0
src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/README.md → src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/README.md

6
src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Tritanomaly.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Tritanomaly.cs" company="James Jackson-South">
// <copyright file="TritanomalyProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness.
/// </summary>
public class Tritanomaly : ColorMatrixFilter
public class TritanomalyProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

6
src/ImageProcessorCore/Filters/ColorMatrix/ColorBlindness/Tritanopia.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Tritanopia.cs" company="James Jackson-South">
// <copyright file="TritanopiaProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness.
/// </summary>
public class Tritanopia : ColorMatrixFilter
public class TritanopiaProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

11
src/ImageProcessorCore/Filters/ColorMatrix/ColorMatrixFilter.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
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();
}
});
}
}
/// <summary>

6
src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleBt601.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt601Processor.cs

@ -1,9 +1,9 @@
// <copyright file="GreyscaleBt601.cs" company="James Jackson-South">
// <copyright file="GreyscaleBt601Processor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
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 <see href="https://en.wikipedia.org/wiki/Luma_%28video%29#Rec._601_luma_versus_Rec._709_luma_coefficients"/>.
/// </summary>
public class GreyscaleBt601 : ColorMatrixFilter
public class GreyscaleBt601Processor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

6
src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleBt709.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt709Processor.cs

@ -1,9 +1,9 @@
// <copyright file="GreyscaleBt709.cs" company="James Jackson-South">
// <copyright file="GreyscaleBt709Processor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
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 <see href="https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients"/>.
/// </summary>
public class GreyscaleBt709 : ColorMatrixFilter
public class GreyscaleBt709Processor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

2
src/ImageProcessorCore/Filters/ColorMatrix/GreyscaleMode.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleMode.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// Provides enumeration over the various greyscale methods available.

13
src/ImageProcessorCore/Filters/ColorMatrix/Hue.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs

@ -1,9 +1,14 @@
namespace ImageProcessorCore.Filters
// <copyright file="HueProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
public class Hue : ColorMatrixFilter
public class HueProcessor : ColorMatrixFilter
{
/// <summary>
/// The <see cref="Matrix4x4"/> used to alter the image.
@ -11,10 +16,10 @@
private Matrix4x4 matrix;
/// <summary>
/// Initializes a new instance of the <see cref="Hue"/> class.
/// Initializes a new instance of the <see cref="HueProcessor"/> class.
/// </summary>
/// <param name="angle">The new brightness of the image. Must be between -100 and 100.</param>
public Hue(float angle)
public HueProcessor(float angle)
{
// Wrap the angle round at 360.
angle = angle % 360;

5
src/ImageProcessorCore/Filters/ColorMatrix/IColorMatrixFilter.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
@ -19,7 +19,8 @@ namespace ImageProcessorCore.Filters
Matrix4x4 Matrix { get; }
/// <summary>
/// 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.
/// </summary>
bool Compand { get; }
}

6
src/ImageProcessorCore/Filters/ColorMatrix/Kodachrome.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/KodachromeProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Kodachrome.cs" company="James Jackson-South">
// <copyright file="KodachromeProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating an old Kodachrome camera effect.
/// </summary>
public class Kodachrome : ColorMatrixFilter
public class KodachromeProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

8
src/ImageProcessorCore/Filters/ColorMatrix/Lomograph.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Lomograph.cs" company="James Jackson-South">
// <copyright file="LomographProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating an old Lomograph effect.
/// </summary>
public class Lomograph : ColorMatrixFilter
public class LomographProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()
@ -26,7 +26,7 @@ namespace ImageProcessorCore.Filters
/// <inheritdoc/>
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);
}
}
}

10
src/ImageProcessorCore/Filters/ColorMatrix/Polaroid.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="Polaroid.cs" company="James Jackson-South">
// <copyright file="PolaroidProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Converts the colors of the image recreating an old Polaroid effect.
/// </summary>
public class Polaroid : ColorMatrixFilter
public class PolaroidProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()
@ -32,8 +32,8 @@ namespace ImageProcessorCore.Filters
/// <inheritdoc/>
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,

10
src/ImageProcessorCore/Filters/ColorMatrix/Saturation.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs

@ -1,9 +1,9 @@
// <copyright file="Saturation.cs" company="James Jackson-South">
// <copyright file="SaturationProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
@ -11,7 +11,7 @@ namespace ImageProcessorCore.Filters
/// <summary>
/// An <see cref="IImageProcessor"/> to change the saturation of an <see cref="Image"/>.
/// </summary>
public class Saturation : ColorMatrixFilter
public class SaturationProcessor : ColorMatrixFilter
{
/// <summary>
/// The saturation to be applied to the image.
@ -24,13 +24,13 @@ namespace ImageProcessorCore.Filters
private Matrix4x4 matrix;
/// <summary>
/// Initializes a new instance of the <see cref="Saturation"/> class.
/// Initializes a new instance of the <see cref="SaturationProcessor"/> class.
/// </summary>
/// <param name="saturation">The new saturation of the image. Must be between -100 and 100.</param>
/// <exception cref="ArgumentException">
/// <paramref name="saturation"/> is less than -100 or is greater than 100.
/// </exception>
public Saturation(int saturation)
public SaturationProcessor(int saturation)
{
Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation));
this.saturation = saturation;

6
src/ImageProcessorCore/Filters/ColorMatrix/Sepia.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs

@ -1,9 +1,9 @@
// <copyright file="Sepia.cs" company="James Jackson-South">
// <copyright file="SepiaProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
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. <see href="http://www.w3.org/TR/filter-effects/#sepiaEquivalent"/>
/// </summary>
public class Sepia : ColorMatrixFilter
public class SepiaProcessor : ColorMatrixFilter
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()

70
src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs

@ -0,0 +1,70 @@
// <copyright file="ContrastProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the contrast of an <see cref="Image"/>.
/// </summary>
public class ContrastProcessor : ParallelImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="ContrastProcessor"/> class.
/// </summary>
/// <param name="contrast">The new contrast of the image. Must be between -100 and 100.</param>
/// <exception cref="ArgumentException">
/// <paramref name="contrast"/> is less than -100 or is greater than 100.
/// </exception>
public ContrastProcessor(int contrast)
{
Guard.MustBeBetweenOrEqualTo(contrast, -100, 100, nameof(contrast));
this.Value = contrast;
}
/// <summary>
/// Gets the contrast value.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
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();
}
});
}
}
}
}

10
src/ImageProcessorCore/Filters/Convolution/BoxBlur.cs → src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs

@ -1,14 +1,14 @@
// <copyright file="BoxBlur.cs" company="James Jackson-South">
// <copyright file="BoxBlurProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// Applies a Box blur filter to the image.
/// </summary>
public class BoxBlur : Convolution2PassFilter
public class BoxBlurProcessor : Convolution2PassFilter
{
/// <summary>
/// The maximum size of the kernal in either direction.
@ -26,12 +26,12 @@ namespace ImageProcessorCore.Filters
private float[,] kernelX;
/// <summary>
/// Initializes a new instance of the <see cref="GuassianBlur"/> class.
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class.
/// </summary>
/// <param name="radius">
/// The 'radius' value representing the size of the area to sample.
/// </param>
public BoxBlur(int radius = 7)
public BoxBlurProcessor(int radius = 7)
{
this.kernelSize = (radius * 2) + 1;
}

18
src/ImageProcessorCore/Filters/Convolution/Convolution2DFilter.cs → src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
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();
}
});
}
}
}
}

12
src/ImageProcessorCore/Filters/Convolution/Convolution2PassFilter.cs → src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
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();
}
});
}
}
}
}

12
src/ImageProcessorCore/Filters/Convolution/ConvolutionFilter.cs → src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
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();
}
});
}
}
}
}

4
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/EdgeDetector2DFilter.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// 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);
}
}
}

4
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/EdgeDetectorFilter.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// 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);
}
}
}

2
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/IEdgeDetectorFilter.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// Provides properties and methods allowing the detection of edges within an image.

6
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Kayyali.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs

@ -1,15 +1,15 @@
// <copyright file="Kayyali.cs" company="James Jackson-South">
// <copyright file="KayyaliProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// The Kayyali operator filter.
/// <see href="http://edgedetection.webs.com/"/>
/// </summary>
public class Kayyali : EdgeDetector2DFilter
public class KayyaliProcessor : EdgeDetector2DFilter
{
/// <inheritdoc/>
public override float[,] KernelX => new float[,]

6
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Kirsch.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs

@ -1,15 +1,15 @@
// <copyright file="Kirsch.cs" company="James Jackson-South">
// <copyright file="KirschProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// The Kirsch operator filter.
/// <see href="http://en.wikipedia.org/wiki/Kirsch_operator"/>
/// </summary>
public class Kirsch : EdgeDetector2DFilter
public class KirschProcessor : EdgeDetector2DFilter
{
/// <inheritdoc/>
public override float[,] KernelX => new float[,]

6
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Laplacian3X3.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs

@ -1,15 +1,15 @@
// <copyright file="Laplacian3X3.cs" company="James Jackson-South">
// <copyright file="Laplacian3X3Processor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// The Laplacian 3 x 3 operator filter.
/// <see href="http://en.wikipedia.org/wiki/Discrete_Laplace_operator"/>
/// </summary>
public class Laplacian3X3 : EdgeDetectorFilter
public class Laplacian3X3Processor : EdgeDetectorFilter
{
/// <inheritdoc/>
public override float[,] KernelXY => new float[,]

6
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Laplacian5X5.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs

@ -1,15 +1,15 @@
// <copyright file="Laplacian5X5.cs" company="James Jackson-South">
// <copyright file="Laplacian5X5Processor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// The Laplacian 5 x 5 operator filter.
/// <see href="http://en.wikipedia.org/wiki/Discrete_Laplace_operator"/>
/// </summary>
public class Laplacian5X5 : EdgeDetectorFilter
public class Laplacian5X5Processor : EdgeDetectorFilter
{
/// <inheritdoc/>
public override float[,] KernelXY => new float[,]

6
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/LaplacianOfGaussian.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs

@ -1,15 +1,15 @@
// <copyright file="LaplacianOfGaussian.cs" company="James Jackson-South">
// <copyright file="LaplacianOfGaussianProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// The Laplacian of Gaussian operator filter.
/// <see href="http://fourier.eng.hmc.edu/e161/lectures/gradient/node8.html"/>
/// </summary>
public class LaplacianOfGaussian : EdgeDetectorFilter
public class LaplacianOfGaussianProcessor : EdgeDetectorFilter
{
/// <inheritdoc/>
public override float[,] KernelXY => new float[,]

6
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Prewitt.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs

@ -1,15 +1,15 @@
// <copyright file="Prewitt.cs" company="James Jackson-South">
// <copyright file="PrewittProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// The Prewitt operator filter.
/// <see href="http://en.wikipedia.org/wiki/Prewitt_operator"/>
/// </summary>
public class Prewitt : EdgeDetector2DFilter
public class PrewittProcessor : EdgeDetector2DFilter
{
/// <inheritdoc/>
public override float[,] KernelX => new float[,]

6
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/RobertsCross.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs

@ -1,15 +1,15 @@
// <copyright file="RobertsCross.cs" company="James Jackson-South">
// <copyright file="RobertsCrossProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// The Roberts Cross operator filter.
/// <see href="http://en.wikipedia.org/wiki/Roberts_cross"/>
/// </summary>
public class RobertsCross : EdgeDetector2DFilter
public class RobertsCrossProcessor : EdgeDetector2DFilter
{
/// <inheritdoc/>
public override float[,] KernelX => new float[,]

6
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Scharr.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs

@ -1,15 +1,15 @@
// <copyright file="Scharr.cs" company="James Jackson-South">
// <copyright file="ScharrProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// The Scharr operator filter.
/// <see href="http://en.wikipedia.org/wiki/Sobel_operator#Alternative_operators"/>
/// </summary>
public class Scharr : EdgeDetector2DFilter
public class ScharrProcessor : EdgeDetector2DFilter
{
/// <inheritdoc/>
public override float[,] KernelX => new float[,]

6
src/ImageProcessorCore/Filters/Convolution/EdgeDetection/Sobel.cs → src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs

@ -1,15 +1,15 @@
// <copyright file="Sobel.cs" company="James Jackson-South">
// <copyright file="SobelProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
/// <summary>
/// The Sobel operator filter.
/// <see href="http://en.wikipedia.org/wiki/Sobel_operator"/>
/// </summary>
public class Sobel : EdgeDetector2DFilter
public class SobelProcessor : EdgeDetector2DFilter
{
/// <inheritdoc/>
public override float[,] KernelX => new float[,]

18
src/ImageProcessorCore/Filters/Convolution/GuassianBlur.cs → src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="GuassianBlur.cs" company="James Jackson-South">
// <copyright file="GuassianBlurProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System;
/// <summary>
/// Applies a Gaussian blur filter to the image.
/// </summary>
public class GuassianBlur : Convolution2PassFilter
public class GuassianBlurProcessor : Convolution2PassFilter
{
/// <summary>
/// The maximum size of the kernal in either direction.
@ -33,29 +33,29 @@ namespace ImageProcessorCore.Filters
private float[,] kernelX;
/// <summary>
/// Initializes a new instance of the <see cref="GuassianBlur"/> class.
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class.
/// </summary>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
public GuassianBlur(float sigma = 3f)
public GuassianBlurProcessor(float sigma = 3f)
{
this.kernelSize = ((int)Math.Ceiling(sigma) * 2) + 1;
this.sigma = sigma;
}
/// <summary>
/// Initializes a new instance of the <see cref="GuassianBlur"/> class.
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class.
/// </summary>
/// <param name="radius">
/// The 'radius' value representing the size of the area to sample.
/// </param>
public GuassianBlur(int radius)
public GuassianBlurProcessor(int radius)
{
this.kernelSize = (radius * 2) + 1;
this.sigma = radius;
}
/// <summary>
/// Initializes a new instance of the <see cref="GuassianBlur"/> class.
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class.
/// </summary>
/// <param name="sigma">
/// 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.
/// </param>
public GuassianBlur(float sigma, int radius)
public GuassianBlurProcessor(float sigma, int radius)
{
this.kernelSize = (radius * 2) + 1;
this.sigma = sigma;

18
src/ImageProcessorCore/Filters/Convolution/GuassianSharpen.cs → src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs

@ -1,16 +1,16 @@
// <copyright file="GuassianSharpen.cs" company="James Jackson-South">
// <copyright file="GuassianSharpenProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System;
/// <summary>
/// Applies a Gaussian sharpening filter to the image.
/// </summary>
public class GuassianSharpen : Convolution2PassFilter
public class GuassianSharpenProcessor : Convolution2PassFilter
{
/// <summary>
/// The maximum size of the kernal in either direction.
@ -33,31 +33,31 @@ namespace ImageProcessorCore.Filters
private float[,] kernelX;
/// <summary>
/// Initializes a new instance of the <see cref="GuassianSharpen"/> class.
/// Initializes a new instance of the <see cref="GuassianSharpenProcessor"/> class.
/// </summary>
/// <param name="sigma">
/// The 'sigma' value representing the weight of the sharpening.
/// </param>
public GuassianSharpen(float sigma = 3f)
public GuassianSharpenProcessor(float sigma = 3f)
{
this.kernelSize = ((int)Math.Ceiling(sigma) * 2) + 1;
this.sigma = sigma;
}
/// <summary>
/// Initializes a new instance of the <see cref="GuassianSharpen"/> class.
/// Initializes a new instance of the <see cref="GuassianSharpenProcessor"/> class.
/// </summary>
/// <param name="radius">
/// The 'radius' value representing the size of the area to sample.
/// </param>
public GuassianSharpen(int radius)
public GuassianSharpenProcessor(int radius)
{
this.kernelSize = (radius * 2) + 1;
this.sigma = radius;
}
/// <summary>
/// Initializes a new instance of the <see cref="GuassianSharpen"/> class.
/// Initializes a new instance of the <see cref="GuassianSharpenProcessor"/> class.
/// </summary>
/// <param name="sigma">
/// 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.
/// </param>
public GuassianSharpen(float sigma, int radius)
public GuassianSharpenProcessor(float sigma, int radius)
{
this.kernelSize = (radius * 2) + 1;
this.sigma = sigma;

37
src/ImageProcessorCore/Filters/Glow.cs → src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs

@ -1,9 +1,9 @@
// <copyright file="Glow.cs" company="James Jackson-South">
// <copyright file="GlowProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
@ -12,10 +12,10 @@ namespace ImageProcessorCore.Filters
/// <summary>
/// Creates a glow effect on the image
/// </summary>
public class Glow : ParallelImageProcessor
public class GlowProcessor : ParallelImageProcessor
{
/// <summary>
/// Gets or sets the vignette color to apply.
/// Gets or sets the glow color to apply.
/// </summary>
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();
});
}
}
}
}

48
src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs

@ -0,0 +1,48 @@
// <copyright file="InvertProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>.
/// </summary>
public class InvertProcessor : ParallelImageProcessor
{
/// <inheritdoc/>
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();
}
});
}
}
}
}

97
src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs

@ -0,0 +1,97 @@
// <copyright file="PixelateProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>.
/// </summary>
public class PixelateProcessor : ParallelImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="PixelateProcessor"/> class.
/// </summary>
/// <param name="size">The size of the pixels. Must be greater than 0.</param>
/// <exception cref="ArgumentException">
/// <paramref name="size"/> is less than 0 or equal to 0.
/// </exception>
public PixelateProcessor(int size)
{
Guard.MustBeGreaterThan(size, 0, nameof(size));
this.Value = size;
}
/// <inheritdoc/>
public override int Parallelism { get; set; } = 1;
/// <summary>
/// Gets or the pixel size.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
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<int> 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();
}
});
}
}
}
}

34
src/ImageProcessorCore/Filters/Vignette.cs → src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs

@ -1,9 +1,9 @@
// <copyright file="Vignette.cs" company="James Jackson-South">
// <copyright file="VignetteProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Filters
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
@ -12,7 +12,7 @@ namespace ImageProcessorCore.Filters
/// <summary>
/// Creates a vignette effect on the image
/// </summary>
public class Vignette : ParallelImageProcessor
public class VignetteProcessor : ParallelImageProcessor
{
/// <summary>
/// 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();
});
}
}
}
}

52
src/ImageProcessorCore/Filters/Saturation.cs

@ -0,0 +1,52 @@
// <copyright file="Saturation.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Alters the saturation component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new saturation of the image. Must be between -100 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Saturation(this Image source, int amount, ProgressEventHandler progressHandler = null)
{
return Saturation(source, amount, source.Bounds, progressHandler);
}
/// <summary>
/// Alters the saturation component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new saturation of the image. Must be between -100 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

50
src/ImageProcessorCore/Filters/Sepia.cs

@ -0,0 +1,50 @@
// <copyright file="Sepia.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies sepia toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Sepia(this Image source, ProgressEventHandler progressHandler = null)
{
return Sepia(source, source.Bounds, progressHandler);
}
/// <summary>
/// Applies sepia toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
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;
}
}
}
}

37
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.
/// </summary>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="image">The <see cref="ImageBase"/> containing pixel data.</param>
/// <param name="pixels">The <see cref="PixelAccessor"/> containing pixel data.</param>
/// <param name="amount">The amount to pad each row by.</param>
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.
/// </summary>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="image">The <see cref="ImageBase"/> containing pixel data.</param>
/// <param name="pixels">The <see cref="PixelAccessor"/> containing pixel data.</param>
/// <param name="amount">The amount to pad each row by.</param>
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 });

2
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);
}
}
}

43
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;

21
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.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="image">The image base.</param>
private void WriteDataChunks(Stream stream, ImageBase image)
/// <param name="pixels">The image pixels.</param>
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;

103
src/ImageProcessorCore/IImage.cs

@ -1,103 +0,0 @@
// <copyright file="IImage.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageProcessorCore.Formats;
/// <summary>
/// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes.
/// </summary>
public interface IImage : IImageBase
{
/// <summary>
/// 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.
/// </summary>
/// <value>The density of the image in x- direction.</value>
double HorizontalResolution { get; set; }
/// <summary>
/// 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.
/// </summary>
/// <value>The density of the image in y- direction.</value>
double VerticalResolution { get; set; }
/// <summary>
/// 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.
/// </summary>
/// <value>The width of the image in inches.</value>
double InchWidth { get; }
/// <summary>
/// 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.
/// </summary>
/// <value>The height of the image in inches.</value>
double InchHeight { get; }
/// <summary>
/// Gets a value indicating whether this image is animated.
/// </summary>
/// <value>
/// <c>True</c> if this image is animated; otherwise, <c>false</c>.
/// </value>
bool IsAnimated { get; }
/// <summary>
/// Gets or sets the number of times any animation is repeated.
/// <remarks>0 means to repeat indefinitely.</remarks>
/// </summary>
ushort RepeatCount { get; set; }
/// <summary>
/// Gets the currently loaded image format.
/// </summary>
IImageFormat CurrentImageFormat { get; }
/// <summary>
/// Gets the other frames for the animation.
/// </summary>
/// <value>The list of frame images.</value>
IList<ImageFrame> Frames { get; }
/// <summary>
/// Gets the list of properties for storing meta information about this image.
/// </summary>
/// <value>A list of image properties.</value>
IList<ImageProperty> Properties { get; }
/// <summary>
/// Saves the image to the given stream using the currently loaded image format.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
void Save(Stream stream);
/// <summary>
/// Saves the image to the given stream using the given image format.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="format">The format to save the image as.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
void Save(Stream stream, IImageFormat format);
/// <summary>
/// Saves the image to the given stream using the given image encoder.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
void Save(Stream stream, IImageEncoder encoder);
}
}

106
src/ImageProcessorCore/IImageBase.cs

@ -1,106 +0,0 @@
// <copyright file="IImageBase.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
{
using System;
using System.Runtime.CompilerServices;
/// <summary>
/// Encapsulates the basic properties and methods required to manipulate images.
/// </summary>
public interface IImageBase
{
/// <summary>
/// Gets the image pixels as byte array.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
float[] Pixels { get; }
/// <summary>
/// Gets the width in pixels.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height in pixels.
/// </summary>
int Height { get; }
/// <summary>
/// Gets the pixel ratio made up of the width and height.
/// </summary>
double PixelRatio { get; }
/// <summary>
/// Gets the <see cref="Rectangle"/> representing the bounds of the image.
/// </summary>
Rectangle Bounds { get; }
/// <summary>
/// Gets or sets th quality of the image. This affects the output quality of lossy image formats.
/// </summary>
int Quality { get; set; }
/// <summary>
/// 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.
/// </summary>
int FrameDelay { get; set; }
/// <summary>
/// Gets or sets the color of a pixel at the specified position.
/// </summary>
/// <param name="x">
/// The x-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <param name="y">
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <returns>The <see cref="Color"/> at the specified position.</returns>
Color this[int x, int y] { get; set; }
/// <summary>
/// Sets the pixel array of the image to the given value.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">
/// The array with colors. Must be a multiple of four times the width and height.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height * 4.
/// </exception>
void SetPixels(int width, int height, float[] pixels);
/// <summary>
/// Sets the pixel array of the image to the given value, creating a copy of
/// the original pixels.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">
/// The array with colors. Must be a multiple of four times the width and height.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height * 4.
/// </exception>
void ClonePixels(int width, int height, float[] pixels);
}
}

4
src/ImageProcessorCore/IImageProcessor.cs

@ -1,9 +1,9 @@
// <copyright file="IImageProcessorCore.cs" company="James Jackson-South">
// <copyright file="IImageProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
namespace ImageProcessorCore.Processors
{
/// <summary>
/// A delegate which is called as progress is made processing an image.

97
src/ImageProcessorCore/Image.cs

@ -19,10 +19,10 @@ namespace ImageProcessorCore
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
[DebuggerDisplay("Image: {Width}x{Height}")]
public class Image : ImageBase, IImage
public class Image : ImageBase
{
/// <summary>
/// The default horizontal resolution value (dots per inch) in x direction.
@ -97,13 +97,26 @@ namespace ImageProcessorCore
/// </summary>
public IReadOnlyCollection<IImageFormat> Formats { get; } = Bootstrapper.Instance.ImageFormats;
/// <inheritdoc/>
/// <summary>
/// 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.
/// </summary>
/// <value>The density of the image in x- direction.</value>
public double HorizontalResolution { get; set; } = DefaultHorizontalResolution;
/// <inheritdoc/>
/// <summary>
/// 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.
/// </summary>
/// <value>The density of the image in y- direction.</value>
public double VerticalResolution { get; set; } = DefaultVerticalResolution;
/// <inheritdoc/>
/// <summary>
/// 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.
/// </summary>
/// <value>The width of the image in inches.</value>
public double InchWidth
{
get
@ -119,7 +132,12 @@ namespace ImageProcessorCore
}
}
/// <inheritdoc/>
/// <summary>
/// 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.
/// </summary>
/// <value>The height of the image in inches.</value>
public double InchHeight
{
get
@ -135,36 +153,66 @@ namespace ImageProcessorCore
}
}
/// <inheritdoc/>
/// <summary>
/// Gets a value indicating whether this image is animated.
/// </summary>
/// <value>
/// <c>True</c> if this image is animated; otherwise, <c>false</c>.
/// </value>
public bool IsAnimated => this.Frames.Count > 0;
/// <inheritdoc/>
/// <summary>
/// Gets or sets the number of times any animation is repeated.
/// <remarks>0 means to repeat indefinitely.</remarks>
/// </summary>
public ushort RepeatCount { get; set; }
/// <inheritdoc/>
/// <summary>
/// Gets the other frames for the animation.
/// </summary>
/// <value>The list of frame images.</value>
public IList<ImageFrame> Frames { get; } = new List<ImageFrame>();
/// <inheritdoc/>
/// <summary>
/// Gets the list of properties for storing meta information about this image.
/// </summary>
/// <value>A list of image properties.</value>
public IList<ImageProperty> Properties { get; } = new List<ImageProperty>();
/// <inheritdoc/>
/// <summary>
/// Gets the currently loaded image format.
/// </summary>
public IImageFormat CurrentImageFormat { get; internal set; }
/// <inheritdoc/>
/// <summary>
/// Saves the image to the given stream using the currently loaded image format.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public void Save(Stream stream)
{
Guard.NotNull(stream, nameof(stream));
this.CurrentImageFormat.Encoder.Encode(this, stream);
}
/// <inheritdoc/>
/// <summary>
/// Saves the image to the given stream using the given image format.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="format">The format to save the image as.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public void Save(Stream stream, IImageFormat format)
{
Guard.NotNull(stream, nameof(stream));
format.Encoder.Encode(this, stream);
}
/// <inheritdoc/>
/// <summary>
/// Saves the image to the given stream using the given image encoder.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public void Save(Stream stream, IImageEncoder encoder)
{
Guard.NotNull(stream, nameof(stream));
@ -186,27 +234,6 @@ namespace ImageProcessorCore
}
}
/// <inheritdoc/>
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);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>

207
src/ImageProcessorCore/ImageBase.cs

@ -6,43 +6,18 @@
namespace ImageProcessorCore
{
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// The base class of all images. Encapsulates the basic properties and methods
/// required to manipulate images.
/// </summary>
public abstract unsafe class ImageBase : IImageBase, IDisposable
public abstract class ImageBase
{
/// <summary>
/// The position of the first pixel in the bitmap.
/// </summary>
private float* pixelsBase;
/// <summary>
/// The array of pixels.
/// </summary>
private float[] pixelsArray;
/// <summary>
/// Provides a way to access the pixels from unmanaged memory.
/// </summary>
private GCHandle pixelsHandle;
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
/// </summary>
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
/// <remarks>
/// 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.
/// </remarks>
internal bool IsDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase"/> class.
/// </summary>
@ -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();
}
/// <summary>
@ -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);
}
/// <inheritdoc/>
~ImageBase()
{
this.Dispose(false);
}
/// <summary>
/// Gets or sets the maximum allowable width in pixels.
/// </summary>
@ -113,67 +78,65 @@ namespace ImageProcessorCore
/// </summary>
public static int MaxHeight { get; set; } = int.MaxValue;
/// <inheritdoc/>
/// <summary>
/// Gets the image pixels as byte array.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public float[] Pixels => this.pixelsArray;
/// <inheritdoc/>
/// <summary>
/// Gets the width in pixels.
/// </summary>
public int Width { get; private set; }
/// <inheritdoc/>
/// <summary>
/// Gets the height in pixels.
/// </summary>
public int Height { get; private set; }
/// <inheritdoc/>
/// <summary>
/// Gets the pixel ratio made up of the width and height.
/// </summary>
public double PixelRatio => (double)this.Width / this.Height;
/// <inheritdoc/>
/// <summary>
/// Gets the <see cref="Rectangle"/> representing the bounds of the image.
/// </summary>
public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height);
/// <inheritdoc/>
/// <summary>
/// Gets or sets th quality of the image. This affects the output quality of lossy image formats.
/// </summary>
public int Quality { get; set; }
/// <inheritdoc/>
/// <summary>
/// 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.
/// </summary>
public int FrameDelay { get; set; }
/// <inheritdoc/>
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;
}
}
/// <inheritdoc/>
/// <summary>
/// Sets the pixel array of the image to the given value.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">
/// The array with colors. Must be a multiple of four times the width and height.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height * 4.
/// </exception>
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();
}
/// <inheritdoc/>
/// <summary>
/// Sets the pixel array of the image to the given value, creating a copy of
/// the original pixels.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">
/// The array with colors. Must be a multiple of four times the width and height.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height * 4.
/// </exception>
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);
}
/// <inheritdoc/>
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);
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <param name="disposing">If true, the object gets disposed.</param>
protected virtual void Dispose(bool disposing)
/// <returns>The <see cref="PixelAccessor"/></returns>
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);
}
}
}

9
src/ImageProcessorCore/ImageExtensions.cs

@ -9,6 +9,7 @@ namespace ImageProcessorCore
using System.IO;
using Formats;
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
@ -128,11 +129,6 @@ namespace ImageProcessorCore
/// <exception cref="ObjectDisposedException">Thrown if the <paramref name="source"/> has been disposed.</exception>
private static Image PerformAction(Image source, bool clone, Action<ImageBase, ImageBase> 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;
}

2
src/ImageProcessorCore/ParallelImageProcessor.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
namespace ImageProcessorCore.Processors
{
using System;
using System.Threading;

164
src/ImageProcessorCore/PixelAccessor.cs

@ -0,0 +1,164 @@
// <copyright file="PixelAccessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
{
using System;
using System.Runtime.InteropServices;
/// <summary>
/// Provides per-pixel access to an images pixels.
/// </summary>
public sealed unsafe class PixelAccessor : IDisposable
{
/// <summary>
/// The position of the first pixel in the bitmap.
/// </summary>
private float* pixelsBase;
/// <summary>
/// Provides a way to access the pixels from unmanaged memory.
/// </summary>
private GCHandle pixelsHandle;
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
/// </summary>
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
/// <remarks>
/// 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.
/// </remarks>
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor"/> class.
/// </summary>
/// <param name="image">
/// The image to provide pixel access for.
/// </param>
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;
}
}
}
/// <summary>
/// Finalizes an instance of the <see cref="PixelAccessor"/> class.
/// </summary>
~PixelAccessor()
{
this.Dispose();
}
/// <summary>
/// Gets the width of the image.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the height of the image.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets or sets the color of a pixel at the specified position.
/// </summary>
/// <param name="x">
/// The x-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <param name="y">
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <returns>The <see cref="Color"/> at the specified position.</returns>
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;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
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);
}
}
}

31
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<Bgra32> palette;
// Get the palette
List<Bgra32> 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
/// <param name="source">The source data</param>
/// <param name="width">The width in pixels of the image.</param>
/// <param name="height">The height in pixels of the image.</param>
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
/// <param name="output">The output pixel array</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
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,

36
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);
}
}
/// <summary>
@ -318,7 +321,7 @@ namespace ImageProcessorCore.Quantizers
/// Builds a 3-D color histogram of <c>counts, r/g/b, c^2</c>.
/// </summary>
/// <param name="image">The image.</param>
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
/// <summary>
/// Generates the quantized result.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="imagePixels">The image pixels.</param>
/// <param name="colorCount">The color count.</param>
/// <param name="cube">The cube.</param>
/// <returns>The result.</returns>
private QuantizedImage GenerateResult(ImageBase image, int colorCount, Box[] cube)
private QuantizedImage GenerateResult(PixelAccessor imagePixels, int colorCount, Box[] cube)
{
List<Bgra32> pallette = new List<Bgra32>();
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);
}
}
}

2
src/ImageProcessorCore/Samplers/Crop.cs

@ -5,6 +5,8 @@
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>

2
src/ImageProcessorCore/Samplers/EntropyCrop.cs

@ -5,6 +5,8 @@
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>

2
src/ImageProcessorCore/Samplers/Pad.cs

@ -5,6 +5,8 @@
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>

32
src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
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();
}
});
}
}
}
}

57
src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs

@ -3,13 +3,11 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
namespace ImageProcessorCore.Processors
{
using System;
using System.Threading.Tasks;
using ImageProcessorCore.Filters;
/// <summary>
/// Provides methods to allow the cropping of an image to preserve areas of highest
/// entropy.
@ -42,21 +40,20 @@ namespace ImageProcessorCore
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
@ -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();
});
}
}
/// <inheritdoc/>

2
src/ImageProcessorCore/Samplers/Processors/IImageSampler.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
namespace ImageProcessorCore.Processors
{
/// <summary>
/// Acts as a marker for generic parameters that require an image sampler.

2
src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
namespace ImageProcessorCore.Processors
{
/// <summary>
/// Applies sampling methods to an image.

187
src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
namespace ImageProcessorCore.Processors
{
using System;
using System.Threading.Tasks;
@ -62,7 +62,13 @@ namespace ImageProcessorCore
}
/// <inheritdoc/>
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();
});
}
}
/// <inheritdoc/>
@ -202,9 +224,6 @@ namespace ImageProcessorCore
{
target.ClonePixels(target.Width, target.Height, source.Pixels);
}
// Clean up
this.firstPass?.Dispose();
}
/// <summary>

153
src/ImageProcessorCore/Samplers/Processors/RotateFlip.cs → 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.
// </copyright>
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();
});
}
}
/// <summary>
@ -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();
});
}
}
/// <summary>
@ -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();
});
}
}
}
}

45
src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
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();
});
}
/// <inheritdoc/>
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();
});
}
}
}
}

45
src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
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();
});
}
/// <inheritdoc/>
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();
});
}
}
}
}

2
src/ImageProcessorCore/Samplers/Resize.cs

@ -5,6 +5,8 @@
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>

2
src/ImageProcessorCore/Samplers/Rotate.cs

@ -5,6 +5,8 @@
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save