Browse Source

Merge flava

Former-commit-id: ceabe9e4c8ebde8472f5a3f8b5a6a2f408e853e9
Former-commit-id: 47a2939e665f4af6e888e2b3a667f81281b7e7a0
Former-commit-id: ca4e024abe7dad42078a4da16d4196e435a568b5
pull/1/head
James Jackson-South 10 years ago
parent
commit
2f3b067be6
  1. 2
      ImageProcessorCore.sln
  2. 3
      NuGet.config
  3. 33
      README.md
  4. 67
      src/ImageProcessorCore/Bootstrapper.cs
  5. 483
      src/ImageProcessorCore/Colors/Color.cs
  6. 575
      src/ImageProcessorCore/Colors/ColorDefinitions.cs
  7. 35
      src/ImageProcessorCore/Colors/ColorTransforms.cs
  8. 39
      src/ImageProcessorCore/Colors/ColorspaceTransforms.cs
  9. 5
      src/ImageProcessorCore/Colors/Colorspaces/Bgra32.cs
  10. 11
      src/ImageProcessorCore/Colors/Colorspaces/CieLab.cs
  11. 8
      src/ImageProcessorCore/Colors/Colorspaces/CieXyz.cs
  12. 8
      src/ImageProcessorCore/Colors/Colorspaces/Cmyk.cs
  13. 12
      src/ImageProcessorCore/Colors/Colorspaces/Hsl.cs
  14. 9
      src/ImageProcessorCore/Colors/Colorspaces/Hsv.cs
  15. 0
      src/ImageProcessorCore/Colors/Colorspaces/IAlmostEquatable.cs
  16. 5
      src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs
  17. 63
      src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs
  18. 8
      src/ImageProcessorCore/Colors/RgbaComponent.cs
  19. 91
      src/ImageProcessorCore/Common/Extensions/Vector4Extensions.cs
  20. 46
      src/ImageProcessorCore/Common/Helpers/ImageMaths.cs
  21. 18
      src/ImageProcessorCore/Filters/Alpha.cs
  22. 16
      src/ImageProcessorCore/Filters/BackgroundColor.cs
  23. 60
      src/ImageProcessorCore/Filters/BinaryThreshold.cs
  24. 22
      src/ImageProcessorCore/Filters/BlackWhite.cs
  25. 28
      src/ImageProcessorCore/Filters/Blend.cs
  26. 22
      src/ImageProcessorCore/Filters/BoxBlur.cs
  27. 22
      src/ImageProcessorCore/Filters/Brightness.cs
  28. 38
      src/ImageProcessorCore/Filters/ColorBlindness.cs
  29. 22
      src/ImageProcessorCore/Filters/Contrast.cs
  30. 88
      src/ImageProcessorCore/Filters/DetectEdges.cs
  31. 34
      src/ImageProcessorCore/Filters/Grayscale.cs
  32. 22
      src/ImageProcessorCore/Filters/GuassianBlur.cs
  33. 22
      src/ImageProcessorCore/Filters/GuassianSharpen.cs
  34. 22
      src/ImageProcessorCore/Filters/Hue.cs
  35. 10
      src/ImageProcessorCore/Filters/Invert.cs
  36. 22
      src/ImageProcessorCore/Filters/Kodachrome.cs
  37. 22
      src/ImageProcessorCore/Filters/Lomograph.cs
  38. 2
      src/ImageProcessorCore/Filters/Options/ColorBlindness.cs
  39. 58
      src/ImageProcessorCore/Filters/Options/EdgeDetection.cs
  40. 8
      src/ImageProcessorCore/Filters/Options/GrayscaleMode.cs
  41. 26
      src/ImageProcessorCore/Filters/Pixelate.cs
  42. 22
      src/ImageProcessorCore/Filters/Polaroid.cs
  43. 25
      src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs
  44. 34
      src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs
  45. 52
      src/ImageProcessorCore/Filters/Processors/Binarization/BinaryThresholdProcessor.cs
  46. 33
      src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs
  47. 29
      src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs
  48. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs
  49. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs
  50. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs
  51. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs
  52. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs
  53. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs
  54. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs
  55. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs
  56. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs
  57. 34
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs
  58. 10
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/GrayscaleBt601Processor.cs
  59. 8
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/GrayscaleBt709Processor.cs
  60. 37
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs
  61. 10
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs
  62. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/KodachromeProcessor.cs
  63. 12
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs
  64. 19
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs
  65. 19
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs
  66. 6
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs
  67. 28
      src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs
  68. 8
      src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs
  69. 28
      src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs
  70. 40
      src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs
  71. 26
      src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs
  72. 12
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs
  73. 12
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs
  74. 15
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs
  75. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs
  76. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/KirschProcessor.cs
  77. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs
  78. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs
  79. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs
  80. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/PrewittProcessor.cs
  81. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs
  82. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/ScharrProcessor.cs
  83. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/SobelProcessor.cs
  84. 8
      src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs
  85. 8
      src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs
  86. 33
      src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs
  87. 23
      src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs
  88. 21
      src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs
  89. 33
      src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs
  90. 22
      src/ImageProcessorCore/Filters/Saturation.cs
  91. 18
      src/ImageProcessorCore/Filters/Sepia.cs
  92. 13
      src/ImageProcessorCore/Formats/Bmp/BmpDecoder.cs
  93. 127
      src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs
  94. 4
      src/ImageProcessorCore/Formats/Bmp/BmpEncoder.cs
  95. 60
      src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs
  96. 12
      src/ImageProcessorCore/Formats/Gif/GifDecoder.cs
  97. 48
      src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs
  98. 4
      src/ImageProcessorCore/Formats/Gif/GifEncoder.cs
  99. 72
      src/ImageProcessorCore/Formats/Gif/GifEncoderCore.cs
  100. 10
      src/ImageProcessorCore/Formats/IImageDecoder.cs

2
ImageProcessorCore.sln

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14 # Visual Studio 14
VisualStudioVersion = 14.0.25123.0 VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ImageProcessorCore", "src\ImageProcessorCore\ImageProcessorCore.xproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ImageProcessorCore", "src\ImageProcessorCore\ImageProcessorCore.xproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}"
EndProject EndProject

3
NuGet.config

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<packageSources> <packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="benchmarkDotNetDevelop" value="https://ci.appveyor.com/nuget/benchmarkdotnet" />
</packageSources> </packageSources>
</configuration> </configuration>

33
README.md

@ -1,3 +1,4 @@
# ImageProcessorCore # ImageProcessorCore
<img src="build/icons/imageprocessor-logo-512.png" width="128" height="128"/> <img src="build/icons/imageprocessor-logo-512.png" width="128" height="128"/>
@ -74,6 +75,7 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- Resampling algorithms. (Optional gamma correction, resize modes, Performance improvements?) - Resampling algorithms. (Optional gamma correction, resize modes, Performance improvements?)
- [x] Box - [x] Box
- [x] Bicubic - [x] Bicubic
- [x] Lanczos2
- [x] Lanczos3 - [x] Lanczos3
- [x] Lanczos5 - [x] Lanczos5
- [x] Lanczos8 - [x] Lanczos8
@ -81,7 +83,6 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] Nearest Neighbour - [x] Nearest Neighbour
- [x] Robidoux - [x] Robidoux
- [x] Robidoux Sharp - [x] Robidoux Sharp
- [x] Robidoux Soft
- [x] Spline - [x] Spline
- [x] Triangle - [x] Triangle
- [x] Welch - [x] Welch
@ -100,8 +101,8 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] Skew by x/y angles and center point. - [x] Skew by x/y angles and center point.
- ColorMatrix operations (Uses Matrix4x4) - ColorMatrix operations (Uses Matrix4x4)
- [x] BlackWhite - [x] BlackWhite
- [x] Greyscale BT709 - [x] Grayscale BT709
- [x] Greyscale BT601 - [x] Grayscale BT601
- [x] Hue - [x] Hue
- [x] Saturation - [x] Saturation
- [x] Lomograph - [x] Lomograph
@ -160,7 +161,7 @@ With this version the API will change dramatically. Without the constraints of `
Image methods are also fluent which allow chaining much like the `ImageFactory` class in the Framework version. Image methods are also fluent which allow chaining much like the `ImageFactory` class in the Framework version.
Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their greyscale equivalent using the BT709 standard matrix. Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their grayscale equivalent using the BT709 standard matrix.
```csharp ```csharp
using (FileStream stream = File.OpenRead("foo.jpg")) using (FileStream stream = File.OpenRead("foo.jpg"))
@ -168,32 +169,12 @@ using (FileStream output = File.OpenWrite("bar.jpg"))
{ {
Image image = new Image(stream); Image image = new Image(stream);
image.Resize(image.Width / 2, image.Height / 2) image.Resize(image.Width / 2, image.Height / 2)
.Greyscale() .Grayscale()
.Save(output); .Save(output);
} }
``` ```
It will also be possible to pass collections of processors as params to manipulate images. For example here I am applying a Gaussian blur with a sigma of 5 to an image, then detecting the edges using a Sobel operator working in greyscale mode. Individual processors can be initialised and apply processing against images. This allows nesting which brings the potential for powerful combinations of processing methods:
```csharp
using (FileStream stream = File.OpenRead("foo.jpg"))
using (FileStream output = File.OpenWrite("bar.jpg"))
{
Image image = new Image(stream);
List<IImageProcessor> processors = new List<IImageProcessor>()
{
new GuassianBlur(5),
new Sobel { Greyscale = true }
};
foreach (IImageProcessor processor in processors){
image.Process(processor)
.Save(output);
}
}
```
Individual processors can be initialised and apply processing against images. This allows nesting which will allow the powerful combination of processing methods:
```csharp ```csharp
new Brightness(50).Apply(sourceImage, targetImage, sourceImage.Bounds); new Brightness(50).Apply(sourceImage, targetImage, sourceImage.Bounds);

67
src/ImageProcessorCore/Bootstrapper.cs

@ -8,7 +8,10 @@ namespace ImageProcessorCore
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using ImageProcessorCore.Formats; using System.Reflection;
using System.Threading.Tasks;
using Formats;
/// <summary> /// <summary>
/// Provides initialization code which allows extending the library. /// Provides initialization code which allows extending the library.
@ -26,18 +29,25 @@ namespace ImageProcessorCore
/// </summary> /// </summary>
private readonly List<IImageFormat> imageFormats; private readonly List<IImageFormat> imageFormats;
private readonly Dictionary<Type, Func<IImageBase, IPixelAccessor>> pixelAccessors;
/// <summary> /// <summary>
/// Prevents a default instance of the <see cref="Bootstrapper"/> class from being created. /// Prevents a default instance of the <see cref="Bootstrapper"/> class from being created.
/// </summary> /// </summary>
private Bootstrapper() private Bootstrapper()
{ {
this.imageFormats = new List<IImageFormat>(new List<IImageFormat> this.imageFormats = new List<IImageFormat>
{ {
new BmpFormat(), new BmpFormat(),
new JpegFormat(), new JpegFormat(),
new PngFormat(), new PngFormat(),
new GifFormat() new GifFormat()
}); };
this.pixelAccessors = new Dictionary<Type, Func<IImageBase, IPixelAccessor>>
{
{ typeof(Color), i=> new ColorPixelAccessor(i) }
};
} }
/// <summary> /// <summary>
@ -46,9 +56,21 @@ namespace ImageProcessorCore
public static Bootstrapper Instance = Lazy.Value; public static Bootstrapper Instance = Lazy.Value;
/// <summary> /// <summary>
/// Gets the list of supported <see cref="IImageFormat"/> /// Gets the collection of supported <see cref="IImageFormat"/>
/// </summary>
public IReadOnlyCollection<IImageFormat> ImageFormats =>
new ReadOnlyCollection<IImageFormat>(this.imageFormats);
/// <summary>
/// Gets the collection of supported pixel accessors
/// </summary>
public IReadOnlyDictionary<Type, Func<IImageBase, IPixelAccessor>> PixelAccessors =>
new ReadOnlyDictionary<Type, Func<IImageBase, IPixelAccessor>>(this.pixelAccessors);
/// <summary>
/// Gets or sets the global parallel options for processing tasks in parallel.
/// </summary> /// </summary>
public IReadOnlyCollection<IImageFormat> ImageFormats => new ReadOnlyCollection<IImageFormat>(this.imageFormats); public ParallelOptions ParallelOptions { get; set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
/// <summary> /// <summary>
/// Adds a new <see cref="IImageFormat"/> to the collection of supported image formats. /// Adds a new <see cref="IImageFormat"/> to the collection of supported image formats.
@ -58,5 +80,40 @@ namespace ImageProcessorCore
{ {
this.imageFormats.Add(format); this.imageFormats.Add(format);
} }
/// <summary>
/// Adds a pixel accessor for the given pixel format.
/// </summary>
/// <param name="packedType">The packed format type, must implement <see cref="IPackedVector"/></param>
/// <param name="initializer">The function to return a new instance of the pixel accessor.</param>
public void AddPixelAccessor(Type packedType, Func<IImageBase, IPixelAccessor> initializer)
{
if (!typeof(IPackedVector).GetTypeInfo().IsAssignableFrom(packedType.GetTypeInfo()))
{
throw new ArgumentException($"Type {packedType} must implement {nameof(IPackedVector)}");
}
this.pixelAccessors.Add(packedType, initializer);
}
/// <summary>
/// Gets an instance of the correct <see cref="IPixelAccessor"/> for the packed vector.
/// </summary>
/// <typeparam name="T">The type of pixel data.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The image</param>
/// <returns>The <see cref="IPixelAccessor"/></returns>
public IPixelAccessor<T, TP> GetPixelAccessor<T, TP>(IImageBase image)
where T : IPackedVector<TP>
where TP : struct
{
Type packed = typeof(T);
if (this.pixelAccessors.ContainsKey(packed))
{
return (IPixelAccessor<T, TP>)this.pixelAccessors[packed].Invoke(image);
}
throw new NotSupportedException($"PixelAccessor cannot be loaded for {packed}:");
}
} }
} }

483
src/ImageProcessorCore/Colors/Color.cs

@ -6,46 +6,65 @@
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using System; using System;
using System.ComponentModel;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary> /// <summary>
/// Represents a four-component color using red, green, blue, and alpha data. /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// Each component is stored in premultiplied format multiplied by the alpha component. /// The color components are stored in red, green, blue, and alpha order.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations. /// as it avoids the need to create new values for modification operations.
/// </remarks> /// </remarks>
public partial struct Color : IEquatable<Color>, IAlmostEquatable<Color, float> [StructLayout(LayoutKind.Explicit)]
public partial struct Color : IPackedVector<uint>, IEquatable<Color>
{ {
/// <summary> /// <summary>
/// Represents an empty <see cref="Color"/> that has R, G, B, and A values set to zero. /// Gets or sets the blue component.
/// </summary> /// </summary>
public static readonly Color Empty = default(Color); [FieldOffset(0)]
public byte R;
/// <summary> /// <summary>
/// The epsilon for comparing floating point numbers. /// Gets or sets the green component.
/// </summary> /// </summary>
private const float Epsilon = 0.001f; [FieldOffset(1)]
public byte G;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// Gets or sets the red component.
/// </summary> /// </summary>
private Vector4 backingVector; [FieldOffset(2)]
public byte B;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct. /// Gets or sets the alpha component.
/// </summary> /// </summary>
/// <param name="r">The red component of this <see cref="Color"/>.</param> [FieldOffset(3)]
/// <param name="g">The green component of this <see cref="Color"/>.</param> public byte A;
/// <param name="b">The blue component of this <see cref="Color"/>.</param>
/// <param name="a">The alpha component of this <see cref="Color"/>.</param> /// <summary>
public Color(float r, float g, float b, float a = 1) /// The packed value.
/// </summary>
[FieldOffset(0)]
private readonly uint packedValue;
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
public Color(byte r, byte g, byte b, byte a = 255)
: this() : this()
{ {
this.backingVector = new Vector4(r, g, b, a); this.R = r;
this.G = g;
this.B = b;
this.A = a;
} }
/// <summary> /// <summary>
@ -68,25 +87,17 @@ namespace ImageProcessorCore
if (hex.Length == 8) if (hex.Length == 8)
{ {
float r = Convert.ToByte(hex.Substring(2, 2), 16); this.R = Convert.ToByte(hex.Substring(2, 2), 16);
float g = Convert.ToByte(hex.Substring(4, 2), 16); this.G = Convert.ToByte(hex.Substring(4, 2), 16);
float b = Convert.ToByte(hex.Substring(6, 2), 16); this.B = Convert.ToByte(hex.Substring(6, 2), 16);
float a = Convert.ToByte(hex.Substring(0, 2), 16); this.A = Convert.ToByte(hex.Substring(0, 2), 16);
// Do division of Vector4 instead of each component to utilize SIMD optimizations
this.backingVector = new Vector4(r, g, b, a) / 255f;
this.backingVector = FromNonPremultiplied(this.backingVector, this.A);
} }
else if (hex.Length == 6) else if (hex.Length == 6)
{ {
float r = Convert.ToByte(hex.Substring(0, 2), 16); this.R = Convert.ToByte(hex.Substring(0, 2), 16);
float g = Convert.ToByte(hex.Substring(2, 2), 16); this.G = Convert.ToByte(hex.Substring(2, 2), 16);
float b = Convert.ToByte(hex.Substring(4, 2), 16); this.B = Convert.ToByte(hex.Substring(4, 2), 16);
float a = 255f; this.A = 255;
// Do division of Vector4 instead of each component to utilize SIMD optimizations
this.backingVector = new Vector4(r, g, b, a) / 255f;
} }
else else
{ {
@ -94,185 +105,60 @@ namespace ImageProcessorCore
string gh = char.ToString(hex[1]); string gh = char.ToString(hex[1]);
string bh = char.ToString(hex[2]); string bh = char.ToString(hex[2]);
float r = Convert.ToByte(rh + rh, 16); this.R = Convert.ToByte(rh + rh, 16);
float g = Convert.ToByte(gh + gh, 16); this.G = Convert.ToByte(gh + gh, 16);
float b = Convert.ToByte(bh + bh, 16); this.B = Convert.ToByte(bh + bh, 16);
float a = 255f; this.A = 255;
this.backingVector = new Vector4(r, g, b, a) / 255f;
} }
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct. /// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary> /// </summary>
/// <param name="vector">The vector.</param> /// <param name="r">The red component.</param>
public Color(Vector4 vector) /// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
public Color(float r, float g, float b, float a = 1)
: this()
{ {
this.backingVector = vector; Vector4 clamped = Vector4.Clamp(new Vector4(r, g, b, a), Vector4.Zero, Vector4.One) * 255F;
this.R = (byte)Math.Round(clamped.X);
this.G = (byte)Math.Round(clamped.Y);
this.B = (byte)Math.Round(clamped.Z);
this.A = (byte)Math.Round(clamped.W);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct. /// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary> /// </summary>
/// <param name="vector"> /// <param name="vector">
/// The vector representing the red, green, and blue componenets. /// The vector containing the components for the packed vector.
/// </param> /// </param>
public Color(Vector3 vector) public Color(Vector3 vector)
: this()
{ {
this.backingVector = new Vector4(vector, 1); Vector3 clamped = Vector3.Clamp(vector, Vector3.Zero, Vector3.One) * 255F;
this.R = (byte)Math.Round(clamped.X);
this.G = (byte)Math.Round(clamped.Y);
this.B = (byte)Math.Round(clamped.Z);
this.A = 255;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct. /// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary> /// </summary>
/// <param name="vector"> /// <param name="vector">
/// The vector representing the red, green, and blue componenets. /// The vector containing the components for the packed vector.
/// </param> /// </param>
/// <param name="alpha">The alpha component.</param> public Color(Vector4 vector)
public Color(Vector3 vector, float alpha) : this()
{
this.backingVector = new Vector4(vector, alpha);
}
/// <summary>
/// Gets or sets the red component of the color.
/// </summary>
public float R
{
get
{
return this.backingVector.X;
}
set
{
this.backingVector.X = value;
}
}
/// <summary>
/// Gets or sets the green component of the color.
/// </summary>
public float G
{
get
{
return this.backingVector.Y;
}
set
{
this.backingVector.Y = value;
}
}
/// <summary>
/// Gets or sets the blue component of the color.
/// </summary>
public float B
{
get
{
return this.backingVector.Z;
}
set
{
this.backingVector.Z = value;
}
}
/// <summary>
/// Gets or sets the alpha component of the color.
/// </summary>
public float A
{
get
{
return this.backingVector.W;
}
set
{
this.backingVector.W = value;
}
}
/// <summary>
/// Gets a value indicating whether this <see cref="Color"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Gets this color with the component values clamped from 0 to 1.
/// </summary>
public Color Limited => new Color(Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One));
/// <summary>
/// Computes the product of multiplying a color by a given factor.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="factor">The multiplication factor.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color operator *(Color color, float factor)
{
return new Color(color.backingVector * factor);
}
/// <summary>
/// Computes the product of multiplying a color by a given factor.
/// </summary>
/// <param name="factor">The multiplication factor.</param>
/// <param name="color">The color.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color operator *(float factor, Color color)
{
return new Color(color.backingVector * factor);
}
/// <summary>
/// Computes the product of multiplying two colors.
/// </summary>
/// <param name="left">The color on the left hand of the operand.</param>
/// <param name="right">The color on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color operator *(Color left, Color right)
{
return new Color(left.backingVector * right.backingVector);
}
/// <summary>
/// Computes the sum of adding two colors.
/// </summary>
/// <param name="left">The color on the left hand of the operand.</param>
/// <param name="right">The color on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color operator +(Color left, Color right)
{
return new Color(left.backingVector + right.backingVector);
}
/// <summary>
/// Computes the difference left by subtracting one color from another.
/// </summary>
/// <param name="left">The color on the left hand of the operand.</param>
/// <param name="right">The color on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color operator -(Color left, Color right)
{ {
return new Color(left.backingVector - right.backingVector); Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255F;
this.R = (byte)Math.Round(clamped.X);
this.G = (byte)Math.Round(clamped.Y);
this.B = (byte)Math.Round(clamped.Z);
this.A = (byte)Math.Round(clamped.W);
} }
/// <summary> /// <summary>
@ -289,230 +175,99 @@ namespace ImageProcessorCore
/// </returns> /// </returns>
public static bool operator ==(Color left, Color right) public static bool operator ==(Color left, Color right)
{ {
return left.Equals(right); return left.packedValue == right.packedValue;
} }
/// <summary> /// <summary>
/// Compares two <see cref="Color"/> objects for inequality. /// Compares two <see cref="Color"/> objects for equality.
/// </summary> /// </summary>
/// <param name="left"> /// <param name="left">The <see cref="Color"/> on the left side of the operand.</param>
/// The <see cref="Color"/> on the left side of the operand. /// <param name="right">The <see cref="Color"/> on the right side of the operand.</param>
/// </param>
/// <param name="right">
/// The <see cref="Color"/> on the right side of the operand.
/// </param>
/// <returns> /// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
public static bool operator !=(Color left, Color right) public static bool operator !=(Color left, Color right)
{ {
return !left.Equals(right); return left.packedValue != right.packedValue;
}
/// <summary>
/// Returns a new color whose components are the average of the components of first and second.
/// </summary>
/// <param name="first">The first color.</param>
/// <param name="second">The second color.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Color Average(Color first, Color second)
{
return new Color((first.backingVector + second.backingVector) * .5f);
}
/// <summary>
/// Compresses a linear color signal to its sRGB equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="linear">The <see cref="Color"/> whose signal to compress.</param>
/// <returns>The <see cref="Color"/>.</returns>
public static Color Compress(Color linear)
{
// TODO: Is there a faster way to do this?
float r = Compress(linear.R);
float g = Compress(linear.G);
float b = Compress(linear.B);
return new Color(r, g, b, linear.A);
}
/// <summary>
/// Expands an sRGB color signal to its linear equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="gamma">The <see cref="Color"/> whose signal to expand.</param>
/// <returns>The <see cref="Color"/>.</returns>
public static Color Expand(Color gamma)
{
// TODO: Is there a faster way to do this?
float r = Expand(gamma.R);
float g = Expand(gamma.G);
float b = Expand(gamma.B);
return new Color(r, g, b, gamma.A);
}
/// <summary>
/// Converts a non-premultipled alpha <see cref="Color"/> to a <see cref="Color"/>
/// that contains premultiplied alpha.
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert.</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Color FromNonPremultiplied(Color color)
{
return new Color(FromNonPremultiplied(color.backingVector, color.A));
}
/// <summary>
/// Converts a non-premultiplied alpha Vector4 to a Vector4 that contains premultiplied alpha.
/// </summary>
/// <param name="vector">The vector to convert.</param>
/// <param name="alpha">The alpha to use in conversion.</param>
/// <returns>The Vector4 with premultiplied alpha.</returns>
private static Vector4 FromNonPremultiplied(Vector4 vector, float alpha)
{
return vector * new Vector4(alpha, alpha, alpha, 1);
} }
/// <summary> /// <inheritdoc/>
/// Converts a premultipled alpha <see cref="Color"/> to a <see cref="Color"/> public uint PackedValue()
/// that contains non-premultiplied alpha.
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert.</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Color ToNonPremultiplied(Color color)
{ {
float a = color.A; return this.packedValue;
if (Math.Abs(a) < Epsilon)
{
return new Color(color.backingVector);
}
return new Color(color.backingVector / new Vector4(a, a, a, 1));
} }
/// <summary> /// <inheritdoc/>
/// Gets a <see cref="Vector4"/> representation for this <see cref="Color"/>. public void PackVector(Vector4 vector)
/// </summary>
/// <returns>A <see cref="Vector4"/> representation for this object.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{ {
return new Vector4(this.R, this.G, this.B, this.A); Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255F;
this.R = (byte)Math.Round(clamped.X);
this.G = (byte)Math.Round(clamped.Y);
this.B = (byte)Math.Round(clamped.Z);
this.A = (byte)Math.Round(clamped.W);
} }
/// <summary> /// <inheritdoc/>
/// Gets a <see cref="Vector3"/> representation for this <see cref="Color"/>. public void PackBytes(byte x, byte y, byte z, byte w)
/// </summary>
/// <returns>A <see cref="Vector3"/> representation for this object.</returns>
public Vector3 ToVector3()
{ {
return new Vector3(this.R, this.G, this.B); this.R = x;
this.G = y;
this.B = z;
this.A = w;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public Vector4 ToVector4()
{ {
return GetHashCode(this); return new Vector4(this.R, this.G, this.B, this.A) / 255F;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public byte[] ToBytes()
{ {
if (this.IsEmpty) return new[] { this.R, this.G, this.B, this.A };
{
return "Color [ Empty ]";
}
return $"Color [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##}, A={this.A:#0.##} ]";
} }
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is Color) return (obj is Color) && this.Equals((Color)obj);
{
return this.Equals((Color)obj);
}
return false;
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(Color other) public bool Equals(Color other)
{ {
return this.AlmostEquals(other, Epsilon); return this.packedValue == other.packedValue;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Color other, float precision)
{
Vector4 result = Vector4.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& result.Y < precision
&& result.Z < precision
&& result.W < precision;
} }
/// <summary> /// <summary>
/// Gets the compressed sRGB value from an linear signal. /// Gets a string representation of the packed vector.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary> /// </summary>
/// <param name="signal">The signal value to compress.</param> /// <returns>A string representation of the packed vector.</returns>
/// <returns> public override string ToString()
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Compress(float signal)
{ {
if (signal <= 0.0031308f) return this.ToVector4().ToString();
{
return signal * 12.92f;
}
return (1.055f * (float)Math.Pow(signal, 0.41666666f)) - 0.055f;
} }
/// <summary> /// <inheritdoc/>
/// Gets the expanded linear value from an sRGB signal. public override int GetHashCode()
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to expand.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Expand(float signal)
{ {
if (signal <= 0.04045f) return this.GetHashCode(this);
{
return signal / 12.92f;
}
return (float)Math.Pow((signal + 0.055f) / 1.055f, 2.4f);
} }
/// <summary> /// <summary>
/// Returns the hash code for this instance. /// Returns the hash code for this instance.
/// </summary> /// </summary>
/// <param name="color"> /// <param name="packed">
/// The instance of <see cref="Color"/> to return the hash code for. /// The instance of <see cref="Color"/> to return the hash code for.
/// </param> /// </param>
/// <returns> /// <returns>
/// A 32-bit signed integer that is the hash code for this instance. /// A 32-bit signed integer that is the hash code for this instance.
/// </returns> /// </returns>
private static int GetHashCode(Color color) => color.backingVector.GetHashCode(); [MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetHashCode(Color packed)
{
return packed.packedValue.GetHashCode();
}
} }
} }

575
src/ImageProcessorCore/Colors/ColorDefinitions.cs

File diff suppressed because it is too large

35
src/ImageProcessorCore/Colors/ColorTransforms.cs

@ -6,10 +6,11 @@
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using System; using System;
using System.Numerics;
/// <summary> /// <summary>
/// Represents a four-component color using red, green, blue, and alpha data. /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// Each component is stored in premultiplied format multiplied by the alpha component. /// The color components are stored in red, green, blue, and alpha order.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
@ -43,31 +44,31 @@ namespace ImageProcessorCore
return source; return source;
} }
return new Color(source.backingVector * destination.backingVector); // TODO: This will use less memory than using Vector4
// but we should test speed vs memory to see which is best balance.
byte r = (byte)(source.R * destination.R).Clamp(0, 255);
byte g = (byte)(source.G * destination.G).Clamp(0, 255);
byte b = (byte)(source.B * destination.B).Clamp(0, 255);
byte a = (byte)(source.A * destination.A).Clamp(0, 255);
return new Color(r, g, b, a);
} }
/// <summary> /// <summary>
/// Linearly interpolates from one color to another based on the given amount. /// Linearly interpolates from one color to another based on the given weighting.
/// </summary> /// </summary>
/// <param name="source">The first color value.</param> /// <param name="from">The first color value.</param>
/// <param name="destination">The second color value.</param> /// <param name="to">The second color value.</param>
/// <param name="amount"> /// <param name="amount">
/// The weight value. At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
/// <returns> /// <returns>
/// The <see cref="Color"/> /// The <see cref="Color"/>
/// </returns> /// </returns>
public static Color Lerp(Color source, Color destination, float amount) public static Color Lerp(Color from, Color to, float amount)
{ {
amount = amount.Clamp(0f, 1f); return new Color(Vector4.Lerp(from.ToVector4(), to.ToVector4(), amount));
if (Math.Abs(source.A - 1) < Epsilon && Math.Abs(destination.A - 1) < Epsilon)
{
return source + ((destination - source) * amount);
}
// Premultiplied.
return (source * (1 - amount)) + destination;
} }
} }
} }

39
src/ImageProcessorCore/Colors/ColorspaceTransforms.cs

@ -6,10 +6,11 @@
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using System; using System;
using System.Numerics;
/// <summary> /// <summary>
/// Represents a four-component color using red, green, blue, and alpha data. /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// Each component is stored in premultiplied format multiplied by the alpha component. /// The color components are stored in red, green, blue, and alpha order.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
@ -17,6 +18,11 @@ namespace ImageProcessorCore
/// </remarks> /// </remarks>
public partial struct Color public partial struct Color
{ {
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001F;
/// <summary> /// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a /// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="Bgra32"/>. /// <see cref="Bgra32"/>.
@ -27,7 +33,7 @@ namespace ImageProcessorCore
/// </returns> /// </returns>
public static implicit operator Color(Bgra32 color) public static implicit operator Color(Bgra32 color)
{ {
return new Color(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f); return new Color(color.R, color.G, color.B, color.A);
} }
/// <summary> /// <summary>
@ -43,7 +49,7 @@ namespace ImageProcessorCore
float r = (1 - cmykColor.C) * (1 - cmykColor.K); float r = (1 - cmykColor.C) * (1 - cmykColor.K);
float g = (1 - cmykColor.M) * (1 - cmykColor.K); float g = (1 - cmykColor.M) * (1 - cmykColor.K);
float b = (1 - cmykColor.Y) * (1 - cmykColor.K); float b = (1 - cmykColor.Y) * (1 - cmykColor.K);
return new Color(r, g, b); return new Color(r, g, b, 1);
} }
/// <summary> /// <summary>
@ -60,11 +66,11 @@ namespace ImageProcessorCore
float cb = color.Cb - 128; float cb = color.Cb - 128;
float cr = color.Cr - 128; float cr = color.Cr - 128;
float r = (float)(y + (1.402 * cr)).Clamp(0, 255) / 255f; byte r = (byte)(y + (1.402 * cr)).Clamp(0, 255);
float g = (float)(y - (0.34414 * cb) - (0.71414 * cr)).Clamp(0, 255) / 255f; byte g = (byte)(y - (0.34414 * cb) - (0.71414 * cr)).Clamp(0, 255);
float b = (float)(y + (1.772 * cb)).Clamp(0, 255) / 255f; byte b = (byte)(y + (1.772 * cb)).Clamp(0, 255);
return new Color(r, g, b); return new Color(r, g, b, 255);
} }
/// <summary> /// <summary>
@ -86,7 +92,8 @@ namespace ImageProcessorCore
float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F); float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F);
float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F); float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F);
return Color.Compress(new Color(r, g, b)); Vector4 vector = new Vector4(r, g, b, 1).Compress();
return new Color(vector);
} }
/// <summary> /// <summary>
@ -111,9 +118,9 @@ namespace ImageProcessorCore
int i = (int)Math.Truncate(h); int i = (int)Math.Truncate(h);
float f = h - i; float f = h - i;
float p = v * (1.0f - s); float p = v * (1.0F - s);
float q = v * (1.0f - (s * f)); float q = v * (1.0F - (s * f));
float t = v * (1.0f - (s * (1.0f - f))); float t = v * (1.0F - (s * (1.0F - f)));
float r, g, b; float r, g, b;
switch (i) switch (i)
@ -155,7 +162,7 @@ namespace ImageProcessorCore
break; break;
} }
return new Color(r, g, b); return new Color(r, g, b, 1);
} }
/// <summary> /// <summary>
@ -168,7 +175,7 @@ namespace ImageProcessorCore
/// </returns> /// </returns>
public static implicit operator Color(Hsl color) public static implicit operator Color(Hsl color)
{ {
float rangedH = color.H / 360f; float rangedH = color.H / 360F;
float r = 0; float r = 0;
float g = 0; float g = 0;
float b = 0; float b = 0;
@ -192,7 +199,7 @@ namespace ImageProcessorCore
} }
} }
return new Color(r, g, b); return new Color(r, g, b, 1);
} }
/// <summary> /// <summary>
@ -226,7 +233,7 @@ namespace ImageProcessorCore
float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F); float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F);
float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F); float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F);
return Color.Compress(new Color(r, g, b)); return new Color(new Vector4(r, g, b, 1F).Compress());
} }
/// <summary> /// <summary>

5
src/ImageProcessorCore/Colors/Colorspaces/Bgra32.cs

@ -80,8 +80,7 @@ namespace ImageProcessorCore
/// </returns> /// </returns>
public static implicit operator Bgra32(Color color) public static implicit operator Bgra32(Color color)
{ {
color = color.Limited * 255f; return new Bgra32(color.B, color.G, color.R, color.A);
return new Bgra32((byte)color.B, (byte)color.G, (byte)color.R, (byte)color.A);
} }
/// <summary> /// <summary>
@ -158,7 +157,7 @@ namespace ImageProcessorCore
/// Returns the hash code for this instance. /// Returns the hash code for this instance.
/// </summary> /// </summary>
/// <param name="color"> /// <param name="color">
/// The instance of <see cref="Cmyk"/> to return the hash code for. /// The instance of <see cref="Bgra32"/> to return the hash code for.
/// </param> /// </param>
/// <returns> /// <returns>
/// A 32-bit signed integer that is the hash code for this instance. /// A 32-bit signed integer that is the hash code for this instance.

11
src/ImageProcessorCore/Colors/Colorspaces/CieLab.cs

@ -71,7 +71,7 @@ namespace ImageProcessorCore
/// <see cref="CieLab"/>. /// <see cref="CieLab"/>.
/// </summary> /// </summary>
/// <param name="color"> /// <param name="color">
/// The instance of <see cref="Bgra32"/> to convert. /// The instance of <see cref="Color"/> to convert.
/// </param> /// </param>
/// <returns> /// <returns>
/// An instance of <see cref="CieLab"/>. /// An instance of <see cref="CieLab"/>.
@ -79,11 +79,10 @@ namespace ImageProcessorCore
public static implicit operator CieLab(Color color) public static implicit operator CieLab(Color color)
{ {
// First convert to CIE XYZ // First convert to CIE XYZ
color = Color.Expand(color); Vector4 vector = color.ToVector4().Expand();
float x = (vector.X * 0.4124F) + (vector.Y * 0.3576F) + (vector.Z * 0.1805F);
float x = (color.R * 0.4124F) + (color.G * 0.3576F) + (color.B * 0.1805F); float y = (vector.X * 0.2126F) + (vector.Y * 0.7152F) + (vector.Z * 0.0722F);
float y = (color.R * 0.2126F) + (color.G * 0.7152F) + (color.B * 0.0722F); float z = (vector.X * 0.0193F) + (vector.Y * 0.1192F) + (vector.Z * 0.9505F);
float z = (color.R * 0.0193F) + (color.G * 0.1192F) + (color.B * 0.9505F);
// Now to LAB // Now to LAB
x /= 0.95047F; x /= 0.95047F;

8
src/ImageProcessorCore/Colors/Colorspaces/CieXyz.cs

@ -79,11 +79,11 @@ namespace ImageProcessorCore
/// </returns> /// </returns>
public static implicit operator CieXyz(Color color) public static implicit operator CieXyz(Color color)
{ {
color = Color.Expand(color); Vector4 vector = color.ToVector4().Expand();
float x = (color.R * 0.4124F) + (color.G * 0.3576F) + (color.B * 0.1805F); float x = (vector.X * 0.4124F) + (vector.Y * 0.3576F) + (vector.Z * 0.1805F);
float y = (color.R * 0.2126F) + (color.G * 0.7152F) + (color.B * 0.0722F); float y = (vector.X * 0.2126F) + (vector.Y * 0.7152F) + (vector.Z * 0.0722F);
float z = (color.R * 0.0193F) + (color.G * 0.1192F) + (color.B * 0.9505F); float z = (vector.X * 0.0193F) + (vector.Y * 0.1192F) + (vector.Z * 0.9505F);
x *= 100F; x *= 100F;
y *= 100F; y *= 100F;

8
src/ImageProcessorCore/Colors/Colorspaces/Cmyk.cs

@ -84,11 +84,9 @@ namespace ImageProcessorCore
/// </returns> /// </returns>
public static implicit operator Cmyk(Color color) public static implicit operator Cmyk(Color color)
{ {
color = color.Limited; float c = 1f - color.R / 255F;
float m = 1f - color.G / 255F;
float c = 1f - color.R; float y = 1f - color.B / 255F;
float m = 1f - color.G;
float y = 1f - color.B;
float k = Math.Min(c, Math.Min(m, y)); float k = Math.Min(c, Math.Min(m, y));

12
src/ImageProcessorCore/Colors/Colorspaces/Hsl.cs

@ -22,7 +22,7 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// The epsilon for comparing floating point numbers. /// The epsilon for comparing floating point numbers.
/// </summary> /// </summary>
private const float Epsilon = 0.001f; private const float Epsilon = 0.001F;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// The backing vector for SIMD support.
@ -74,10 +74,9 @@ namespace ImageProcessorCore
/// </returns> /// </returns>
public static implicit operator Hsl(Color color) public static implicit operator Hsl(Color color)
{ {
color = Color.ToNonPremultiplied(color.Limited); float r = color.R / 255F;
float r = color.R; float g = color.G / 255F;
float g = color.G; float b = color.B / 255F;
float b = color.B;
float max = Math.Max(r, Math.Max(g, b)); float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b)); float min = Math.Min(r, Math.Min(g, b));
@ -114,7 +113,8 @@ namespace ImageProcessorCore
{ {
s = chroma / (max + min); s = chroma / (max + min);
} }
else { else
{
s = chroma / (2 - chroma); s = chroma / (2 - chroma);
} }

9
src/ImageProcessorCore/Colors/Colorspaces/Hsv.cs

@ -22,7 +22,7 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// The epsilon for comparing floating point numbers. /// The epsilon for comparing floating point numbers.
/// </summary> /// </summary>
private const float Epsilon = 0.001f; private const float Epsilon = 0.001F;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// The backing vector for SIMD support.
@ -74,10 +74,9 @@ namespace ImageProcessorCore
/// </returns> /// </returns>
public static implicit operator Hsv(Color color) public static implicit operator Hsv(Color color)
{ {
color = Color.ToNonPremultiplied(color.Limited); float r = color.R / 255F;
float r = color.R; float g = color.G / 255F;
float g = color.G; float b = color.B / 255F;
float b = color.B;
float max = Math.Max(r, Math.Max(g, b)); float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b)); float min = Math.Min(r, Math.Min(g, b));

0
src/ImageProcessorCore/Colors/IAlmostEquatable.cs → src/ImageProcessorCore/Colors/Colorspaces/IAlmostEquatable.cs

5
src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs

@ -24,7 +24,7 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// The epsilon for comparing floating point numbers. /// The epsilon for comparing floating point numbers.
/// </summary> /// </summary>
private const float Epsilon = 0.001f; private const float Epsilon = 0.001F;
/// <summary> /// <summary>
/// The backing vector for SIMD support. /// The backing vector for SIMD support.
@ -79,7 +79,6 @@ namespace ImageProcessorCore
/// </returns> /// </returns>
public static implicit operator YCbCr(Color color) public static implicit operator YCbCr(Color color)
{ {
color = Color.ToNonPremultiplied(color.Limited) * 255f;
float r = color.R; float r = color.R;
float g = color.G; float g = color.G;
float b = color.B; float b = color.B;
@ -173,7 +172,7 @@ namespace ImageProcessorCore
/// Returns the hash code for this instance. /// Returns the hash code for this instance.
/// </summary> /// </summary>
/// <param name="color"> /// <param name="color">
/// The instance of <see cref="Hsv"/> to return the hash code for. /// The instance of <see cref="YCbCr"/> to return the hash code for.
/// </param> /// </param>
/// <returns> /// <returns>
/// A 32-bit signed integer that is the hash code for this instance. /// A 32-bit signed integer that is the hash code for this instance.

63
src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs

@ -0,0 +1,63 @@
// <copyright file="IPackedVector.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.Numerics;
/// <summary>
/// An interface that converts packed vector types to and from <see cref="Vector4"/> values,
/// allowing multiple encodings to be manipulated in a generic way.
/// </summary>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public interface IPackedVector<TP> : IPackedVector
where TP : struct
{
/// <summary>
/// Gets the packed representation of the value.
/// Typically packed in least to greatest significance order.
/// </summary>
/// <returns>
/// The <see cref="TP"/>.
/// </returns>
TP PackedValue();
}
/// <summary>
/// An interface that converts packed vector types to and from <see cref="Vector4"/> values.
/// </summary>
public interface IPackedVector
{
/// <summary>
/// Sets the packed representation from a <see cref="Vector4"/>.
/// </summary>
/// <param name="vector">The vector to pack.</param>
void PackVector(Vector4 vector);
/// <summary>
/// Sets the packed representation from a <see cref="Vector4"/>.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
void PackBytes(byte x, byte y, byte z, byte w);
/// <summary>
/// Expands the packed representation into a <see cref="Vector4"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToVector4();
/// <summary>
/// Expands the packed representation into a <see cref="T:byte[]"/>.
/// The bytes are typically expanded in least to greatest significance order.
/// Red -> Green -> Blue -> Alpha
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
byte[] ToBytes();
}
}

8
src/ImageProcessorCore/Colors/RgbaComponent.cs

@ -11,9 +11,9 @@ namespace ImageProcessorCore
public enum RgbaComponent public enum RgbaComponent
{ {
/// <summary> /// <summary>
/// The blue component. /// The red component.
/// </summary> /// </summary>
B = 0, R = 0,
/// <summary> /// <summary>
/// The green component. /// The green component.
@ -21,9 +21,9 @@ namespace ImageProcessorCore
G = 1, G = 1,
/// <summary> /// <summary>
/// The red component. /// The blue component.
/// </summary> /// </summary>
R = 2, B = 2,
/// <summary> /// <summary>
/// The alpha component. /// The alpha component.

91
src/ImageProcessorCore/Common/Extensions/Vector4Extensions.cs

@ -0,0 +1,91 @@
// <copyright file="Vector4Extensions.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.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Extension methods for the <see cref="Vector4"/> struct.
/// </summary>
public static class Vector4Extensions
{
/// <summary>
/// Compresses a linear color signal to its sRGB equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="linear">The <see cref="Vector4"/> whose signal to compress.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
public static Vector4 Compress(this Vector4 linear)
{
// TODO: Is there a faster way to do this?
float r = Compress(linear.X);
float g = Compress(linear.Y);
float b = Compress(linear.Z);
return new Vector4(r, g, b, linear.W);
}
/// <summary>
/// Expands an sRGB color signal to its linear equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="gamma">The <see cref="Color"/> whose signal to expand.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
public static Vector4 Expand(this Vector4 gamma)
{
// TODO: Is there a faster way to do this?
float r = Expand(gamma.X);
float g = Expand(gamma.Y);
float b = Expand(gamma.Z);
return new Vector4(r, g, b, gamma.W);
}
/// <summary>
/// Gets the compressed sRGB value from an linear signal.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to compress.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Compress(float signal)
{
if (signal <= 0.0031308f)
{
return signal * 12.92f;
}
return (1.055f * (float)Math.Pow(signal, 0.41666666f)) - 0.055f;
}
/// <summary>
/// Gets the expanded linear value from an sRGB signal.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to expand.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Expand(float signal)
{
if (signal <= 0.04045f)
{
return signal / 12.92f;
}
return (float)Math.Pow((signal + 0.055f) / 1.055f, 2.4f);
}
}
}

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

@ -61,25 +61,25 @@ namespace ImageProcessorCore
{ {
float temp; float temp;
if (x < 0) if (x < 0F)
{ {
x = -x; x = -x;
} }
temp = x * x; temp = x * x;
if (x < 1) if (x < 1F)
{ {
x = ((12 - (9 * b) - (6 * c)) * (x * temp)) + ((-18 + (12 * b) + (6 * c)) * temp) + (6 - (2 * b)); x = ((12 - (9 * b) - (6 * c)) * (x * temp)) + ((-18 + (12 * b) + (6 * c)) * temp) + (6 - (2 * b));
return x / 6; return x / 6F;
} }
if (x < 2) if (x < 2F)
{ {
x = ((-b - (6 * c)) * (x * temp)) + (((6 * b) + (30 * c)) * temp) + (((-12 * b) - (48 * c)) * x) + ((8 * b) + (24 * c)); x = ((-b - (6 * c)) * (x * temp)) + (((6 * b) + (30 * c)) * temp) + (((-12 * b) - (48 * c)) * x) + ((8 * b) + (24 * c));
return x / 6; return x / 6F;
} }
return 0; return 0F;
} }
/// <summary> /// <summary>
@ -91,7 +91,7 @@ namespace ImageProcessorCore
/// </returns> /// </returns>
public static float SinC(float x) public static float SinC(float x)
{ {
const float Epsilon = .00001f; const float Epsilon = .00001F;
if (Math.Abs(x) > Epsilon) if (Math.Abs(x) > Epsilon)
{ {
@ -156,13 +156,17 @@ namespace ImageProcessorCore
/// Finds the bounding rectangle based on the first instance of any color component other /// Finds the bounding rectangle based on the first instance of any color component other
/// than the given one. /// than the given one.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="bitmap">The <see cref="Image"/> to search within.</param> /// <param name="bitmap">The <see cref="Image"/> to search within.</param>
/// <param name="componentValue">The color component value to remove.</param> /// <param name="componentValue">The color component value to remove.</param>
/// <param name="channel">The <see cref="RgbaComponent"/> channel to test against.</param> /// <param name="channel">The <see cref="RgbaComponent"/> channel to test against.</param>
/// <returns> /// <returns>
/// The <see cref="Rectangle"/>. /// The <see cref="Rectangle"/>.
/// </returns> /// </returns>
public static Rectangle GetFilteredBoundingRectangle(ImageBase bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) public static Rectangle GetFilteredBoundingRectangle<T, TP>(ImageBase<T, TP> bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B)
where T : IPackedVector<TP>
where TP : struct
{ {
const float Epsilon = .00001f; const float Epsilon = .00001f;
int width = bitmap.Width; int width = bitmap.Width;
@ -170,29 +174,29 @@ namespace ImageProcessorCore
Point topLeft = new Point(); Point topLeft = new Point();
Point bottomRight = new Point(); Point bottomRight = new Point();
Func<PixelAccessor, int, int, float, bool> delegateFunc; Func<IPixelAccessor<T, TP>, int, int, float, bool> delegateFunc;
// Determine which channel to check against // Determine which channel to check against
switch (channel) switch (channel)
{ {
case RgbaComponent.R: case RgbaComponent.R:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].R - b) > Epsilon; delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[0] - b) > Epsilon;
break; break;
case RgbaComponent.G: case RgbaComponent.G:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].G - b) > Epsilon; delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[1] - b) > Epsilon;
break; break;
case RgbaComponent.A: case RgbaComponent.B:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].A - b) > Epsilon; delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[2] - b) > Epsilon;
break; break;
default: default:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].B - b) > Epsilon; delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[3] - b) > Epsilon;
break; break;
} }
Func<PixelAccessor, int> getMinY = pixels => Func<IPixelAccessor<T, TP>, int> getMinY = pixels =>
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
@ -208,7 +212,7 @@ namespace ImageProcessorCore
return 0; return 0;
}; };
Func<PixelAccessor, int> getMaxY = pixels => Func<IPixelAccessor<T, TP>, int> getMaxY = pixels =>
{ {
for (int y = height - 1; y > -1; y--) for (int y = height - 1; y > -1; y--)
{ {
@ -224,7 +228,7 @@ namespace ImageProcessorCore
return height; return height;
}; };
Func<PixelAccessor, int> getMinX = pixels => Func<IPixelAccessor<T, TP>, int> getMinX = pixels =>
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
@ -240,7 +244,7 @@ namespace ImageProcessorCore
return 0; return 0;
}; };
Func<PixelAccessor, int> getMaxX = pixels => Func<IPixelAccessor<T, TP>, int> getMaxX = pixels =>
{ {
for (int x = width - 1; x > -1; x--) for (int x = width - 1; x > -1; x--)
{ {
@ -256,7 +260,7 @@ namespace ImageProcessorCore
return height; return height;
}; };
using (PixelAccessor bitmapPixels = bitmap.Lock()) using (IPixelAccessor<T, TP> bitmapPixels = bitmap.Lock())
{ {
topLeft.Y = getMinY(bitmapPixels); topLeft.Y = getMinY(bitmapPixels);
topLeft.X = getMinX(bitmapPixels); topLeft.X = getMinX(bitmapPixels);
@ -276,11 +280,11 @@ namespace ImageProcessorCore
/// </returns>. /// </returns>.
private static float Clean(float x) private static float Clean(float x)
{ {
const float Epsilon = .00001f; const float Epsilon = .00001F;
if (Math.Abs(x) < Epsilon) if (Math.Abs(x) < Epsilon)
{ {
return 0f; return 0F;
} }
return x; return x;

18
src/ImageProcessorCore/Filters/Alpha.cs

@ -1,7 +1,7 @@
// <copyright file="Alpha.cs" company="James Jackson-South"> // <copyright file="Alpha.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
@ -15,11 +15,15 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Alters the alpha component of the image. /// Alters the alpha component of the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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="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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Alpha(this Image source, int percent, ProgressEventHandler progressHandler = null) public static Image<T, TP> Alpha<T, TP>(this Image<T, TP> source, int percent, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Alpha(source, percent, source.Bounds, progressHandler); return Alpha(source, percent, source.Bounds, progressHandler);
} }
@ -27,6 +31,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Alters the alpha component of the image. /// Alters the alpha component of the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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="percent">The new opacity of the image. Must be between 0 and 100.</param>
/// <param name="rectangle"> /// <param name="rectangle">
@ -34,9 +40,11 @@ namespace ImageProcessorCore
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image"/>.</returns>
public static Image Alpha(this Image source, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Alpha<T, TP>(this Image<T, TP> source, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
AlphaProcessor processor = new AlphaProcessor(percent); AlphaProcessor<T, TP> processor = new AlphaProcessor<T, TP>(percent);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

16
src/ImageProcessorCore/Filters/BackgroundColor.cs

@ -1,27 +1,31 @@
// <copyright file="BackgroundColor.cs" company="James Jackson-South"> // <copyright file="BackgroundColor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Combines the given image together with the current one by blending their pixels. /// Replaces the background color of image with the given one.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="color">The color to set as the background.</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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image BackgroundColor(this Image source, Color color, ProgressEventHandler progressHandler = null) public static Image<T, TP> BackgroundColor<T, TP>(this Image<T, TP> source, T color, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
BackgroundColorProcessor processor = new BackgroundColorProcessor(color); BackgroundColorProcessor<T, TP> processor = new BackgroundColorProcessor<T, TP>(color);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

60
src/ImageProcessorCore/Filters/BinaryThreshold.cs

@ -0,0 +1,60 @@
// <copyright file="BinaryThreshold.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{T,TP}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies binerization to the image splitting the pixels at the given threshold.
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="threshold">The threshold to apply binerization of the image. Must be between 0 and 1.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image<T, TP> BinaryThreshold<T, TP>(this Image<T, TP> source, float threshold, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{
return BinaryThreshold(source, threshold, source.Bounds, progressHandler);
}
/// <summary>
/// Applies binerization to the image splitting the pixels at the given threshold.
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="threshold">The threshold to apply binerization of the image. Must be between 0 and 1.</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{T,TP}"/>.</returns>
public static Image<T, TP> BinaryThreshold<T, TP>(this Image<T, TP> source, float threshold, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{
BinaryThresholdProcessor<T, TP> processor = new BinaryThresholdProcessor<T, TP>(threshold);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
}
}
}

22
src/ImageProcessorCore/Filters/BlackWhite.cs

@ -1,24 +1,28 @@
// <copyright file="BlackWhite.cs" company="James Jackson-South"> // <copyright file="BlackWhite.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Applies black and white toning to the image. /// Applies black and white toning to the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image BlackWhite(this Image source, ProgressEventHandler progressHandler = null) public static Image<T, TP> BlackWhite<T, TP>(this Image<T, TP> source, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return BlackWhite(source, source.Bounds, progressHandler); return BlackWhite(source, source.Bounds, progressHandler);
} }
@ -26,15 +30,19 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Applies black and white toning to the image. /// Applies black and white toning to the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image BlackWhite(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> BlackWhite<T, TP>(this Image<T, TP> source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
BlackWhiteProcessor processor = new BlackWhiteProcessor(); BlackWhiteProcessor<T, TP> processor = new BlackWhiteProcessor<T, TP>();
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

28
src/ImageProcessorCore/Filters/Blend.cs

@ -16,14 +16,15 @@ namespace ImageProcessorCore
/// Combines the given image together with the current one by blending their pixels. /// Combines the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="image"> /// <param name="image">The image to blend with the currently processing image.</param>
/// The image to blend with the currently processing image. /// <typeparam name="T">The pixel format.</typeparam>
/// Disposal of this image is the responsibility of the developer. /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// </param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Blend(this Image source, ImageBase image, int percent = 50, ProgressEventHandler progressHandler = null) public static Image<T, TP> Blend<T, TP>(this Image<T, TP> source, ImageBase<T, TP> image, int percent = 50, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Blend(source, image, percent, source.Bounds, progressHandler); return Blend(source, image, percent, source.Bounds, progressHandler);
} }
@ -32,19 +33,20 @@ namespace ImageProcessorCore
/// Combines the given image together with the current one by blending their pixels. /// Combines the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="image"> /// <param name="image">The image to blend with the currently processing image.</param>
/// The image to blend with the currently processing image. /// <typeparam name="T">The pixel format.</typeparam>
/// Disposal of this image is the responsibility of the developer. /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// </param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param> /// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Blend(this Image source, ImageBase image, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Blend<T, TP>(this Image<T, TP> source, ImageBase<T, TP> image, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
BlendProcessor processor = new BlendProcessor(image, percent); BlendProcessor<T, TP> processor = new BlendProcessor<T, TP>(image, percent);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

22
src/ImageProcessorCore/Filters/BoxBlur.cs

@ -1,25 +1,29 @@
// <copyright file="BoxBlur.cs" company="James Jackson-South"> // <copyright file="BoxBlur.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Applies a box blur to the image. /// Applies a box blur to the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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="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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image BoxBlur(this Image source, int radius = 7, ProgressEventHandler progressHandler = null) public static Image<T, TP> BoxBlur<T, TP>(this Image<T, TP> source, int radius = 7, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return BoxBlur(source, radius, source.Bounds, progressHandler); return BoxBlur(source, radius, source.Bounds, progressHandler);
} }
@ -27,16 +31,20 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Applies a box blur to the image. /// Applies a box blur to the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image BoxBlur(this Image source, int radius, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> BoxBlur<T, TP>(this Image<T, TP> source, int radius, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
BoxBlurProcessor processor = new BoxBlurProcessor(radius); BoxBlurProcessor<T, TP> processor = new BoxBlurProcessor<T, TP>(radius);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

22
src/ImageProcessorCore/Filters/Brightness.cs

@ -1,25 +1,29 @@
// <copyright file="Brightness.cs" company="James Jackson-South"> // <copyright file="Brightness.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Alters the brightness component of the image. /// Alters the brightness component of the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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="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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Brightness(this Image source, int amount, ProgressEventHandler progressHandler = null) public static Image<T, TP> Brightness<T, TP>(this Image<T, TP> source, int amount, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Brightness(source, amount, source.Bounds, progressHandler); return Brightness(source, amount, source.Bounds, progressHandler);
} }
@ -27,16 +31,20 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Alters the brightness component of the image. /// Alters the brightness component of the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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="amount">The new brightness of the image. Must be between -100 and 100.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Brightness(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Brightness<T, TP>(this Image<T, TP> source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
BrightnessProcessor processor = new BrightnessProcessor(amount); BrightnessProcessor<T, TP> processor = new BrightnessProcessor<T, TP>(amount);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

38
src/ImageProcessorCore/Filters/ColorBlindness.cs

@ -1,25 +1,29 @@
// <copyright file="ColorBlindness.cs" company="James Jackson-South"> // <copyright file="ColorBlindness.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Applies the given colorblindness simulator to the image. /// Applies the given colorblindness simulator to the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="colorBlindness">The type of color blindness simulator to apply.</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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, ProgressEventHandler progressHandler = null) public static Image<T, TP> ColorBlindness<T, TP>(this Image<T, TP> source, ColorBlindness colorBlindness, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return ColorBlindness(source, colorBlindness, source.Bounds, progressHandler); return ColorBlindness(source, colorBlindness, source.Bounds, progressHandler);
} }
@ -27,49 +31,53 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Applies the given colorblindness simulator to the image. /// Applies the given colorblindness simulator to the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="colorBlindness">The type of color blindness simulator to apply.</param> /// <param name="colorBlindness">The type of color blindness simulator to apply.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> ColorBlindness<T, TP>(this Image<T, TP> source, ColorBlindness colorBlindness, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
IImageProcessor processor; IImageProcessor<T, TP> processor;
switch (colorBlindness) switch (colorBlindness)
{ {
case ImageProcessorCore.ColorBlindness.Achromatomaly: case ImageProcessorCore.ColorBlindness.Achromatomaly:
processor = new AchromatomalyProcessor(); processor = new AchromatomalyProcessor<T, TP>();
break; break;
case ImageProcessorCore.ColorBlindness.Achromatopsia: case ImageProcessorCore.ColorBlindness.Achromatopsia:
processor = new AchromatopsiaProcessor(); processor = new AchromatopsiaProcessor<T, TP>();
break; break;
case ImageProcessorCore.ColorBlindness.Deuteranomaly: case ImageProcessorCore.ColorBlindness.Deuteranomaly:
processor = new DeuteranomalyProcessor(); processor = new DeuteranomalyProcessor<T, TP>();
break; break;
case ImageProcessorCore.ColorBlindness.Deuteranopia: case ImageProcessorCore.ColorBlindness.Deuteranopia:
processor = new DeuteranopiaProcessor(); processor = new DeuteranopiaProcessor<T, TP>();
break; break;
case ImageProcessorCore.ColorBlindness.Protanomaly: case ImageProcessorCore.ColorBlindness.Protanomaly:
processor = new ProtanomalyProcessor(); processor = new ProtanomalyProcessor<T, TP>();
break; break;
case ImageProcessorCore.ColorBlindness.Protanopia: case ImageProcessorCore.ColorBlindness.Protanopia:
processor = new ProtanopiaProcessor(); processor = new ProtanopiaProcessor<T, TP>();
break; break;
case ImageProcessorCore.ColorBlindness.Tritanomaly: case ImageProcessorCore.ColorBlindness.Tritanomaly:
processor = new TritanomalyProcessor(); processor = new TritanomalyProcessor<T, TP>();
break; break;
default: default:
processor = new TritanopiaProcessor(); processor = new TritanopiaProcessor<T, TP>();
break; break;
} }

22
src/ImageProcessorCore/Filters/Contrast.cs

@ -1,25 +1,29 @@
// <copyright file="Contrast.cs" company="James Jackson-South"> // <copyright file="Contrast.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Alters the contrast component of the image. /// Alters the contrast component of the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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="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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Contrast(this Image source, int amount, ProgressEventHandler progressHandler = null) public static Image<T, TP> Contrast<T, TP>(this Image<T, TP> source, int amount, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Contrast(source, amount, source.Bounds, progressHandler); return Contrast(source, amount, source.Bounds, progressHandler);
} }
@ -27,16 +31,20 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Alters the contrast component of the image. /// Alters the contrast component of the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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="amount">The new contrast of the image. Must be between -100 and 100.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Contrast(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Contrast<T, TP>(this Image<T, TP> source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
ContrastProcessor processor = new ContrastProcessor(amount); ContrastProcessor<T, TP> processor = new ContrastProcessor<T, TP>(amount);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

88
src/ImageProcessorCore/Filters/DetectEdges.cs

@ -1,37 +1,103 @@
// <copyright file="DetectEdges.cs" company="James Jackson-South"> // <copyright file="DetectEdges.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Detects any edges within the image. Uses the <see cref="SobelProcessor"/> filter /// Detects any edges within the image. Uses the <see cref="SobelProcessor"/> filter
/// operating in greyscale mode. /// operating in Grayscale mode.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image DetectEdges(this Image source, ProgressEventHandler progressHandler = null) public static Image<T, TP> DetectEdges<T, TP>(this Image<T, TP> source, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return DetectEdges(source, source.Bounds, new SobelProcessor { Greyscale = true }, progressHandler); return DetectEdges(source, source.Bounds, new SobelProcessor<T, TP> { Grayscale = true }, progressHandler);
} }
/// <summary> /// <summary>
/// Detects any edges within the image. /// Detects any edges within the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="filter">The filter for detecting edges.</param> /// <param name="filter">The filter for detecting edges.</param>
/// <param name="Grayscale">Whether to convert the image to Grayscale first. Defaults to true.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image DetectEdges(this Image source, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null) public static Image<T, TP> DetectEdges<T, TP>(this Image<T, TP> source, EdgeDetection filter, bool Grayscale = true, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{
IEdgeDetectorFilter<T, TP> processor;
switch (filter)
{
case EdgeDetection.Kayyali:
processor = new KayyaliProcessor<T, TP> { Grayscale = Grayscale };
break;
case EdgeDetection.Kirsch:
processor = new KirschProcessor<T, TP> { Grayscale = Grayscale };
break;
case EdgeDetection.Lapacian3X3:
processor = new Laplacian3X3Processor<T, TP> { Grayscale = Grayscale };
break;
case EdgeDetection.Lapacian5X5:
processor = new Laplacian5X5Processor<T, TP> { Grayscale = Grayscale };
break;
case EdgeDetection.LaplacianOfGaussian:
processor = new LaplacianOfGaussianProcessor<T, TP> { Grayscale = Grayscale };
break;
case EdgeDetection.Prewitt:
processor = new PrewittProcessor<T, TP> { Grayscale = Grayscale };
break;
case EdgeDetection.RobertsCross:
processor = new RobertsCrossProcessor<T, TP> { Grayscale = Grayscale };
break;
case EdgeDetection.Scharr:
processor = new ScharrProcessor<T, TP> { Grayscale = Grayscale };
break;
default:
processor = new ScharrProcessor<T, TP> { Grayscale = Grayscale };
break;
}
return DetectEdges(source, source.Bounds, processor, progressHandler);
}
/// <summary>
/// Detects any edges within the image.
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <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{T,TP}"/>.</returns>
public static Image<T, TP> DetectEdges<T, TP>(this Image<T, TP> source, IEdgeDetectorFilter<T, TP> filter, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return DetectEdges(source, source.Bounds, filter, progressHandler); return DetectEdges(source, source.Bounds, filter, progressHandler);
} }
@ -45,8 +111,10 @@ namespace ImageProcessorCore
/// </param> /// </param>
/// <param name="filter">The filter for detecting edges.</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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null) public static Image<T, TP> DetectEdges<T, TP>(this Image<T, TP> source, Rectangle rectangle, IEdgeDetectorFilter<T, TP> filter, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
filter.OnProgress += progressHandler; filter.OnProgress += progressHandler;

34
src/ImageProcessorCore/Filters/Greyscale.cs → src/ImageProcessorCore/Filters/Grayscale.cs

@ -1,44 +1,52 @@
// <copyright file="Greyscale.cs" company="James Jackson-South"> // <copyright file="Grayscale.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Applies greyscale toning to the image. /// Applies Grayscale toning to the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="mode">The formula to apply to perform the operation.</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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Greyscale(this Image source, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null) public static Image<T, TP> Grayscale<T, TP>(this Image<T, TP> source, GrayscaleMode mode = GrayscaleMode.Bt709, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Greyscale(source, source.Bounds, mode, progressHandler); return Grayscale(source, source.Bounds, mode, progressHandler);
} }
/// <summary> /// <summary>
/// Applies greyscale toning to the image. /// Applies Grayscale toning to the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="mode">The formula to apply to perform the operation.</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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Greyscale(this Image source, Rectangle rectangle, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null) public static Image<T, TP> Grayscale<T, TP>(this Image<T, TP> source, Rectangle rectangle, GrayscaleMode mode = GrayscaleMode.Bt709, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
IImageProcessor processor = mode == GreyscaleMode.Bt709 IImageProcessor<T, TP> processor = mode == GrayscaleMode.Bt709
? (IImageProcessor)new GreyscaleBt709Processor() ? (IImageProcessor<T, TP>)new GrayscaleBt709Processor<T, TP>()
: new GreyscaleBt601Processor(); : new GrayscaleBt601Processor<T, TP>();
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;

22
src/ImageProcessorCore/Filters/GuassianBlur.cs

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

22
src/ImageProcessorCore/Filters/GuassianSharpen.cs

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

22
src/ImageProcessorCore/Filters/Hue.cs

@ -1,25 +1,29 @@
// <copyright file="Hue.cs" company="James Jackson-South"> // <copyright file="Hue.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Alters the hue component of the image. /// Alters the hue component of the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="degrees">The angle in degrees to adjust the image.</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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Hue(this Image source, float degrees, ProgressEventHandler progressHandler = null) public static Image<T, TP> Hue<T, TP>(this Image<T, TP> source, float degrees, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Hue(source, degrees, source.Bounds, progressHandler); return Hue(source, degrees, source.Bounds, progressHandler);
} }
@ -27,16 +31,20 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Alters the hue component of the image. /// Alters the hue component of the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="degrees">The angle in degrees to adjust the image.</param> /// <param name="degrees">The angle in degrees to adjust the image.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Hue(this Image source, float degrees, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Hue<T, TP>(this Image<T, TP> source, float degrees, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
HueProcessor processor = new HueProcessor(degrees); HueProcessor<T, TP> processor = new HueProcessor<T, TP>(degrees);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

10
src/ImageProcessorCore/Filters/Invert.cs

@ -18,7 +18,9 @@ namespace ImageProcessorCore
/// <param name="source">The image this method extends.</param> /// <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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Invert(this Image source, ProgressEventHandler progressHandler = null) public static Image<T, TP> Invert<T, TP>(this Image<T, TP> source, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Invert(source, source.Bounds, progressHandler); return Invert(source, source.Bounds, progressHandler);
} }
@ -32,9 +34,11 @@ namespace ImageProcessorCore
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image"/>.</returns>
public static Image Invert(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Invert<T, TP>(this Image<T, TP> source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
InvertProcessor processor = new InvertProcessor(); InvertProcessor<T, TP> processor = new InvertProcessor<T, TP>();
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

22
src/ImageProcessorCore/Filters/Kodachrome.cs

@ -1,24 +1,28 @@
// <copyright file="Kodachrome.cs" company="James Jackson-South"> // <copyright file="Kodachrome.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect. /// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Kodachrome(this Image source, ProgressEventHandler progressHandler = null) public static Image<T, TP> Kodachrome<T, TP>(this Image<T, TP> source, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Kodachrome(source, source.Bounds, progressHandler); return Kodachrome(source, source.Bounds, progressHandler);
} }
@ -26,15 +30,19 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect. /// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Kodachrome(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Kodachrome<T, TP>(this Image<T, TP> source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
KodachromeProcessor processor = new KodachromeProcessor(); KodachromeProcessor<T, TP> processor = new KodachromeProcessor<T, TP>();
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

22
src/ImageProcessorCore/Filters/Lomograph.cs

@ -1,24 +1,28 @@
// <copyright file="Lomograph.cs" company="James Jackson-South"> // <copyright file="Lomograph.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect. /// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Lomograph(this Image source, ProgressEventHandler progressHandler = null) public static Image<T, TP> Lomograph<T, TP>(this Image<T, TP> source, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Lomograph(source, source.Bounds, progressHandler); return Lomograph(source, source.Bounds, progressHandler);
} }
@ -26,15 +30,19 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect. /// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Lomograph(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Lomograph<T, TP>(this Image<T, TP> source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
LomographProcessor processor = new LomographProcessor(); LomographProcessor<T, TP> processor = new LomographProcessor<T, TP>();
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

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

@ -6,7 +6,7 @@
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
/// <summary> /// <summary>
/// Enumerates the various types of color blindness. /// Enumerates the various types of defined color blindness filters.
/// </summary> /// </summary>
public enum ColorBlindness public enum ColorBlindness
{ {

58
src/ImageProcessorCore/Filters/Options/EdgeDetection.cs

@ -0,0 +1,58 @@
// <copyright file="EdgeDetection.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
{
/// <summary>
/// Enumerates the various types of defined edge detection filters.
/// </summary>
public enum EdgeDetection
{
/// <summary>
/// The Kayyali operator filter.
/// </summary>
Kayyali,
/// <summary>
/// The Kirsch operator filter.
/// </summary>
Kirsch,
/// <summary>
/// The Lapacian3X3 operator filter.
/// </summary>
Lapacian3X3,
/// <summary>
/// The Lapacian5X5 operator filter.
/// </summary>
Lapacian5X5,
/// <summary>
/// The LaplacianOfGaussian operator filter.
/// </summary>
LaplacianOfGaussian,
/// <summary>
/// The Prewitt operator filter.
/// </summary>
Prewitt,
/// <summary>
/// The RobertsCross operator filter.
/// </summary>
RobertsCross,
/// <summary>
/// The Scharr operator filter.
/// </summary>
Scharr,
/// <summary>
/// The Sobel operator filter.
/// </summary>
Sobel
}
}

8
src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleMode.cs → src/ImageProcessorCore/Filters/Options/GrayscaleMode.cs

@ -1,14 +1,14 @@
// <copyright file="GreyscaleMode.cs" company="James Jackson-South"> // <copyright file="GrayscaleMode.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageProcessorCore.Processors namespace ImageProcessorCore
{ {
/// <summary> /// <summary>
/// Provides enumeration over the various greyscale methods available. /// Enumerates the various types of defined Grayscale filters.
/// </summary> /// </summary>
public enum GreyscaleMode public enum GrayscaleMode
{ {
/// <summary> /// <summary>
/// ITU-R Recommendation BT.709 /// ITU-R Recommendation BT.709

26
src/ImageProcessorCore/Filters/Pixelate.cs

@ -1,15 +1,18 @@
// <copyright file="Pixelate.cs" company="James Jackson-South"> // <copyright file="Pixelate.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
using System;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
@ -18,8 +21,10 @@ namespace ImageProcessorCore
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="size">The size of the pixels.</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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Pixelate(this Image source, int size = 4, ProgressEventHandler progressHandler = null) public static Image<T, TP> Pixelate<T, TP>(this Image<T, TP> source, int size = 4, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Pixelate(source, size, source.Bounds, progressHandler); return Pixelate(source, size, source.Bounds, progressHandler);
} }
@ -33,10 +38,17 @@ namespace ImageProcessorCore
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Pixelate(this Image source, int size, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Pixelate<T, TP>(this Image<T, TP> source, int size, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
PixelateProcessor processor = new PixelateProcessor(size); if (size <= 0 || size > source.Height || size > source.Width)
{
throw new ArgumentOutOfRangeException(nameof(size));
}
PixelateProcessor<T, TP> processor = new PixelateProcessor<T, TP>(size);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

22
src/ImageProcessorCore/Filters/Polaroid.cs

@ -1,24 +1,28 @@
// <copyright file="Polaroid.cs" company="James Jackson-South"> // <copyright file="Polaroid.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect. /// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Polaroid(this Image source, ProgressEventHandler progressHandler = null) public static Image<T, TP> Polaroid<T, TP>(this Image<T, TP> source, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Polaroid(source, source.Bounds, progressHandler); return Polaroid(source, source.Bounds, progressHandler);
} }
@ -26,15 +30,19 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect. /// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image Polaroid(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Polaroid<T, TP>(this Image<T, TP> source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
PolaroidProcessor processor = new PolaroidProcessor(); PolaroidProcessor<T, TP> processor = new PolaroidProcessor<T, TP>();
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

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

@ -5,17 +5,20 @@
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System;
using System.Numerics; using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// An <see cref="IImageProcessor"/> to change the Alpha of an <see cref="Image"/>. /// An <see cref="IImageProcessor{T,TP}"/> to change the Alpha of an <see cref="Image{T,TP}"/>.
/// </summary> /// </summary>
public class AlphaProcessor : ImageProcessor /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class AlphaProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AlphaProcessor"/> class. /// Initializes a new instance of the <see cref="AlphaProcessor{T,TP}"/> class.
/// </summary> /// </summary>
/// <param name="percent">The percentage to adjust the opacity of the image. Must be between 0 and 100.</param> /// <param name="percent">The percentage to adjust the opacity of the image. Must be between 0 and 100.</param>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
@ -33,7 +36,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; } public int Value { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
float alpha = this.Value / 100f; float alpha = this.Value / 100f;
int sourceY = sourceRectangle.Y; int sourceY = sourceRectangle.Y;
@ -42,21 +45,25 @@ namespace ImageProcessorCore.Processors
int endX = sourceRectangle.Right; int endX = sourceRectangle.Right;
Vector4 alphaVector = new Vector4(1, 1, 1, alpha); Vector4 alphaVector = new Vector4(1, 1, 1, alpha);
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
if (y >= sourceY && y < sourceBottom) if (y >= sourceY && y < sourceBottom)
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
Vector4 color = Color.ToNonPremultiplied(sourcePixels[x, y]).ToVector4(); Vector4 color = sourcePixels[x, y].ToVector4();
color *= alphaVector; color *= alphaVector;
targetPixels[x, y] = Color.FromNonPremultiplied(new Color(color));
T packed = default(T);
packed.PackVector(color);
targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();

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

@ -6,12 +6,15 @@
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System; using System;
using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// Sets the background color of the image. /// Sets the background color of the image.
/// </summary> /// </summary>
public class BackgroundColorProcessor : ImageProcessor public class BackgroundColorProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// The epsilon for comparing floating point numbers. /// The epsilon for comparing floating point numbers.
@ -19,46 +22,47 @@ namespace ImageProcessorCore.Processors
private const float Epsilon = 0.001f; private const float Epsilon = 0.001f;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BackgroundColorProcessor"/> class. /// Initializes a new instance of the <see cref="BackgroundColorProcessor{T,TP}"/> class.
/// </summary> /// </summary>
/// <param name="color">The <see cref="Color"/> to set the background color to.</param> /// <param name="color">The <see cref="T"/> to set the background color to.</param>
public BackgroundColorProcessor(Color color) public BackgroundColorProcessor(T color)
{ {
this.Value = Color.FromNonPremultiplied(color); this.Value = color;
} }
/// <summary> /// <summary>
/// Gets the background color value. /// Gets the background color value.
/// </summary> /// </summary>
public Color Value { get; } public T Value { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
int sourceY = sourceRectangle.Y; int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom; int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X; int startX = sourceRectangle.X;
int endX = sourceRectangle.Right; int endX = sourceRectangle.Right;
Color backgroundColor = this.Value; Vector4 backgroundColor = this.Value.ToVector4();
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
if (y >= sourceY && y < sourceBottom) if (y >= sourceY && y < sourceBottom)
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
Color color = sourcePixels[x, y]; Vector4 color = sourcePixels[x, y].ToVector4();
float a = color.A; float a = color.W;
if (a < 1 && a > 0) if (a < 1 && a > 0)
{ {
color = Color.Lerp(color, backgroundColor, .5f); color = Vector4.Lerp(color, backgroundColor, .5f);
} }
if (Math.Abs(a) < Epsilon) if (Math.Abs(a) < Epsilon)
@ -66,7 +70,9 @@ namespace ImageProcessorCore.Processors
color = backgroundColor; color = backgroundColor;
} }
targetPixels[x, y] = color; T packed = default(T);
packed.PackVector(color);
targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();

52
src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs → src/ImageProcessorCore/Filters/Processors/Binarization/BinaryThresholdProcessor.cs

@ -1,19 +1,22 @@
// <copyright file="ThresholdProcessor.cs" company="James Jackson-South"> // <copyright file="BinaryThresholdProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// An <see cref="IImageProcessor"/> to perform binary threshold filtering against an /// An <see cref="IImageProcessor{T,TP}"/> to perform binary threshold filtering against an
/// <see cref="Image"/>. The image will be converted to greyscale before thresholding /// <see cref="Image"/>. The image will be converted to Grayscale before thresholding
/// occurs. /// occurs.
/// </summary> /// </summary>
public class ThresholdProcessor : ImageProcessor /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class BinaryThresholdProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ThresholdProcessor"/> class. /// Initializes a new instance of the <see cref="ThresholdProcessor"/> class.
@ -22,10 +25,19 @@ namespace ImageProcessorCore.Processors
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="threshold"/> is less than 0 or is greater than 1. /// <paramref name="threshold"/> is less than 0 or is greater than 1.
/// </exception> /// </exception>
public ThresholdProcessor(float threshold) public BinaryThresholdProcessor(float threshold)
{ {
// TODO: Check limit.
Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold));
this.Value = threshold; this.Value = threshold;
T upper = default(T);
upper.PackVector(Color.White.ToVector4());
this.UpperColor = upper;
T lower = default(T);
lower.PackVector(Color.Black.ToVector4());
this.LowerColor = lower;
} }
/// <summary> /// <summary>
@ -36,46 +48,50 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// The color to use for pixels that are above the threshold. /// The color to use for pixels that are above the threshold.
/// </summary> /// </summary>
public Color UpperColor => Color.White; public T UpperColor { get; set; }
/// <summary> /// <summary>
/// The color to use for pixels that fall below the threshold. /// The color to use for pixels that fall below the threshold.
/// </summary> /// </summary>
public Color LowerColor => Color.Black; public T LowerColor { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
new GreyscaleBt709Processor().Apply(source, source, sourceRectangle); new GrayscaleBt709Processor<T, TP>().Apply(source, source, sourceRectangle);
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
// target.SetPixels(source.Width, source.Height, source.Pixels);
float threshold = this.Value; float threshold = this.Value;
Color upper = this.UpperColor; T upper = this.UpperColor;
Color lower = this.LowerColor; T lower = this.LowerColor;
int sourceY = sourceRectangle.Y; int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom; int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X; int startX = sourceRectangle.X;
int endX = sourceRectangle.Right; int endX = sourceRectangle.Right;
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
if (y >= sourceY && y < sourceBottom) if (y >= sourceY && y < sourceBottom)
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
Color color = sourcePixels[x, y]; T color = sourcePixels[x, y];
// Any channel will do since it's greyscale. // Any channel will do since it's Grayscale.
targetPixels[x, y] = color.B >= threshold ? upper : lower; targetPixels[x, y] = color.ToVector4().X >= threshold ? upper : lower;
} }
this.OnRowProcessed(); this.OnRowProcessed();
} }

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

@ -5,17 +5,22 @@
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// Combines two images together by blending the pixels. /// Combines two images together by blending the pixels.
/// </summary> /// </summary>
public class BlendProcessor : ImageProcessor /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class BlendProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// The image to blend. /// The image to blend.
/// </summary> /// </summary>
private readonly ImageBase blend; private readonly ImageBase<T, TP> blend;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BlendProcessor"/> class. /// Initializes a new instance of the <see cref="BlendProcessor"/> class.
@ -25,7 +30,7 @@ namespace ImageProcessorCore.Processors
/// Disposal of this image is the responsibility of the developer. /// Disposal of this image is the responsibility of the developer.
/// </param> /// </param>
/// <param name="alpha">The opacity of the image to blend. Between 0 and 100.</param> /// <param name="alpha">The opacity of the image to blend. Between 0 and 100.</param>
public BlendProcessor(ImageBase image, int alpha = 100) public BlendProcessor(ImageBase<T, TP> image, int alpha = 100)
{ {
Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha)); Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha));
this.blend = image; this.blend = image;
@ -38,7 +43,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; } public int Value { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
int sourceY = sourceRectangle.Y; int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom; int sourceBottom = sourceRectangle.Bottom;
@ -47,9 +52,9 @@ namespace ImageProcessorCore.Processors
Rectangle bounds = this.blend.Bounds; Rectangle bounds = this.blend.Bounds;
float alpha = this.Value / 100f; float alpha = this.Value / 100f;
using (PixelAccessor toBlendPixels = this.blend.Lock()) using (IPixelAccessor<T, TP> toBlendPixels = this.blend.Lock())
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
@ -60,21 +65,23 @@ namespace ImageProcessorCore.Processors
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
Color color = sourcePixels[x, y]; Vector4 color = sourcePixels[x, y].ToVector4();
if (bounds.Contains(x, y)) if (bounds.Contains(x, y))
{ {
Color blendedColor = toBlendPixels[x, y]; Vector4 blendedColor = toBlendPixels[x, y].ToVector4();
if (blendedColor.A > 0) if (blendedColor.W > 0)
{ {
// Lerping colors is dependent on the alpha of the blended color // Lerping colors is dependent on the alpha of the blended color
float alphaFactor = alpha > 0 ? alpha : blendedColor.A; float alphaFactor = alpha > 0 ? alpha : blendedColor.W;
color = Color.Lerp(color, blendedColor, alphaFactor); color = Vector4.Lerp(color, blendedColor, alphaFactor);
} }
} }
targetPixels[x, y] = color; T packed = default(T);
packed.PackVector(color);
targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();

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

@ -5,14 +5,17 @@
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System;
using System.Numerics; using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// An <see cref="IImageProcessor"/> to change the brightness of an <see cref="Image"/>. /// An <see cref="IImageProcessor{T,TP}"/> to change the brightness of an <see cref="Image{T,TP}"/>.
/// </summary> /// </summary>
public class BrightnessProcessor : ImageProcessor /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class BrightnessProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BrightnessProcessor"/> class. /// Initializes a new instance of the <see cref="BrightnessProcessor"/> class.
@ -33,7 +36,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; } public int Value { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
float brightness = this.Value / 100f; float brightness = this.Value / 100f;
int sourceY = sourceRectangle.Y; int sourceY = sourceRectangle.Y;
@ -41,25 +44,31 @@ namespace ImageProcessorCore.Processors
int startX = sourceRectangle.X; int startX = sourceRectangle.X;
int endX = sourceRectangle.Right; int endX = sourceRectangle.Right;
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
if (y >= sourceY && y < sourceBottom) if (y >= sourceY && y < sourceBottom)
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
Color color = Color.Expand(sourcePixels[x, y]); // TODO: Check this with other formats.
Vector4 vector = sourcePixels[x, y].ToVector4().Expand();
Vector3 transformed = new Vector3(vector.X, vector.Y, vector.Z);
transformed += new Vector3(brightness);
vector = new Vector4(transformed, vector.W);
Vector3 vector3 = color.ToVector3(); T packed = default(T);
vector3 += new Vector3(brightness); packed.PackVector(vector.Compress());
targetPixels[x, y] = Color.Compress(new Color(vector3, color.A)); targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();
} }
}); });

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image to their black and white equivalent. /// Converts the colors of the image to their black and white equivalent.
/// </summary> /// </summary>
public class BlackWhiteProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class BlackWhiteProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
/// </summary> /// </summary>
public class AchromatomalyProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class AchromatomalyProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
/// </summary> /// </summary>
public class AchromatopsiaProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class AchromatopsiaProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness.
/// </summary> /// </summary>
public class DeuteranomalyProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class DeuteranomalyProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness.
/// </summary> /// </summary>
public class DeuteranopiaProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class DeuteranopiaProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness. /// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness.
/// </summary> /// </summary>
public class ProtanomalyProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class ProtanomalyProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness.
/// </summary> /// </summary>
public class ProtanopiaProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class ProtanopiaProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness.
/// </summary> /// </summary>
public class TritanomalyProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class TritanomalyProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness.
/// </summary> /// </summary>
public class TritanopiaProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class TritanopiaProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

34
src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs

@ -9,34 +9,40 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// The color matrix filter. /// The color matrix filter. Inherit from this class to perform operation involving color matrices.
/// </summary> /// </summary>
public abstract class ColorMatrixFilter : ImageProcessor, IColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public abstract class ColorMatrixFilter<T, TP> : ImageProcessor<T, TP>, IColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public abstract Matrix4x4 Matrix { get; } public abstract Matrix4x4 Matrix { get; }
/// <inheritdoc/> /// <inheritdoc/>
public virtual bool Compand => true; public override bool Compand { get; set; } = true;
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
int startX = sourceRectangle.X; int startX = sourceRectangle.X;
int endX = sourceRectangle.Right; int endX = sourceRectangle.Right;
Matrix4x4 matrix = this.Matrix; Matrix4x4 matrix = this.Matrix;
bool compand = this.Compand;
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
targetPixels[x, y] = this.ApplyMatrix(sourcePixels[x, y], matrix); targetPixels[x, y] = this.ApplyMatrix(sourcePixels[x, y], matrix, compand);
} }
this.OnRowProcessed(); this.OnRowProcessed();
@ -49,20 +55,24 @@ namespace ImageProcessorCore.Processors
/// </summary> /// </summary>
/// <param name="color">The source color.</param> /// <param name="color">The source color.</param>
/// <param name="matrix">The matrix.</param> /// <param name="matrix">The matrix.</param>
/// <param name="compand">Whether to compand the color during processing.</param>
/// <returns> /// <returns>
/// The <see cref="Color"/>. /// The <see cref="Color"/>.
/// </returns> /// </returns>
private Color ApplyMatrix(Color color, Matrix4x4 matrix) private T ApplyMatrix(T color, Matrix4x4 matrix, bool compand)
{ {
bool compand = this.Compand; Vector4 vector = color.ToVector4();
if (compand) if (compand)
{ {
color = Color.Expand(color); vector = vector.Expand();
} }
Vector3 transformed = Vector3.Transform(color.ToVector3(), matrix); Vector3 transformed = Vector3.Transform(new Vector3(vector.X, vector.Y, vector.Z), matrix);
return compand ? Color.Compress(new Color(transformed, color.A)) : new Color(transformed, color.A); vector = new Vector4(transformed, vector.W);
T packed = default(T);
packed.PackVector(compand ? vector.Compress() : vector);
return packed;
} }
} }
} }

10
src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt601Processor.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/GrayscaleBt601Processor.cs

@ -1,4 +1,4 @@
// <copyright file="GreyscaleBt601Processor.cs" company="James Jackson-South"> // <copyright file="GrayscaleBt601Processor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -8,10 +8,14 @@ namespace ImageProcessorCore.Processors
using System.Numerics; using System.Numerics;
/// <summary> /// <summary>
/// Converts the colors of the image to greyscale applying the formula as specified by /// Converts the colors of the image to Grayscale 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"/>. /// ITU-R Recommendation BT.601 <see href="https://en.wikipedia.org/wiki/Luma_%28video%29#Rec._601_luma_versus_Rec._709_luma_coefficients"/>.
/// </summary> /// </summary>
public class GreyscaleBt601Processor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class GrayscaleBt601Processor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

8
src/ImageProcessorCore/Filters/Processors/ColorMatrix/GreyscaleBt709Processor.cs → src/ImageProcessorCore/Filters/Processors/ColorMatrix/GrayscaleBt709Processor.cs

@ -1,4 +1,4 @@
// <copyright file="GreyscaleBt709Processor.cs" company="James Jackson-South"> // <copyright file="GrayscaleBt709Processor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -8,10 +8,12 @@ namespace ImageProcessorCore.Processors
using System.Numerics; using System.Numerics;
/// <summary> /// <summary>
/// Converts the colors of the image to greyscale applying the formula as specified by /// Converts the colors of the image to Grayscale applying the formula as specified by
/// ITU-R Recommendation BT.709 <see href="https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients"/>. /// ITU-R Recommendation BT.709 <see href="https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients"/>.
/// </summary> /// </summary>
public class GreyscaleBt709Processor : ColorMatrixFilter public class GrayscaleBt709Processor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

37
src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs

@ -8,7 +8,14 @@ namespace ImageProcessorCore.Processors
using System; using System;
using System.Numerics; using System.Numerics;
public class HueProcessor : ColorMatrixFilter /// <summary>
/// An <see cref="IImageProcessor{T,TP}"/> to change the hue of an <see cref="Image"/>.
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class HueProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// The <see cref="Matrix4x4"/> used to alter the image. /// The <see cref="Matrix4x4"/> used to alter the image.
@ -31,23 +38,8 @@ namespace ImageProcessorCore.Processors
} }
this.Angle = angle; this.Angle = angle;
}
/// <summary> float radians = ImageMaths.DegreesToRadians(angle);
/// Gets the rotation value.
/// </summary>
public float Angle { get; }
/// <inheritdoc/>
public override Matrix4x4 Matrix => this.matrix;
/// <inheritdoc/>
public override bool Compand => false;
/// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
float radians = (float)ImageMaths.DegreesToRadians(this.Angle);
double cosradians = Math.Cos(radians); double cosradians = Math.Cos(radians);
double sinradians = Math.Sin(radians); double sinradians = Math.Sin(radians);
@ -77,5 +69,16 @@ namespace ImageProcessorCore.Processors
this.matrix = matrix4X4; this.matrix = matrix4X4;
} }
/// <summary>
/// Gets the rotation value.
/// </summary>
public float Angle { get; }
/// <inheritdoc/>
public override Matrix4x4 Matrix => this.matrix;
/// <inheritdoc/>
public override bool Compand => false;
} }
} }

10
src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs

@ -11,17 +11,13 @@ namespace ImageProcessorCore.Processors
/// Encapsulates properties and methods for creating processors that utilize a matrix to /// Encapsulates properties and methods for creating processors that utilize a matrix to
/// alter the image pixels. /// alter the image pixels.
/// </summary> /// </summary>
public interface IColorMatrixFilter : IImageProcessor public interface IColorMatrixFilter<T, TP> : IImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// Gets the <see cref="Matrix4x4"/> used to alter the image. /// Gets the <see cref="Matrix4x4"/> used to alter the image.
/// </summary> /// </summary>
Matrix4x4 Matrix { get; } Matrix4x4 Matrix { get; }
/// <summary>
/// Gets a value indicating whether to compress
/// or expand individual pixel colors the value on processing.
/// </summary>
bool Compand { get; }
} }
} }

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating an old Kodachrome camera effect. /// Converts the colors of the image recreating an old Kodachrome camera effect.
/// </summary> /// </summary>
public class KodachromeProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class KodachromeProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

12
src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating an old Lomograph effect. /// Converts the colors of the image recreating an old Lomograph effect.
/// </summary> /// </summary>
public class LomographProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class LomographProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()
@ -24,9 +28,11 @@ namespace ImageProcessorCore.Processors
}; };
/// <inheritdoc/> /// <inheritdoc/>
protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void AfterApply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
new VignetteProcessor { Color = new Color(0, 10 / 255f, 0) }.Apply(target, target, targetRectangle); T packed = default(T);
packed.PackBytes(0, 10, 0, 255);
new VignetteProcessor<T, TP> { VignetteColor = packed }.Apply(target, target, targetRectangle);
} }
} }
} }

19
src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Converts the colors of the image recreating an old Polaroid effect. /// Converts the colors of the image recreating an old Polaroid effect.
/// </summary> /// </summary>
public class PolaroidProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class PolaroidProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()
@ -30,12 +34,17 @@ namespace ImageProcessorCore.Processors
}; };
/// <inheritdoc/> /// <inheritdoc/>
protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void AfterApply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
new VignetteProcessor { Color = new Color(102 / 255f, 34 / 255f, 0) }.Apply(target, target, targetRectangle); T packedV = default(T);
new GlowProcessor packedV.PackBytes(102, 34, 0, 255);
new VignetteProcessor<T, TP> { VignetteColor = packedV }.Apply(target, target, targetRectangle);
T packedG = default(T);
packedG.PackBytes(255, 153, 102, 178);
new GlowProcessor<T, TP>
{ {
Color = new Color(1, 153 / 255f, 102 / 255f, .7f), GlowColor = packedG,
RadiusX = target.Width / 4f, RadiusX = target.Width / 4f,
RadiusY = target.Width / 4f RadiusY = target.Width / 4f
} }

19
src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs

@ -5,13 +5,16 @@
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System;
using System.Numerics; using System.Numerics;
/// <summary> /// <summary>
/// An <see cref="IImageProcessor"/> to change the saturation of an <see cref="Image"/>. /// An <see cref="IImageProcessor{T,TP}"/> to change the saturation of an <see cref="Image"/>.
/// </summary> /// </summary>
public class SaturationProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class SaturationProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// The saturation to be applied to the image. /// The saturation to be applied to the image.
@ -34,14 +37,7 @@ namespace ImageProcessorCore.Processors
{ {
Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation)); Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation));
this.saturation = saturation; this.saturation = saturation;
}
/// <inheritdoc/>
public override Matrix4x4 Matrix => this.matrix;
/// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
float saturationFactor = this.saturation / 100f; float saturationFactor = this.saturation / 100f;
// Stop at -1 to prevent inversion. // Stop at -1 to prevent inversion.
@ -71,5 +67,8 @@ namespace ImageProcessorCore.Processors
this.matrix = matrix4X4; this.matrix = matrix4X4;
} }
/// <inheritdoc/>
public override Matrix4x4 Matrix => this.matrix;
} }
} }

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

@ -11,7 +11,11 @@ namespace ImageProcessorCore.Processors
/// Converts the colors of the image to their sepia equivalent. /// 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"/> /// The formula used matches the svg specification. <see href="http://www.w3.org/TR/filter-effects/#sepiaEquivalent"/>
/// </summary> /// </summary>
public class SepiaProcessor : ColorMatrixFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class SepiaProcessor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4() public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -5,14 +5,17 @@
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System;
using System.Numerics; using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// An <see cref="IImageProcessor"/> to change the contrast of an <see cref="Image"/>. /// An <see cref="IImageProcessor{T,TP}"/> to change the contrast of an <see cref="Image"/>.
/// </summary> /// </summary>
public class ContrastProcessor : ImageProcessor /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class ContrastProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ContrastProcessor"/> class. /// Initializes a new instance of the <see cref="ContrastProcessor"/> class.
@ -33,7 +36,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; } public int Value { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
float contrast = (100f + this.Value) / 100f; float contrast = (100f + this.Value) / 100f;
int sourceY = sourceRectangle.Y; int sourceY = sourceRectangle.Y;
@ -43,23 +46,26 @@ namespace ImageProcessorCore.Processors
Vector4 contrastVector = new Vector4(contrast, contrast, contrast, 1); Vector4 contrastVector = new Vector4(contrast, contrast, contrast, 1);
Vector4 shiftVector = new Vector4(.5f, .5f, .5f, 1); Vector4 shiftVector = new Vector4(.5f, .5f, .5f, 1);
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
if (y >= sourceY && y < sourceBottom) if (y >= sourceY && y < sourceBottom)
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
Vector4 color = Color.Expand(sourcePixels[x, y]).ToVector4(); Vector4 vector = (sourcePixels[x, y]).ToVector4().Expand();
color -= shiftVector; vector -= shiftVector;
color *= contrastVector; vector *= contrastVector;
color += shiftVector; vector += shiftVector;
targetPixels[x, y] = Color.Compress(new Color(color)); T packed = default(T);
packed.PackVector(vector.Compress());
targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();
} }

8
src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs

@ -8,7 +8,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Applies a Box blur filter to the image. /// Applies a Box blur filter to the image.
/// </summary> /// </summary>
public class BoxBlurProcessor : Convolution2PassFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class BoxBlurProcessor<T, TP> : Convolution2PassFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// The maximum size of the kernal in either direction. /// The maximum size of the kernal in either direction.
@ -43,7 +47,7 @@ namespace ImageProcessorCore.Processors
public override float[,] KernelY => this.kernelY; public override float[,] KernelY => this.kernelY;
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
if (this.kernelY == null) if (this.kernelY == null)
{ {

28
src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs

@ -6,12 +6,17 @@
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System; using System;
using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// Defines a filter that uses two one-dimensional matrices to perform convolution against an image. /// Defines a filter that uses two one-dimensional matrices to perform convolution against an image.
/// </summary> /// </summary>
public abstract class Convolution2DFilter : ImageProcessor /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public abstract class Convolution2DFilter<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// Gets the horizontal gradient operator. /// Gets the horizontal gradient operator.
@ -24,7 +29,7 @@ namespace ImageProcessorCore.Processors
public abstract float[,] KernelY { get; } public abstract float[,] KernelY { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
float[,] kernelX = this.KernelX; float[,] kernelX = this.KernelX;
float[,] kernelY = this.KernelY; float[,] kernelY = this.KernelY;
@ -42,12 +47,13 @@ namespace ImageProcessorCore.Processors
int maxY = sourceBottom - 1; int maxY = sourceBottom - 1;
int maxX = endX - 1; int maxX = endX - 1;
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
if (y >= sourceY && y < sourceBottom) if (y >= sourceY && y < sourceBottom)
@ -76,10 +82,10 @@ namespace ImageProcessorCore.Processors
offsetX = offsetX.Clamp(0, maxX); offsetX = offsetX.Clamp(0, maxX);
Color currentColor = sourcePixels[offsetX, offsetY]; Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.R; float r = currentColor.X;
float g = currentColor.G; float g = currentColor.Y;
float b = currentColor.B; float b = currentColor.Z;
if (fy < kernelXHeight) if (fy < kernelXHeight)
{ {
@ -101,8 +107,10 @@ namespace ImageProcessorCore.Processors
float green = (float)Math.Sqrt((gX * gX) + (gY * gY)); float green = (float)Math.Sqrt((gX * gX) + (gY * gY));
float blue = (float)Math.Sqrt((bX * bX) + (bY * bY)); float blue = (float)Math.Sqrt((bX * bX) + (bY * bY));
Color targetColor = targetPixels[x, y]; Vector4 targetColor = targetPixels[x, y].ToVector4();
targetPixels[x, y] = new Color(red, green, blue, targetColor.A); T packed = default(T);
packed.PackVector(new Vector4(red, green, blue, targetColor.Z));
targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();
} }

40
src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs

@ -5,12 +5,17 @@
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// Defines a filter that uses two one-dimensional matrices to perform two-pass convolution against an image. /// Defines a filter that uses two one-dimensional matrices to perform two-pass convolution against an image.
/// </summary> /// </summary>
public abstract class Convolution2PassFilter : ImageProcessor /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public abstract class Convolution2PassFilter<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// Gets the horizontal gradient operator. /// Gets the horizontal gradient operator.
@ -23,24 +28,18 @@ namespace ImageProcessorCore.Processors
public abstract float[,] KernelY { get; } public abstract float[,] KernelY { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply( protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
ImageBase target,
ImageBase source,
Rectangle targetRectangle,
Rectangle sourceRectangle,
int startY,
int endY)
{ {
float[,] kernelX = this.KernelX; float[,] kernelX = this.KernelX;
float[,] kernelY = this.KernelY; float[,] kernelY = this.KernelY;
ImageBase firstPass = new Image(source.Width, source.Height); ImageBase<T, TP> firstPass = new Image<T, TP>(source.Width, source.Height);
this.ApplyConvolution(firstPass, source, sourceRectangle, startY, endY, kernelX); this.ApplyConvolution(firstPass, source, sourceRectangle, startY, endY, kernelX);
this.ApplyConvolution(target, firstPass, sourceRectangle, startY, endY, kernelY); this.ApplyConvolution(target, firstPass, sourceRectangle, startY, endY, kernelY);
} }
/// <summary> /// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase"/> at the specified location /// Applies the process to the specified portion of the specified <see cref="ImageBase{T,TP}"/> at the specified location
/// and with the specified size. /// and with the specified size.
/// </summary> /// </summary>
/// <param name="target">Target image to apply the process to.</param> /// <param name="target">Target image to apply the process to.</param>
@ -51,13 +50,7 @@ namespace ImageProcessorCore.Processors
/// <param name="startY">The index of the row within the source image to start processing.</param> /// <param name="startY">The index of the row within the source image to start processing.</param>
/// <param name="endY">The index of the row within the source image to end processing.</param> /// <param name="endY">The index of the row within the source image to end processing.</param>
/// <param name="kernel">The kernel operator.</param> /// <param name="kernel">The kernel operator.</param>
private void ApplyConvolution( private void ApplyConvolution(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle sourceRectangle, int startY, int endY, float[,] kernel)
ImageBase target,
ImageBase source,
Rectangle sourceRectangle,
int startY,
int endY,
float[,] kernel)
{ {
int kernelHeight = kernel.GetLength(0); int kernelHeight = kernel.GetLength(0);
int kernelWidth = kernel.GetLength(1); int kernelWidth = kernel.GetLength(1);
@ -70,17 +63,18 @@ namespace ImageProcessorCore.Processors
int maxY = sourceBottom - 1; int maxY = sourceBottom - 1;
int maxX = endX - 1; int maxX = endX - 1;
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
Color destination = Color.Empty; Vector4 destination = new Vector4();
// Apply each matrix multiplier to the color components for each pixel. // Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelHeight; fy++) for (int fy = 0; fy < kernelHeight; fy++)
@ -97,12 +91,14 @@ namespace ImageProcessorCore.Processors
offsetX = offsetX.Clamp(0, maxX); offsetX = offsetX.Clamp(0, maxX);
Color currentColor = sourcePixels[offsetX, offsetY]; Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
destination += kernel[fy, fx] * currentColor; destination += kernel[fy, fx] * currentColor;
} }
} }
targetPixels[x, y] = destination; T packed = default(T);
packed.PackVector(destination);
targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();

26
src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs

@ -5,12 +5,15 @@
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// Defines a filter that uses a 2 dimensional matrix to perform convolution against an image. /// Defines a filter that uses a 2 dimensional matrix to perform convolution against an image.
/// </summary> /// </summary>
public abstract class ConvolutionFilter : ImageProcessor public abstract class ConvolutionFilter<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// Gets the 2d gradient operator. /// Gets the 2d gradient operator.
@ -18,7 +21,7 @@ namespace ImageProcessorCore.Processors
public abstract float[,] KernelXY { get; } public abstract float[,] KernelXY { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
float[,] kernelX = this.KernelXY; float[,] kernelX = this.KernelXY;
int kernelLength = kernelX.GetLength(0); int kernelLength = kernelX.GetLength(0);
@ -31,12 +34,13 @@ namespace ImageProcessorCore.Processors
int maxY = sourceBottom - 1; int maxY = sourceBottom - 1;
int maxX = endX - 1; int maxX = endX - 1;
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
if (y >= sourceY && y < sourceBottom) if (y >= sourceY && y < sourceBottom)
@ -62,10 +66,10 @@ namespace ImageProcessorCore.Processors
offsetX = offsetX.Clamp(0, maxX); offsetX = offsetX.Clamp(0, maxX);
Color currentColor = sourcePixels[offsetX, offsetY]; Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.R; float r = currentColor.X;
float g = currentColor.G; float g = currentColor.Y;
float b = currentColor.B; float b = currentColor.Z;
rX += kernelX[fy, fx] * r; rX += kernelX[fy, fx] * r;
gX += kernelX[fy, fx] * g; gX += kernelX[fy, fx] * g;
@ -77,7 +81,11 @@ namespace ImageProcessorCore.Processors
float green = gX; float green = gX;
float blue = bX; float blue = bX;
targetPixels[x, y] = new Color(red, green, blue); Vector4 targetColor = targetPixels[x, y].ToVector4();
T packed = default(T);
packed.PackVector(new Vector4(red, green, blue, targetColor.Z));
targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();
} }

12
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs

@ -9,17 +9,19 @@ namespace ImageProcessorCore.Processors
/// Defines a filter that detects edges within an image using two /// Defines a filter that detects edges within an image using two
/// one-dimensional matrices. /// one-dimensional matrices.
/// </summary> /// </summary>
public abstract class EdgeDetector2DFilter : Convolution2DFilter, IEdgeDetectorFilter public abstract class EdgeDetector2DFilter<T, TP> : Convolution2DFilter<T, TP>, IEdgeDetectorFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public bool Greyscale { get; set; } public bool Grayscale { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
if (this.Greyscale) if (this.Grayscale)
{ {
new GreyscaleBt709Processor().Apply(source, source, sourceRectangle); new GrayscaleBt709Processor<T, TP>().Apply(source, source, sourceRectangle);
} }
} }
} }

12
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs

@ -9,17 +9,19 @@ namespace ImageProcessorCore.Processors
/// Defines a filter that detects edges within an image using a single /// Defines a filter that detects edges within an image using a single
/// two dimensional matrix. /// two dimensional matrix.
/// </summary> /// </summary>
public abstract class EdgeDetectorFilter : ConvolutionFilter, IEdgeDetectorFilter public abstract class EdgeDetectorFilter<T, TP> : ConvolutionFilter<T, TP>, IEdgeDetectorFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public bool Greyscale { get; set; } public bool Grayscale { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
if (this.Greyscale) if (this.Grayscale)
{ {
new GreyscaleBt709Processor().Apply(source, source, sourceRectangle); new GrayscaleBt709Processor<T, TP>().Apply(source, source, sourceRectangle);
} }
} }
} }

15
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs

@ -8,12 +8,21 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Provides properties and methods allowing the detection of edges within an image. /// Provides properties and methods allowing the detection of edges within an image.
/// </summary> /// </summary>
public interface IEdgeDetectorFilter : IImageProcessor public interface IEdgeDetectorFilter<T, TP> : IImageProcessor<T, TP>, IEdgeDetectorFilter
where T : IPackedVector<TP>
where TP : struct
{
}
/// <summary>
/// Provides properties and methods allowing the detection of edges within an image.
/// </summary>
public interface IEdgeDetectorFilter
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether to convert the /// Gets or sets a value indicating whether to convert the
/// image to greyscale before performing edge detection. /// image to Grayscale before performing edge detection.
/// </summary> /// </summary>
bool Greyscale { get; set; } bool Grayscale { get; set; }
} }
} }

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

@ -9,7 +9,11 @@ namespace ImageProcessorCore.Processors
/// The Kayyali operator filter. /// The Kayyali operator filter.
/// <see href="http://edgedetection.webs.com/"/> /// <see href="http://edgedetection.webs.com/"/>
/// </summary> /// </summary>
public class KayyaliProcessor : EdgeDetector2DFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class KayyaliProcessor<T, TP> : EdgeDetector2DFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override float[,] KernelX => new float[,] public override float[,] KernelX => new float[,]

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

@ -9,7 +9,11 @@ namespace ImageProcessorCore.Processors
/// The Kirsch operator filter. /// The Kirsch operator filter.
/// <see href="http://en.wikipedia.org/wiki/Kirsch_operator"/> /// <see href="http://en.wikipedia.org/wiki/Kirsch_operator"/>
/// </summary> /// </summary>
public class KirschProcessor : EdgeDetector2DFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class KirschProcessor<T, TP> : EdgeDetector2DFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override float[,] KernelX => new float[,] public override float[,] KernelX => new float[,]

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

@ -9,7 +9,11 @@ namespace ImageProcessorCore.Processors
/// The Laplacian 3 x 3 operator filter. /// The Laplacian 3 x 3 operator filter.
/// <see href="http://en.wikipedia.org/wiki/Discrete_Laplace_operator"/> /// <see href="http://en.wikipedia.org/wiki/Discrete_Laplace_operator"/>
/// </summary> /// </summary>
public class Laplacian3X3Processor : EdgeDetectorFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class Laplacian3X3Processor<T, TP> : EdgeDetectorFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override float[,] KernelXY => new float[,] public override float[,] KernelXY => new float[,]

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

@ -9,7 +9,11 @@ namespace ImageProcessorCore.Processors
/// The Laplacian 5 x 5 operator filter. /// The Laplacian 5 x 5 operator filter.
/// <see href="http://en.wikipedia.org/wiki/Discrete_Laplace_operator"/> /// <see href="http://en.wikipedia.org/wiki/Discrete_Laplace_operator"/>
/// </summary> /// </summary>
public class Laplacian5X5Processor : EdgeDetectorFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class Laplacian5X5Processor<T, TP> : EdgeDetectorFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override float[,] KernelXY => new float[,] public override float[,] KernelXY => new float[,]

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

@ -9,7 +9,11 @@ namespace ImageProcessorCore.Processors
/// The Laplacian of Gaussian operator filter. /// The Laplacian of Gaussian operator filter.
/// <see href="http://fourier.eng.hmc.edu/e161/lectures/gradient/node8.html"/> /// <see href="http://fourier.eng.hmc.edu/e161/lectures/gradient/node8.html"/>
/// </summary> /// </summary>
public class LaplacianOfGaussianProcessor : EdgeDetectorFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class LaplacianOfGaussianProcessor<T, TP> : EdgeDetectorFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override float[,] KernelXY => new float[,] public override float[,] KernelXY => new float[,]

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

@ -9,7 +9,11 @@ namespace ImageProcessorCore.Processors
/// The Prewitt operator filter. /// The Prewitt operator filter.
/// <see href="http://en.wikipedia.org/wiki/Prewitt_operator"/> /// <see href="http://en.wikipedia.org/wiki/Prewitt_operator"/>
/// </summary> /// </summary>
public class PrewittProcessor : EdgeDetector2DFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class PrewittProcessor<T, TP> : EdgeDetector2DFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override float[,] KernelX => new float[,] public override float[,] KernelX => new float[,]

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

@ -9,7 +9,11 @@ namespace ImageProcessorCore.Processors
/// The Roberts Cross operator filter. /// The Roberts Cross operator filter.
/// <see href="http://en.wikipedia.org/wiki/Roberts_cross"/> /// <see href="http://en.wikipedia.org/wiki/Roberts_cross"/>
/// </summary> /// </summary>
public class RobertsCrossProcessor : EdgeDetector2DFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class RobertsCrossProcessor<T, TP> : EdgeDetector2DFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override float[,] KernelX => new float[,] public override float[,] KernelX => new float[,]

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

@ -9,7 +9,11 @@ namespace ImageProcessorCore.Processors
/// The Scharr operator filter. /// The Scharr operator filter.
/// <see href="http://en.wikipedia.org/wiki/Sobel_operator#Alternative_operators"/> /// <see href="http://en.wikipedia.org/wiki/Sobel_operator#Alternative_operators"/>
/// </summary> /// </summary>
public class ScharrProcessor : EdgeDetector2DFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class ScharrProcessor<T, TP> : EdgeDetector2DFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override float[,] KernelX => new float[,] public override float[,] KernelX => new float[,]

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

@ -9,7 +9,11 @@ namespace ImageProcessorCore.Processors
/// The Sobel operator filter. /// The Sobel operator filter.
/// <see href="http://en.wikipedia.org/wiki/Sobel_operator"/> /// <see href="http://en.wikipedia.org/wiki/Sobel_operator"/>
/// </summary> /// </summary>
public class SobelProcessor : EdgeDetector2DFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class SobelProcessor<T, TP> : EdgeDetector2DFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override float[,] KernelX => new float[,] public override float[,] KernelX => new float[,]

8
src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Applies a Gaussian blur filter to the image. /// Applies a Gaussian blur filter to the image.
/// </summary> /// </summary>
public class GuassianBlurProcessor : Convolution2PassFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class GuassianBlurProcessor<T, TP> : Convolution2PassFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// The maximum size of the kernal in either direction. /// The maximum size of the kernal in either direction.
@ -77,7 +81,7 @@ namespace ImageProcessorCore.Processors
public override float[,] KernelY => this.kernelY; public override float[,] KernelY => this.kernelY;
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
if (this.kernelY == null) if (this.kernelY == null)
{ {

8
src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Applies a Gaussian sharpening filter to the image. /// Applies a Gaussian sharpening filter to the image.
/// </summary> /// </summary>
public class GuassianSharpenProcessor : Convolution2PassFilter /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class GuassianSharpenProcessor<T, TP> : Convolution2PassFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// The maximum size of the kernal in either direction. /// The maximum size of the kernal in either direction.
@ -79,7 +83,7 @@ namespace ImageProcessorCore.Processors
public override float[,] KernelY => this.kernelY; public override float[,] KernelY => this.kernelY;
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
if (this.kernelY == null) if (this.kernelY == null)
{ {

33
src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs

@ -12,12 +12,24 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Creates a glow effect on the image /// Creates a glow effect on the image
/// </summary> /// </summary>
public class GlowProcessor : ImageProcessor /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class GlowProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary>
/// Initializes a new instance of the <see cref="GlowProcessor"/> class.
/// </summary>
public GlowProcessor()
{
this.GlowColor.PackVector(Color.White.ToVector4());
}
/// <summary> /// <summary>
/// Gets or sets the glow color to apply. /// Gets or sets the glow color to apply.
/// </summary> /// </summary>
public Color Color { get; set; } = Color.White; public T GlowColor { get; set; }
/// <summary> /// <summary>
/// Gets or sets the the x-radius. /// Gets or sets the the x-radius.
@ -30,29 +42,34 @@ namespace ImageProcessorCore.Processors
public float RadiusY { get; set; } public float RadiusY { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
int startX = sourceRectangle.X; int startX = sourceRectangle.X;
int endX = sourceRectangle.Right; int endX = sourceRectangle.Right;
Color glowColor = this.Color; T glowColor = this.GlowColor;
Vector2 centre = Rectangle.Center(targetRectangle).ToVector2(); Vector2 centre = Rectangle.Center(targetRectangle).ToVector2();
float rX = this.RadiusX > 0 ? this.RadiusX : targetRectangle.Width / 2f; float rX = this.RadiusX > 0 ? this.RadiusX : targetRectangle.Width / 2f;
float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f; float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f;
float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY); float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY);
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
// TODO: Premultiply?
float distance = Vector2.Distance(centre, new Vector2(x, y)); float distance = Vector2.Distance(centre, new Vector2(x, y));
Color sourceColor = sourcePixels[x, y]; Vector4 sourceColor = sourcePixels[x, y].ToVector4();
targetPixels[x, y] = Color.Lerp(glowColor, sourceColor, .5f * (distance / maxDistance)); Vector4 result = Vector4.Lerp(glowColor.ToVector4(), sourceColor, .5f * (distance / maxDistance));
T packed = default(T);
packed.PackVector(result);
targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();

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

@ -9,12 +9,14 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>. /// An <see cref="IImageProcessor{T,TP}"/> to invert the colors of an <see cref="Image"/>.
/// </summary> /// </summary>
public class InvertProcessor : ImageProcessor public class InvertProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
int sourceY = sourceRectangle.Y; int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom; int sourceBottom = sourceRectangle.Bottom;
@ -22,21 +24,25 @@ namespace ImageProcessorCore.Processors
int endX = sourceRectangle.Right; int endX = sourceRectangle.Right;
Vector3 inverseVector = Vector3.One; Vector3 inverseVector = Vector3.One;
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
if (y >= sourceY && y < sourceBottom) if (y >= sourceY && y < sourceBottom)
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
Color color = sourcePixels[x, y]; Vector4 color = sourcePixels[x, y].ToVector4();
Vector3 vector = inverseVector - color.ToVector3(); Vector3 vector = inverseVector - new Vector3(color.X, color.Y, color.Z);
targetPixels[x, y] = new Color(vector, color.A);
T packed = default(T);
packed.PackVector(new Vector4(vector, color.W));
targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();
@ -46,3 +52,4 @@ namespace ImageProcessorCore.Processors
} }
} }
} }

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

@ -5,20 +5,23 @@
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>. /// An <see cref="IImageProcessor{T,TP}"/> to invert the colors of an <see cref="Image{T,TP}"/>.
/// </summary> /// </summary>
public class PixelateProcessor : ImageProcessor /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class PixelateProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelateProcessor"/> class. /// Initializes a new instance of the <see cref="PixelateProcessor{T,TP}"/> class.
/// </summary> /// </summary>
/// <param name="size">The size of the pixels. Must be greater than 0.</param> /// <param name="size">The size of the pixels. Must be greater than 0.</param>
/// <exception cref="ArgumentException"> /// <exception cref="System.ArgumentException">
/// <paramref name="size"/> is less than 0 or equal to 0. /// <paramref name="size"/> is less than 0 or equal to 0.
/// </exception> /// </exception>
public PixelateProcessor(int size) public PixelateProcessor(int size)
@ -33,7 +36,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; } public int Value { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
int sourceY = sourceRectangle.Y; int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom; int sourceBottom = sourceRectangle.Bottom;
@ -45,8 +48,8 @@ namespace ImageProcessorCore.Processors
// Get the range on the y-plane to choose from. // Get the range on the y-plane to choose from.
IEnumerable<int> range = EnumerableExtensions.SteppedRange(startY, i => i < endY, size); IEnumerable<int> range = EnumerableExtensions.SteppedRange(startY, i => i < endY, size);
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.ForEach( Parallel.ForEach(
range, range,
@ -73,7 +76,7 @@ namespace ImageProcessorCore.Processors
// Get the pixel color in the centre of the soon to be pixelated area. // Get the pixel color in the centre of the soon to be pixelated area.
// ReSharper disable AccessToDisposedClosure // ReSharper disable AccessToDisposedClosure
Color pixel = sourcePixels[x + offsetX, y + offsetY]; T pixel = sourcePixels[x + offsetX, y + offsetY];
// For each pixel in the pixelate size, set it to the centre color. // 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 l = y; l < y + size && l < sourceBottom; l++)

33
src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs

@ -12,12 +12,24 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Creates a vignette effect on the image /// Creates a vignette effect on the image
/// </summary> /// </summary>
public class VignetteProcessor : ImageProcessor /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class VignetteProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary>
/// Initializes a new instance of the <see cref="VignetteProcessor"/> class.
/// </summary>
public VignetteProcessor()
{
this.VignetteColor.PackVector(Color.Black.ToVector4());
}
/// <summary> /// <summary>
/// Gets or sets the vignette color to apply. /// Gets or sets the vignette color to apply.
/// </summary> /// </summary>
public Color Color { get; set; } = Color.Black; public T VignetteColor { get; set; }
/// <summary> /// <summary>
/// Gets or sets the the x-radius. /// Gets or sets the the x-radius.
@ -30,29 +42,34 @@ namespace ImageProcessorCore.Processors
public float RadiusY { get; set; } public float RadiusY { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
int startX = sourceRectangle.X; int startX = sourceRectangle.X;
int endX = sourceRectangle.Right; int endX = sourceRectangle.Right;
Color vignetteColor = this.Color; T vignetteColor = this.VignetteColor;
Vector2 centre = Rectangle.Center(targetRectangle).ToVector2(); Vector2 centre = Rectangle.Center(targetRectangle).ToVector2();
float rX = this.RadiusX > 0 ? this.RadiusX : targetRectangle.Width / 2f; float rX = this.RadiusX > 0 ? this.RadiusX : targetRectangle.Width / 2f;
float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f; float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f;
float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY); float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY);
using (PixelAccessor sourcePixels = source.Lock()) using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
float distance = Vector2.Distance(centre, new Vector2(x, y)); float distance = Vector2.Distance(centre, new Vector2(x, y));
Color sourceColor = sourcePixels[x, y]; Vector4 sourceColor = sourcePixels[x, y].ToVector4();
targetPixels[x, y] = Color.Lerp(vignetteColor, sourceColor, 1 - .9f * distance / maxDistance); Vector4 result = Vector4.Lerp(vignetteColor.ToVector4(), sourceColor, 1 - .9f * (distance / maxDistance));
T packed = default(T);
packed.PackVector(result);
targetPixels[x, y] = packed;
} }
this.OnRowProcessed(); this.OnRowProcessed();
}); });

22
src/ImageProcessorCore/Filters/Saturation.cs

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

18
src/ImageProcessorCore/Filters/Sepia.cs

@ -1,24 +1,28 @@
// <copyright file="Sepia.cs" company="James Jackson-South"> // <copyright file="Sepia.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Applies sepia toning to the image. /// Applies sepia toning to the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <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> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Sepia(this Image source, ProgressEventHandler progressHandler = null) public static Image<T, TP> Sepia<T, TP>(this Image<T, TP> source, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
return Sepia(source, source.Bounds, progressHandler); return Sepia(source, source.Bounds, progressHandler);
} }
@ -26,15 +30,19 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Applies sepia toning to the image. /// Applies sepia toning to the image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image"/>.</returns>
public static Image Sepia(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null) public static Image<T, TP> Sepia<T, TP>(this Image<T, TP> source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{ {
SepiaProcessor processor = new SepiaProcessor(); SepiaProcessor<T, TP> processor = new SepiaProcessor<T, TP>();
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

13
src/ImageProcessorCore/Formats/Bmp/BmpDecoder.cs

@ -69,13 +69,14 @@ namespace ImageProcessorCore.Formats
return isBmp; return isBmp;
} }
/// <summary> /// <inheritdoc/>
/// Decodes the image from the specified stream to the <see cref="ImageBase"/>. public void Decode<T, TP>(Image<T, TP> image, Stream stream)
/// </summary> where T : IPackedVector<TP>
/// <param name="image">The <see cref="ImageBase"/> to decode to.</param> where TP : struct
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public void Decode(Image image, Stream stream)
{ {
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
new BmpDecoderCore().Decode(image, stream); new BmpDecoderCore().Decode(image, stream);
} }
} }

127
src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs

@ -49,16 +49,20 @@ namespace ImageProcessorCore.Formats
/// Decodes the image from the specified this._stream and sets /// Decodes the image from the specified this._stream and sets
/// the data to image. /// the data to image.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The image, where the data should be set to. /// <param name="image">The image, where the data should be set to.
/// Cannot be null (Nothing in Visual Basic).</param> /// Cannot be null (Nothing in Visual Basic).</param>
/// <param name="stream">The this._stream, where the image should be /// <param name="stream">The stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).</param> /// decoded from. Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="ArgumentNullException"> /// <exception cref="ArgumentNullException">
/// <para><paramref name="image"/> is null.</para> /// <para><paramref name="image"/> is null.</para>
/// <para>- or -</para> /// <para>- or -</para>
/// <para><paramref name="stream"/> is null.</para> /// <para><paramref name="stream"/> is null.</para>
/// </exception> /// </exception>
public void Decode(Image image, Stream stream) public void Decode<T, TP>(Image<T, TP> image, Stream stream)
where T : IPackedVector<TP>
where TP : struct
{ {
this.currentStream = stream; this.currentStream = stream;
@ -110,14 +114,14 @@ namespace ImageProcessorCore.Formats
this.currentStream.Read(palette, 0, colorMapSize); this.currentStream.Read(palette, 0, colorMapSize);
} }
if (this.infoHeader.Width > ImageBase.MaxWidth || this.infoHeader.Height > ImageBase.MaxHeight) if (this.infoHeader.Width > image.MaxWidth || this.infoHeader.Height > image.MaxHeight)
{ {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(
$"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is " $"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'"); + $"bigger then the max allowed size '{image.MaxWidth}x{image.MaxHeight}'");
} }
float[] imageData = new float[this.infoHeader.Width * this.infoHeader.Height * 4]; T[] imageData = new T[this.infoHeader.Width * this.infoHeader.Height];
switch (this.infoHeader.Compression) switch (this.infoHeader.Compression)
{ {
@ -130,25 +134,19 @@ namespace ImageProcessorCore.Formats
if (this.infoHeader.BitsPerPixel == 32) if (this.infoHeader.BitsPerPixel == 32)
{ {
this.ReadRgb32(imageData, this.infoHeader.Width, this.infoHeader.Height, inverted); this.ReadRgb32<T, TP>(imageData, this.infoHeader.Width, this.infoHeader.Height, inverted);
} }
else if (this.infoHeader.BitsPerPixel == 24) else if (this.infoHeader.BitsPerPixel == 24)
{ {
this.ReadRgb24(imageData, this.infoHeader.Width, this.infoHeader.Height, inverted); this.ReadRgb24<T, TP>(imageData, this.infoHeader.Width, this.infoHeader.Height, inverted);
} }
else if (this.infoHeader.BitsPerPixel == 16) else if (this.infoHeader.BitsPerPixel == 16)
{ {
this.ReadRgb16(imageData, this.infoHeader.Width, this.infoHeader.Height, inverted); this.ReadRgb16<T, TP>(imageData, this.infoHeader.Width, this.infoHeader.Height, inverted);
} }
else if (this.infoHeader.BitsPerPixel <= 8) else if (this.infoHeader.BitsPerPixel <= 8)
{ {
this.ReadRgbPalette( this.ReadRgbPalette<T, TP>(imageData, palette, this.infoHeader.Width, this.infoHeader.Height, this.infoHeader.BitsPerPixel, inverted);
imageData,
palette,
this.infoHeader.Width,
this.infoHeader.Height,
this.infoHeader.BitsPerPixel,
inverted);
} }
break; break;
@ -169,6 +167,7 @@ namespace ImageProcessorCore.Formats
/// </summary> /// </summary>
/// <param name="y">The y- value representing the current row.</param> /// <param name="y">The y- value representing the current row.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
/// <returns>The <see cref="int"/> representing the inverted value.</returns> /// <returns>The <see cref="int"/> representing the inverted value.</returns>
private static int Invert(int y, int height, bool inverted) private static int Invert(int y, int height, bool inverted)
{ {
@ -189,12 +188,17 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Reads the color palette from the stream. /// Reads the color palette from the stream.
/// </summary> /// </summary>
/// <param name="imageData">The <see cref="T:float[]"/> image data to assign the palette to.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="imageData">The <see cref="T:T[]"/> image data to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param> /// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="bits">The number of bits per pixel.</param> /// <param name="bits">The number of bits per pixel.</param>
private void ReadRgbPalette(float[] imageData, byte[] colors, int width, int height, int bits, bool inverted) /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgbPalette<T, TP>(T[] imageData, byte[] colors, int width, int height, int bits, bool inverted)
where T : IPackedVector<TP>
where TP : struct
{ {
// Pixels per byte (bits per pixel) // Pixels per byte (bits per pixel)
int ppb = 8 / bits; int ppb = 8 / bits;
@ -218,6 +222,7 @@ namespace ImageProcessorCore.Formats
Parallel.For( Parallel.For(
0, 0,
height, height,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
int rowOffset = y * (arrayWidth + alignment); int rowOffset = y * (arrayWidth + alignment);
@ -234,14 +239,12 @@ namespace ImageProcessorCore.Formats
for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++) for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++)
{ {
int colorIndex = ((data[offset] >> (8 - bits - (shift * bits))) & mask) * 4; int colorIndex = ((data[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
int arrayOffset = ((row * width) + (colOffset + shift)) * 4; int arrayOffset = (row * width) + (colOffset + shift);
// We divide by 255 as we will store the colors in our floating point format. // Stored in b-> g-> r order.
// Stored in r-> g-> b-> a order. T packed = default(T);
imageData[arrayOffset] = colors[colorIndex + 2] / 255f; // r packed.PackBytes(colors[colorIndex + 2], colors[colorIndex + 1], colors[colorIndex], 255);
imageData[arrayOffset + 1] = colors[colorIndex + 1] / 255f; // g imageData[arrayOffset] = packed;
imageData[arrayOffset + 2] = colors[colorIndex] / 255f; // b
imageData[arrayOffset + 3] = 1; // a
} }
} }
}); });
@ -250,14 +253,19 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Reads the 16 bit color palette from the stream /// Reads the 16 bit color palette from the stream
/// </summary> /// </summary>
/// <param name="imageData">The <see cref="T:float[]"/> image data to assign the palette to.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="imageData">The <see cref="T:T[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
private void ReadRgb16(float[] imageData, int width, int height, bool inverted) /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb16<T, TP>(T[] imageData, int width, int height, bool inverted)
where T : IPackedVector<TP>
where TP : struct
{ {
// We divide here as we will store the colors in our floating point format. // We divide here as we will store the colors in our floating point format.
const float ScaleR = 0.25F; // (256 / 32) / 32 const int ScaleR = 8; // 256/32
const float ScaleG = 0.0625F; // (256 / 64) / 64 const int ScaleG = 4; // 256/64
int alignment; int alignment;
byte[] data = this.GetImageArray(width, height, 2, out alignment); byte[] data = this.GetImageArray(width, height, 2, out alignment);
@ -265,6 +273,7 @@ namespace ImageProcessorCore.Formats
Parallel.For( Parallel.For(
0, 0,
height, height,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
int rowOffset = y * ((width * 2) + alignment); int rowOffset = y * ((width * 2) + alignment);
@ -278,17 +287,16 @@ namespace ImageProcessorCore.Formats
short temp = BitConverter.ToInt16(data, offset); short temp = BitConverter.ToInt16(data, offset);
float r = ((temp & Rgb16RMask) >> 11) * ScaleR; byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR);
float g = ((temp & Rgb16GMask) >> 5) * ScaleG; byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG);
float b = (temp & Rgb16BMask) * ScaleR; byte b = (byte)((temp & Rgb16BMask) * ScaleR);
int arrayOffset = ((row * width) + x) * 4; int arrayOffset = ((row * width) + x);
// Stored in r-> g-> b-> a order. // Stored in b-> g-> r order.
imageData[arrayOffset] = r; T packed = default(T);
imageData[arrayOffset + 1] = g; packed.PackBytes(r, g, b, 255);
imageData[arrayOffset + 2] = b; imageData[arrayOffset] = packed;
imageData[arrayOffset + 3] = 1;
} }
}); });
} }
@ -296,10 +304,15 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Reads the 24 bit color palette from the stream /// Reads the 24 bit color palette from the stream
/// </summary> /// </summary>
/// <param name="imageData">The <see cref="T:float[]"/> image data to assign the palette to.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="imageData">The <see cref="T:T[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
private void ReadRgb24(float[] imageData, int width, int height, bool inverted) /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb24<T, TP>(T[] imageData, int width, int height, bool inverted)
where T : IPackedVector<TP>
where TP : struct
{ {
int alignment; int alignment;
byte[] data = this.GetImageArray(width, height, 3, out alignment); byte[] data = this.GetImageArray(width, height, 3, out alignment);
@ -307,6 +320,7 @@ namespace ImageProcessorCore.Formats
Parallel.For( Parallel.For(
0, 0,
height, height,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
int rowOffset = y * ((width * 3) + alignment); int rowOffset = y * ((width * 3) + alignment);
@ -317,14 +331,13 @@ namespace ImageProcessorCore.Formats
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
int offset = rowOffset + (x * 3); int offset = rowOffset + (x * 3);
int arrayOffset = ((row * width) + x) * 4; int arrayOffset = ((row * width) + x);
// We divide by 255 as we will store the colors in our floating point format. // We divide by 255 as we will store the colors in our floating point format.
// Stored in r-> g-> b-> a order. // Stored in b-> g-> r-> a order.
imageData[arrayOffset] = data[offset + 2] / 255f; T packed = default(T);
imageData[arrayOffset + 1] = data[offset + 1] / 255f; packed.PackBytes(data[offset + 2], data[offset + 1], data[offset], 255);
imageData[arrayOffset + 2] = data[offset] / 255f; imageData[arrayOffset] = packed;
imageData[arrayOffset + 3] = 1;
} }
}); });
} }
@ -332,10 +345,15 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Reads the 32 bit color palette from the stream /// Reads the 32 bit color palette from the stream
/// </summary> /// </summary>
/// <param name="imageData">The <see cref="T:float[]"/> image data to assign the palette to.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="imageData">The <see cref="T:T[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
private void ReadRgb32(float[] imageData, int width, int height, bool inverted) /// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb32<T, TP>(T[] imageData, int width, int height, bool inverted)
where T : IPackedVector<TP>
where TP : struct
{ {
int alignment; int alignment;
byte[] data = this.GetImageArray(width, height, 4, out alignment); byte[] data = this.GetImageArray(width, height, 4, out alignment);
@ -343,6 +361,7 @@ namespace ImageProcessorCore.Formats
Parallel.For( Parallel.For(
0, 0,
height, height,
Bootstrapper.Instance.ParallelOptions,
y => y =>
{ {
int rowOffset = y * ((width * 4) + alignment); int rowOffset = y * ((width * 4) + alignment);
@ -353,14 +372,12 @@ namespace ImageProcessorCore.Formats
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
int offset = rowOffset + (x * 4); int offset = rowOffset + (x * 4);
int arrayOffset = ((row * width) + x) * 4; int arrayOffset = ((row * width) + x);
// We divide by 255 as we will store the colors in our floating point format. // Stored in b-> g-> r-> a order.
// Stored in r-> g-> b-> a order. T packed = default(T);
imageData[arrayOffset] = data[offset + 2] / 255f; packed.PackBytes(data[offset + 2], data[offset + 1], data[offset], data[offset + 3]);
imageData[arrayOffset + 1] = data[offset + 1] / 255f; imageData[arrayOffset] = packed;
imageData[arrayOffset + 2] = data[offset] / 255f;
imageData[arrayOffset + 3] = 1; // TODO: Can we use our real alpha here?
} }
}); });
} }

4
src/ImageProcessorCore/Formats/Bmp/BmpEncoder.cs

@ -43,7 +43,9 @@ namespace ImageProcessorCore.Formats
} }
/// <inheritdoc/> /// <inheritdoc/>
public void Encode(ImageBase image, Stream stream) public void Encode<T,TP>(ImageBase<T,TP> image, Stream stream)
where T : IPackedVector<TP>
where TP : struct
{ {
BmpEncoderCore encoder = new BmpEncoderCore(); BmpEncoderCore encoder = new BmpEncoderCore();
encoder.Encode(image, stream, this.BitsPerPixel); encoder.Encode(image, stream, this.BitsPerPixel);

60
src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs

@ -8,12 +8,11 @@ namespace ImageProcessorCore.Formats
using System; using System;
using System.IO; using System.IO;
using ImageProcessorCore.IO; using IO;
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream as a Windows bitmap. /// Image encoder for writing an image to a stream as a Windows bitmap.
/// </summary> /// </summary>
/// <remarks>The encoder can currently only write 24-bit rgb images to streams.</remarks>
internal sealed class BmpEncoderCore internal sealed class BmpEncoderCore
{ {
/// <summary> /// <summary>
@ -22,12 +21,16 @@ namespace ImageProcessorCore.Formats
private BmpBitsPerPixel bmpBitsPerPixel; private BmpBitsPerPixel bmpBitsPerPixel;
/// <summary> /// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageBase"/>. /// Encodes the image to the specified stream from the <see cref="ImageBase{T,TP}"/>.
/// </summary> /// </summary>
/// <param name="image">The <see cref="ImageBase"/> to encode from.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The <see cref="ImageBase{T,TP}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="bitsPerPixel">The <see cref="BmpBitsPerPixel"/></param> /// <param name="bitsPerPixel">The <see cref="BmpBitsPerPixel"/></param>
public void Encode(ImageBase image, Stream stream, BmpBitsPerPixel bitsPerPixel) public void Encode<T, TP>(ImageBase<T, TP> image, Stream stream, BmpBitsPerPixel bitsPerPixel)
where T : IPackedVector<TP>
where TP : struct
{ {
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -36,6 +39,7 @@ namespace ImageProcessorCore.Formats
int rowWidth = image.Width; int rowWidth = image.Width;
// TODO: Check this for varying file formats.
int amount = (image.Width * (int)this.bmpBitsPerPixel) % 4; int amount = (image.Width * (int)this.bmpBitsPerPixel) % 4;
if (amount != 0) if (amount != 0)
{ {
@ -117,13 +121,16 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Writes the pixel data to the binary stream. /// Writes the pixel data to the binary stream.
/// </summary> /// </summary>
/// <param name="writer"> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>/// <param name="writer">
/// The <see cref="EndianBinaryWriter"/> containing the stream to write to. /// The <see cref="EndianBinaryWriter"/> containing the stream to write to.
/// </param> /// </param>
/// <param name="image"> /// <param name="image">
/// The <see cref="ImageBase"/> containing pixel data. /// The <see cref="ImageBase{T,TP}"/> containing pixel data.
/// </param> /// </param>
private void WriteImage(EndianBinaryWriter writer, ImageBase image) private void WriteImage<T, TP>(EndianBinaryWriter writer, ImageBase<T, TP> image)
where T : IPackedVector<TP>
where TP : struct
{ {
// TODO: Add more compression formats. // TODO: Add more compression formats.
int amount = (image.Width * (int)this.bmpBitsPerPixel) % 4; int amount = (image.Width * (int)this.bmpBitsPerPixel) % 4;
@ -132,7 +139,7 @@ namespace ImageProcessorCore.Formats
amount = 4 - amount; amount = 4 - amount;
} }
using (PixelAccessor pixels = image.Lock()) using (IPixelAccessor<T, TP> pixels = image.Lock())
{ {
switch (this.bmpBitsPerPixel) switch (this.bmpBitsPerPixel)
{ {
@ -150,22 +157,22 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Writes the 32bit color palette to the stream. /// Writes the 32bit color palette to the stream.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param> /// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor"/> containing pixel data.</param> /// <param name="pixels">The <see cref="IPixelAccessor"/> containing pixel data.</param>
/// <param name="amount">The amount to pad each row by.</param> /// <param name="amount">The amount to pad each row by.</param>
private void Write32bit(EndianBinaryWriter writer, PixelAccessor pixels, int amount) private void Write32bit<T, TP>(EndianBinaryWriter writer, IPixelAccessor<T, TP> pixels, int amount)
where T : IPackedVector<TP>
where TP : struct
{ {
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
{ {
for (int x = 0; x < pixels.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 back to b-> g-> r-> a order.
// Convert to non-premultiplied color. byte[] bytes = pixels[x, y].ToBytes();
Bgra32 color = Color.ToNonPremultiplied(pixels[x, y]); writer.Write(new[] { bytes[2], bytes[1], bytes[0], bytes[3] });
// We can take advantage of BGRA here
writer.Write(color.Bgra);
} }
// Pad // Pad
@ -179,22 +186,21 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Writes the 24bit color palette to the stream. /// Writes the 24bit color palette to the stream.
/// </summary> /// </summary>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor"/> containing pixel data.</param> /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="pixels">The <see cref="IPixelAccessor"/> containing pixel data.</param>
/// <param name="amount">The amount to pad each row by.</param> /// <param name="amount">The amount to pad each row by.</param>
private void Write24bit(EndianBinaryWriter writer, PixelAccessor pixels, int amount) private void Write24bit<T, TP>(EndianBinaryWriter writer, IPixelAccessor<T, TP> pixels, int amount)
where T : IPackedVector<TP>
where TP : struct
{ {
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
{ {
for (int x = 0; x < pixels.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 order.
// Convert back to b-> g-> r-> a order. byte[] bytes = pixels[x, y].ToBytes();
// Convert to non-premultiplied color. writer.Write(new[] { bytes[2], bytes[1], bytes[0] });
Bgra32 color = Color.ToNonPremultiplied(pixels[x, y]);
// Allocate 1 array instead of allocating 3.
writer.Write(new[] { color.B, color.G, color.R });
} }
// Pad // Pad

12
src/ImageProcessorCore/Formats/Gif/GifDecoder.cs

@ -54,14 +54,12 @@ namespace ImageProcessorCore.Formats
header[5] == 0x61; // a header[5] == 0x61; // a
} }
/// <summary> /// <inheritdoc/>
/// Decodes the image from the specified stream to the <see cref="ImageBase"/>. public void Decode<T, TP>(Image<T, TP> image, Stream stream)
/// </summary> where T : IPackedVector<TP>
/// <param name="image">The <see cref="ImageBase"/> to decode to.</param> where TP : struct
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public void Decode(Image image, Stream stream)
{ {
new GifDecoderCore().Decode(image, stream); new GifDecoderCore<T, TP>().Decode(image, stream);
} }
} }
} }

48
src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs

@ -11,12 +11,16 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Performs the gif decoding operation. /// Performs the gif decoding operation.
/// </summary> /// </summary>
internal class GifDecoderCore /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
internal class GifDecoderCore<T, TP>
where T : IPackedVector<TP>
where TP : struct
{ {
/// <summary> /// <summary>
/// The image to decode the information to. /// The image to decode the information to.
/// </summary> /// </summary>
private Image decodedImage; private Image<T, TP> decodedImage;
/// <summary> /// <summary>
/// The currently loaded stream. /// The currently loaded stream.
@ -31,7 +35,7 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// The current frame. /// The current frame.
/// </summary> /// </summary>
private float[] currentFrame; private T[] currentFrame;
/// <summary> /// <summary>
/// The logical screen descriptor. /// The logical screen descriptor.
@ -48,7 +52,7 @@ namespace ImageProcessorCore.Formats
/// </summary> /// </summary>
/// <param name="image">The image to decode to.</param> /// <param name="image">The image to decode to.</param>
/// <param name="stream">The stream containing image data. </param> /// <param name="stream">The stream containing image data. </param>
public void Decode(Image image, Stream stream) public void Decode(Image<T, TP> image, Stream stream)
{ {
this.decodedImage = image; this.decodedImage = image;
@ -175,10 +179,10 @@ namespace ImageProcessorCore.Formats
$"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'"); $"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'");
} }
if (this.logicalScreenDescriptor.Width > ImageBase.MaxWidth || this.logicalScreenDescriptor.Height > ImageBase.MaxHeight) if (this.logicalScreenDescriptor.Width > this.decodedImage.MaxWidth || this.logicalScreenDescriptor.Height > this.decodedImage.MaxHeight)
{ {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(
$"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'"); $"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{this.decodedImage.MaxWidth}x{this.decodedImage.MaxHeight}'");
} }
} }
@ -288,15 +292,15 @@ namespace ImageProcessorCore.Formats
if (this.currentFrame == null) if (this.currentFrame == null)
{ {
this.currentFrame = new float[imageWidth * imageHeight * 4]; this.currentFrame = new T[imageWidth * imageHeight];
} }
float[] lastFrame = null; T[] lastFrame = null;
if (this.graphicsControlExtension != null && if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{ {
lastFrame = new float[imageWidth * imageHeight * 4]; lastFrame = new T[imageWidth * imageHeight];
Array.Copy(this.currentFrame, lastFrame, lastFrame.Length); Array.Copy(this.currentFrame, lastFrame, lastFrame.Length);
} }
@ -345,33 +349,30 @@ namespace ImageProcessorCore.Formats
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++)
{ {
offset = ((writeY * imageWidth) + x) * 4; offset = (writeY * imageWidth) + x;
int index = indices[i]; int index = indices[i];
if (this.graphicsControlExtension == null || if (this.graphicsControlExtension == null ||
this.graphicsControlExtension.TransparencyFlag == false || this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index) this.graphicsControlExtension.TransparencyIndex != index)
{ {
// We divide by 255 as we will store the colors in our floating point format.
// Stored in r-> g-> b-> a order. // Stored in r-> g-> b-> a order.
// Gifs don't store alpha transparency so we don't need to convert to
// premultiplied.
int indexOffset = index * 3; int indexOffset = index * 3;
this.currentFrame[offset + 0] = colorTable[indexOffset] / 255f; // r
this.currentFrame[offset + 1] = colorTable[indexOffset + 1] / 255f; // g T pixel = default(T);
this.currentFrame[offset + 2] = colorTable[indexOffset + 2] / 255f; // b pixel.PackBytes(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2], 255);
this.currentFrame[offset + 3] = 1; // a this.currentFrame[offset] = pixel;
} }
i++; i++;
} }
} }
float[] pixels = new float[imageWidth * imageHeight * 4]; T[] pixels = new T[imageWidth * imageHeight];
Array.Copy(this.currentFrame, pixels, pixels.Length); Array.Copy(this.currentFrame, pixels, pixels.Length);
ImageBase currentImage; ImageBase<T, TP> currentImage;
if (this.decodedImage.Pixels == null) if (this.decodedImage.Pixels == null)
{ {
@ -386,7 +387,7 @@ namespace ImageProcessorCore.Formats
} }
else else
{ {
ImageFrame frame = new ImageFrame(); ImageFrame<T, TP> frame = new ImageFrame<T, TP>();
currentImage = frame; currentImage = frame;
currentImage.SetPixels(imageWidth, imageHeight, pixels); currentImage.SetPixels(imageWidth, imageHeight, pixels);
@ -408,13 +409,10 @@ namespace ImageProcessorCore.Formats
{ {
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++)
{ {
offset = ((y * imageWidth) + x) * 4; offset = (y * imageWidth) + x;
// Stored in r-> g-> b-> a order. // Stored in r-> g-> b-> a order.
this.currentFrame[offset] = 0; this.currentFrame[offset] = default(T);
this.currentFrame[offset + 1] = 0;
this.currentFrame[offset + 2] = 0;
this.currentFrame[offset + 3] = 0;
} }
} }
} }

4
src/ImageProcessorCore/Formats/Gif/GifEncoder.cs

@ -47,7 +47,9 @@ namespace ImageProcessorCore.Formats
} }
/// <inheritdoc/> /// <inheritdoc/>
public void Encode(ImageBase image, Stream stream) public void Encode<T,TP>(ImageBase<T,TP> image, Stream stream)
where T : IPackedVector<TP>
where TP : struct
{ {
GifEncoderCore encoder = new GifEncoderCore GifEncoderCore encoder = new GifEncoderCore
{ {

72
src/ImageProcessorCore/Formats/Gif/GifEncoderCore.cs

@ -10,8 +10,8 @@ namespace ImageProcessorCore.Formats
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using ImageProcessorCore.IO; using IO;
using ImageProcessorCore.Quantizers; using Quantizers;
/// <summary> /// <summary>
/// Performs the gif encoding operation. /// Performs the gif encoding operation.
@ -40,20 +40,24 @@ namespace ImageProcessorCore.Formats
public IQuantizer Quantizer { get; set; } public IQuantizer Quantizer { get; set; }
/// <summary> /// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageBase"/>. /// Encodes the image to the specified stream from the <see cref="ImageBase{T,TP}"/>.
/// </summary> /// </summary>
/// <param name="imageBase">The <see cref="ImageBase"/> to encode from.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="imageBase">The <see cref="ImageBase{T,TP}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public void Encode(ImageBase imageBase, Stream stream) public void Encode<T, TP>(ImageBase<T, TP> imageBase, Stream stream)
where T : IPackedVector<TP>
where TP : struct
{ {
Guard.NotNull(imageBase, nameof(imageBase)); Guard.NotNull(imageBase, nameof(imageBase));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
Image image = (Image)imageBase; Image<T, TP> image = (Image<T, TP>)imageBase;
if (this.Quantizer == null) if (this.Quantizer == null)
{ {
this.Quantizer = new OctreeQuantizer { Threshold = this.Threshold }; this.Quantizer = new OctreeQuantizer<T, TP> { Threshold = this.Threshold };
} }
// Do not use IDisposable pattern here as we want to preserve the stream. // Do not use IDisposable pattern here as we want to preserve the stream.
@ -67,7 +71,7 @@ namespace ImageProcessorCore.Formats
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(this.Quality); this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(this.Quality);
// Quantize the image returning a palette. // Quantize the image returning a palette.
QuantizedImage quantized = this.Quantizer.Quantize(image, this.Quality); QuantizedImage<T, TP> quantized = ((IQuantizer<T, TP>)this.Quantizer).Quantize(image, this.Quality);
// Write the header. // Write the header.
this.WriteHeader(writer); this.WriteHeader(writer);
@ -85,9 +89,9 @@ namespace ImageProcessorCore.Formats
if (image.Frames.Any()) if (image.Frames.Any())
{ {
this.WriteApplicationExtension(writer, image.RepeatCount, image.Frames.Count); this.WriteApplicationExtension(writer, image.RepeatCount, image.Frames.Count);
foreach (ImageFrame frame in image.Frames) foreach (ImageFrame<T, TP> frame in image.Frames)
{ {
QuantizedImage quantizedFrame = this.Quantizer.Quantize(frame, this.Quality); QuantizedImage<T, TP> quantizedFrame = ((IQuantizer<T, TP>)this.Quantizer).Quantize(frame, this.Quality);
this.WriteGraphicalControlExtension(frame, writer, quantizedFrame.TransparentIndex); this.WriteGraphicalControlExtension(frame, writer, quantizedFrame.TransparentIndex);
this.WriteImageDescriptor(frame, writer); this.WriteImageDescriptor(frame, writer);
this.WriteColorTable(quantizedFrame, writer); this.WriteColorTable(quantizedFrame, writer);
@ -111,10 +115,14 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Writes the logical screen descriptor to the stream. /// Writes the logical screen descriptor to the stream.
/// </summary> /// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The image to encode.</param> /// <param name="image">The image to encode.</param>
/// <param name="writer">The writer to write to the stream with.</param> /// <param name="writer">The writer to write to the stream with.</param>
/// <param name="tranparencyIndex">The transparency index to set the default backgound index to.</param> /// <param name="tranparencyIndex">The transparency index to set the default backgound index to.</param>
private void WriteLogicalScreenDescriptor(Image image, EndianBinaryWriter writer, int tranparencyIndex) private void WriteLogicalScreenDescriptor<T, TP>(Image<T, TP> image, EndianBinaryWriter writer, int tranparencyIndex)
where T : IPackedVector<TP>
where TP : struct
{ {
GifLogicalScreenDescriptor descriptor = new GifLogicalScreenDescriptor GifLogicalScreenDescriptor descriptor = new GifLogicalScreenDescriptor
{ {
@ -180,10 +188,14 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Writes the graphics control extension to the stream. /// Writes the graphics control extension to the stream.
/// </summary> /// </summary>
/// <param name="image">The <see cref="ImageBase"/> to encode.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The <see cref="ImageBase{T,TP}"/> to encode.</param>
/// <param name="writer">The stream to write to.</param> /// <param name="writer">The stream to write to.</param>
/// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param> /// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param>
private void WriteGraphicalControlExtension(ImageBase image, EndianBinaryWriter writer, int transparencyIndex) private void WriteGraphicalControlExtension<T, TP>(ImageBase<T, TP> image, EndianBinaryWriter writer, int transparencyIndex)
where T : IPackedVector<TP>
where TP : struct
{ {
// TODO: Check transparency logic. // TODO: Check transparency logic.
bool hasTransparent = transparencyIndex > -1; bool hasTransparent = transparencyIndex > -1;
@ -224,9 +236,13 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Writes the image descriptor to the stream. /// Writes the image descriptor to the stream.
/// </summary> /// </summary>
/// <param name="image">The <see cref="ImageBase"/> to be encoded.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The <see cref="ImageBase{T,TP}"/> to be encoded.</param>
/// <param name="writer">The stream to write to.</param> /// <param name="writer">The stream to write to.</param>
private void WriteImageDescriptor(ImageBase image, EndianBinaryWriter writer) private void WriteImageDescriptor<T, TP>(ImageBase<T, TP> image, EndianBinaryWriter writer)
where T : IPackedVector<TP>
where TP : struct
{ {
writer.Write(GifConstants.ImageDescriptorLabel); // 2c writer.Write(GifConstants.ImageDescriptorLabel); // 2c
// TODO: Can we capture this? // TODO: Can we capture this?
@ -247,12 +263,16 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Writes the color table to the stream. /// Writes the color table to the stream.
/// </summary> /// </summary>
/// <param name="image">The <see cref="ImageBase"/> to encode.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The <see cref="ImageBase{T,TP}"/> to encode.</param>
/// <param name="writer">The writer to write to the stream with.</param> /// <param name="writer">The writer to write to the stream with.</param>
private void WriteColorTable(QuantizedImage image, EndianBinaryWriter writer) private void WriteColorTable<T, TP>(QuantizedImage<T, TP> image, EndianBinaryWriter writer)
where T : IPackedVector<TP>
where TP : struct
{ {
// Grab the palette and write it to the stream. // Grab the palette and write it to the stream.
Bgra32[] palette = image.Palette; T[] palette = image.Palette;
int pixelCount = palette.Length; int pixelCount = palette.Length;
// Get max colors for bit depth. // Get max colors for bit depth.
@ -263,11 +283,11 @@ namespace ImageProcessorCore.Formats
i => i =>
{ {
int offset = i * 3; int offset = i * 3;
Bgra32 color = palette[i]; byte[] color = palette[i].ToBytes();
colorTable[offset] = color.R; colorTable[offset] = color[0];
colorTable[offset + 1] = color.G; colorTable[offset + 1] = color[1];
colorTable[offset + 2] = color.B; colorTable[offset + 2] = color[2];
}); });
writer.Write(colorTable, 0, colorTableLength); writer.Write(colorTable, 0, colorTableLength);
@ -276,9 +296,13 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Writes the image pixel data to the stream. /// Writes the image pixel data to the stream.
/// </summary> /// </summary>
/// <param name="image">The <see cref="QuantizedImage"/> containing indexed pixels.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The <see cref="QuantizedImage{T,TP}"/> containing indexed pixels.</param>
/// <param name="writer">The stream to write to.</param> /// <param name="writer">The stream to write to.</param>
private void WriteImageData(QuantizedImage image, EndianBinaryWriter writer) private void WriteImageData<T, TP>(QuantizedImage<T, TP> image, EndianBinaryWriter writer)
where T : IPackedVector<TP>
where TP : struct
{ {
byte[] indexedPixels = image.Pixels; byte[] indexedPixels = image.Pixels;

10
src/ImageProcessorCore/Formats/IImageDecoder.cs

@ -39,10 +39,14 @@ namespace ImageProcessorCore.Formats
bool IsSupportedFileFormat(byte[] header); bool IsSupportedFileFormat(byte[] header);
/// <summary> /// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase"/>. /// Decodes the image from the specified stream to the <see cref="ImageBase{T,TP}"/>.
/// </summary> /// </summary>
/// <param name="image">The <see cref="ImageBase"/> to decode to.</param> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The <see cref="ImageBase{T,TP}"/> to decode to.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
void Decode(Image image, Stream stream); void Decode<T, TP>(Image<T, TP> image, Stream stream)
where T : IPackedVector<TP>
where TP : struct;
} }
} }

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

Loading…
Cancel
Save