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
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ImageProcessorCore", "src\ImageProcessorCore\ImageProcessorCore.xproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}"
EndProject

3
NuGet.config

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

33
README.md

@ -1,3 +1,4 @@
# ImageProcessorCore
<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?)
- [x] Box
- [x] Bicubic
- [x] Lanczos2
- [x] Lanczos3
- [x] Lanczos5
- [x] Lanczos8
@ -81,7 +83,6 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] Nearest Neighbour
- [x] Robidoux
- [x] Robidoux Sharp
- [x] Robidoux Soft
- [x] Spline
- [x] Triangle
- [x] Welch
@ -100,8 +101,8 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] Skew by x/y angles and center point.
- ColorMatrix operations (Uses Matrix4x4)
- [x] BlackWhite
- [x] Greyscale BT709
- [x] Greyscale BT601
- [x] Grayscale BT709
- [x] Grayscale BT601
- [x] Hue
- [x] Saturation
- [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.
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
using (FileStream stream = File.OpenRead("foo.jpg"))
@ -168,32 +169,12 @@ using (FileStream output = File.OpenWrite("bar.jpg"))
{
Image image = new Image(stream);
image.Resize(image.Width / 2, image.Height / 2)
.Greyscale()
.Grayscale()
.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.
```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:
Individual processors can be initialised and apply processing against images. This allows nesting which brings the potential for powerful combinations of processing methods:
```csharp
new Brightness(50).Apply(sourceImage, targetImage, sourceImage.Bounds);

67
src/ImageProcessorCore/Bootstrapper.cs

@ -8,7 +8,10 @@ namespace ImageProcessorCore
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ImageProcessorCore.Formats;
using System.Reflection;
using System.Threading.Tasks;
using Formats;
/// <summary>
/// Provides initialization code which allows extending the library.
@ -26,18 +29,25 @@ namespace ImageProcessorCore
/// </summary>
private readonly List<IImageFormat> imageFormats;
private readonly Dictionary<Type, Func<IImageBase, IPixelAccessor>> pixelAccessors;
/// <summary>
/// Prevents a default instance of the <see cref="Bootstrapper"/> class from being created.
/// </summary>
private Bootstrapper()
{
this.imageFormats = new List<IImageFormat>(new List<IImageFormat>
this.imageFormats = new List<IImageFormat>
{
new BmpFormat(),
new JpegFormat(),
new PngFormat(),
new GifFormat()
});
};
this.pixelAccessors = new Dictionary<Type, Func<IImageBase, IPixelAccessor>>
{
{ typeof(Color), i=> new ColorPixelAccessor(i) }
};
}
/// <summary>
@ -46,9 +56,21 @@ namespace ImageProcessorCore
public static Bootstrapper Instance = Lazy.Value;
/// <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>
public IReadOnlyCollection<IImageFormat> ImageFormats => new ReadOnlyCollection<IImageFormat>(this.imageFormats);
public ParallelOptions ParallelOptions { get; set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
/// <summary>
/// Adds a new <see cref="IImageFormat"/> to the collection of supported image formats.
@ -58,5 +80,40 @@ namespace ImageProcessorCore
{
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
{
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Represents a four-component color using red, green, blue, and alpha data.
/// Each component is stored in premultiplied format multiplied by the alpha component.
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public partial struct Color : IEquatable<Color>, IAlmostEquatable<Color, float>
[StructLayout(LayoutKind.Explicit)]
public partial struct Color : IPackedVector<uint>, IEquatable<Color>
{
/// <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>
public static readonly Color Empty = default(Color);
[FieldOffset(0)]
public byte R;
/// <summary>
/// The epsilon for comparing floating point numbers.
/// Gets or sets the green component.
/// </summary>
private const float Epsilon = 0.001f;
[FieldOffset(1)]
public byte G;
/// <summary>
/// The backing vector for SIMD support.
/// Gets or sets the red component.
/// </summary>
private Vector4 backingVector;
[FieldOffset(2)]
public byte B;
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// Gets or sets the alpha component.
/// </summary>
/// <param name="r">The red component of this <see cref="Color"/>.</param>
/// <param name="g">The green component of this <see cref="Color"/>.</param>
/// <param name="b">The blue component of this <see cref="Color"/>.</param>
/// <param name="a">The alpha component of this <see cref="Color"/>.</param>
public Color(float r, float g, float b, float a = 1)
[FieldOffset(3)]
public byte A;
/// <summary>
/// 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.backingVector = new Vector4(r, g, b, a);
this.R = r;
this.G = g;
this.B = b;
this.A = a;
}
/// <summary>
@ -68,25 +87,17 @@ namespace ImageProcessorCore
if (hex.Length == 8)
{
float r = Convert.ToByte(hex.Substring(2, 2), 16);
float g = Convert.ToByte(hex.Substring(4, 2), 16);
float b = Convert.ToByte(hex.Substring(6, 2), 16);
float 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);
this.R = Convert.ToByte(hex.Substring(2, 2), 16);
this.G = Convert.ToByte(hex.Substring(4, 2), 16);
this.B = Convert.ToByte(hex.Substring(6, 2), 16);
this.A = Convert.ToByte(hex.Substring(0, 2), 16);
}
else if (hex.Length == 6)
{
float r = Convert.ToByte(hex.Substring(0, 2), 16);
float g = Convert.ToByte(hex.Substring(2, 2), 16);
float b = Convert.ToByte(hex.Substring(4, 2), 16);
float a = 255f;
// Do division of Vector4 instead of each component to utilize SIMD optimizations
this.backingVector = new Vector4(r, g, b, a) / 255f;
this.R = Convert.ToByte(hex.Substring(0, 2), 16);
this.G = Convert.ToByte(hex.Substring(2, 2), 16);
this.B = Convert.ToByte(hex.Substring(4, 2), 16);
this.A = 255;
}
else
{
@ -94,185 +105,60 @@ namespace ImageProcessorCore
string gh = char.ToString(hex[1]);
string bh = char.ToString(hex[2]);
float r = Convert.ToByte(rh + rh, 16);
float g = Convert.ToByte(gh + gh, 16);
float b = Convert.ToByte(bh + bh, 16);
float a = 255f;
this.backingVector = new Vector4(r, g, b, a) / 255f;
this.R = Convert.ToByte(rh + rh, 16);
this.G = Convert.ToByte(gh + gh, 16);
this.B = Convert.ToByte(bh + bh, 16);
this.A = 255;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">The vector.</param>
public Color(Vector4 vector)
/// <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(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>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">
/// The vector representing the red, green, and blue componenets.
/// The vector containing the components for the packed vector.
/// </param>
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>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">
/// The vector representing the red, green, and blue componenets.
/// The vector containing the components for the packed vector.
/// </param>
/// <param name="alpha">The alpha component.</param>
public Color(Vector3 vector, float alpha)
{
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)
public Color(Vector4 vector)
: this()
{
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>
@ -289,230 +175,99 @@ namespace ImageProcessorCore
/// </returns>
public static bool operator ==(Color left, Color right)
{
return left.Equals(right);
return left.packedValue == right.packedValue;
}
/// <summary>
/// Compares two <see cref="Color"/> objects for inequality.
/// Compares two <see cref="Color"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Color"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Color"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Color"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Color"/> on the right side of the operand.</param>
/// <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>
public static bool operator !=(Color left, Color right)
{
return !left.Equals(right);
}
/// <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);
return left.packedValue != right.packedValue;
}
/// <summary>
/// Converts a premultipled alpha <see cref="Color"/> to a <see cref="Color"/>
/// 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)
/// <inheritdoc/>
public uint PackedValue()
{
float a = color.A;
if (Math.Abs(a) < Epsilon)
{
return new Color(color.backingVector);
}
return new Color(color.backingVector / new Vector4(a, a, a, 1));
return this.packedValue;
}
/// <summary>
/// Gets a <see cref="Vector4"/> representation for this <see cref="Color"/>.
/// </summary>
/// <returns>A <see cref="Vector4"/> representation for this object.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
/// <inheritdoc/>
public void PackVector(Vector4 vector)
{
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>
/// Gets a <see cref="Vector3"/> representation for this <see cref="Color"/>.
/// </summary>
/// <returns>A <see cref="Vector3"/> representation for this object.</returns>
public Vector3 ToVector3()
/// <inheritdoc/>
public void PackBytes(byte x, byte y, byte z, byte w)
{
return new Vector3(this.R, this.G, this.B);
this.R = x;
this.G = y;
this.B = z;
this.A = w;
}
/// <inheritdoc/>
public override int GetHashCode()
public Vector4 ToVector4()
{
return GetHashCode(this);
return new Vector4(this.R, this.G, this.B, this.A) / 255F;
}
/// <inheritdoc/>
public override string ToString()
public byte[] ToBytes()
{
if (this.IsEmpty)
{
return "Color [ Empty ]";
}
return $"Color [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##}, A={this.A:#0.##} ]";
return new[] { this.R, this.G, this.B, this.A };
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Color)
{
return this.Equals((Color)obj);
}
return false;
return (obj is Color) && this.Equals((Color)obj);
}
/// <inheritdoc/>
public bool Equals(Color other)
{
return this.AlmostEquals(other, Epsilon);
}
/// <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;
return this.packedValue == other.packedValue;
}
/// <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/"/>
/// Gets a string representation of the packed vector.
/// </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)
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
if (signal <= 0.0031308f)
{
return signal * 12.92f;
}
return (1.055f * (float)Math.Pow(signal, 0.41666666f)) - 0.055f;
return this.ToVector4().ToString();
}
/// <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)
/// <inheritdoc/>
public override int GetHashCode()
{
if (signal <= 0.04045f)
{
return signal / 12.92f;
}
return (float)Math.Pow((signal + 0.055f) / 1.055f, 2.4f);
return this.GetHashCode(this);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="color">
/// <param name="packed">
/// The instance of <see cref="Color"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </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
{
using System;
using System.Numerics;
/// <summary>
/// Represents a four-component color using red, green, blue, and alpha data.
/// Each component is stored in premultiplied format multiplied by the alpha component.
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
/// </summary>
/// <remarks>
/// 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 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>
/// 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>
/// <param name="source">The first color value.</param>
/// <param name="destination">The second color value.</param>
/// <param name="from">The first color value.</param>
/// <param name="to">The second color value.</param>
/// <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>
/// <returns>
/// The <see cref="Color"/>
/// </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);
if (Math.Abs(source.A - 1) < Epsilon && Math.Abs(destination.A - 1) < Epsilon)
{
return source + ((destination - source) * amount);
}
// Premultiplied.
return (source * (1 - amount)) + destination;
return new Color(Vector4.Lerp(from.ToVector4(), to.ToVector4(), amount));
}
}
}

39
src/ImageProcessorCore/Colors/ColorspaceTransforms.cs

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

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

@ -80,8 +80,7 @@ namespace ImageProcessorCore
/// </returns>
public static implicit operator Bgra32(Color color)
{
color = color.Limited * 255f;
return new Bgra32((byte)color.B, (byte)color.G, (byte)color.R, (byte)color.A);
return new Bgra32(color.B, color.G, color.R, color.A);
}
/// <summary>
@ -158,7 +157,7 @@ namespace ImageProcessorCore
/// Returns the hash code for this instance.
/// </summary>
/// <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>
/// <returns>
/// 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"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Bgra32"/> to convert.
/// The instance of <see cref="Color"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="CieLab"/>.
@ -79,11 +79,10 @@ namespace ImageProcessorCore
public static implicit operator CieLab(Color color)
{
// First convert to CIE XYZ
color = Color.Expand(color);
float x = (color.R * 0.4124F) + (color.G * 0.3576F) + (color.B * 0.1805F);
float y = (color.R * 0.2126F) + (color.G * 0.7152F) + (color.B * 0.0722F);
float z = (color.R * 0.0193F) + (color.G * 0.1192F) + (color.B * 0.9505F);
Vector4 vector = color.ToVector4().Expand();
float x = (vector.X * 0.4124F) + (vector.Y * 0.3576F) + (vector.Z * 0.1805F);
float y = (vector.X * 0.2126F) + (vector.Y * 0.7152F) + (vector.Z * 0.0722F);
float z = (vector.X * 0.0193F) + (vector.Y * 0.1192F) + (vector.Z * 0.9505F);
// Now to LAB
x /= 0.95047F;

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

@ -79,11 +79,11 @@ namespace ImageProcessorCore
/// </returns>
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 y = (color.R * 0.2126F) + (color.G * 0.7152F) + (color.B * 0.0722F);
float z = (color.R * 0.0193F) + (color.G * 0.1192F) + (color.B * 0.9505F);
float x = (vector.X * 0.4124F) + (vector.Y * 0.3576F) + (vector.Z * 0.1805F);
float y = (vector.X * 0.2126F) + (vector.Y * 0.7152F) + (vector.Z * 0.0722F);
float z = (vector.X * 0.0193F) + (vector.Y * 0.1192F) + (vector.Z * 0.9505F);
x *= 100F;
y *= 100F;

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

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

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

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

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

@ -22,7 +22,7 @@ namespace ImageProcessorCore
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001f;
private const float Epsilon = 0.001F;
/// <summary>
/// The backing vector for SIMD support.
@ -74,10 +74,9 @@ namespace ImageProcessorCore
/// </returns>
public static implicit operator Hsv(Color color)
{
color = Color.ToNonPremultiplied(color.Limited);
float r = color.R;
float g = color.G;
float b = color.B;
float r = color.R / 255F;
float g = color.G / 255F;
float b = color.B / 255F;
float max = Math.Max(r, Math.Max(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>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001f;
private const float Epsilon = 0.001F;
/// <summary>
/// The backing vector for SIMD support.
@ -79,7 +79,6 @@ namespace ImageProcessorCore
/// </returns>
public static implicit operator YCbCr(Color color)
{
color = Color.ToNonPremultiplied(color.Limited) * 255f;
float r = color.R;
float g = color.G;
float b = color.B;
@ -173,7 +172,7 @@ namespace ImageProcessorCore
/// Returns the hash code for this instance.
/// </summary>
/// <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>
/// <returns>
/// 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
{
/// <summary>
/// The blue component.
/// The red component.
/// </summary>
B = 0,
R = 0,
/// <summary>
/// The green component.
@ -21,9 +21,9 @@ namespace ImageProcessorCore
G = 1,
/// <summary>
/// The red component.
/// The blue component.
/// </summary>
R = 2,
B = 2,
/// <summary>
/// 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;
if (x < 0)
if (x < 0F)
{
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));
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));
return x / 6;
return x / 6F;
}
return 0;
return 0F;
}
/// <summary>
@ -91,7 +91,7 @@ namespace ImageProcessorCore
/// </returns>
public static float SinC(float x)
{
const float Epsilon = .00001f;
const float Epsilon = .00001F;
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
/// than the given one.
/// </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="componentValue">The color component value to remove.</param>
/// <param name="channel">The <see cref="RgbaComponent"/> channel to test against.</param>
/// <returns>
/// The <see cref="Rectangle"/>.
/// </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;
int width = bitmap.Width;
@ -170,29 +174,29 @@ namespace ImageProcessorCore
Point topLeft = 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
switch (channel)
{
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;
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;
case RgbaComponent.A:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].A - b) > Epsilon;
case RgbaComponent.B:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToBytes()[2] - b) > Epsilon;
break;
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;
}
Func<PixelAccessor, int> getMinY = pixels =>
Func<IPixelAccessor<T, TP>, int> getMinY = pixels =>
{
for (int y = 0; y < height; y++)
{
@ -208,7 +212,7 @@ namespace ImageProcessorCore
return 0;
};
Func<PixelAccessor, int> getMaxY = pixels =>
Func<IPixelAccessor<T, TP>, int> getMaxY = pixels =>
{
for (int y = height - 1; y > -1; y--)
{
@ -224,7 +228,7 @@ namespace ImageProcessorCore
return height;
};
Func<PixelAccessor, int> getMinX = pixels =>
Func<IPixelAccessor<T, TP>, int> getMinX = pixels =>
{
for (int x = 0; x < width; x++)
{
@ -240,7 +244,7 @@ namespace ImageProcessorCore
return 0;
};
Func<PixelAccessor, int> getMaxX = pixels =>
Func<IPixelAccessor<T, TP>, int> getMaxX = pixels =>
{
for (int x = width - 1; x > -1; x--)
{
@ -256,7 +260,7 @@ namespace ImageProcessorCore
return height;
};
using (PixelAccessor bitmapPixels = bitmap.Lock())
using (IPixelAccessor<T, TP> bitmapPixels = bitmap.Lock())
{
topLeft.Y = getMinY(bitmapPixels);
topLeft.X = getMinX(bitmapPixels);
@ -276,11 +280,11 @@ namespace ImageProcessorCore
/// </returns>.
private static float Clean(float x)
{
const float Epsilon = .00001f;
const float Epsilon = .00001F;
if (Math.Abs(x) < Epsilon)
{
return 0f;
return 0F;
}
return x;

18
src/ImageProcessorCore/Filters/Alpha.cs

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

16
src/ImageProcessorCore/Filters/BackgroundColor.cs

@ -1,27 +1,31 @@
// <copyright file="BackgroundColor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
// </copyright>
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <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>
/// <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="color">The color to set as the background.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image BackgroundColor(this Image source, Color color, ProgressEventHandler progressHandler = null)
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
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;
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 (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
// </copyright>
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies black and white toning to 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="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image BlackWhite(this Image source, ProgressEventHandler progressHandler = null)
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
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);
}
@ -26,15 +30,19 @@ namespace ImageProcessorCore
/// <summary>
/// Applies black and white toning to 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="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image BlackWhite(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
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;
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.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">
/// The image to blend with the currently processing image.
/// Disposal of this image is the responsibility of the developer.
/// </param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Blend(this Image source, ImageBase image, int percent = 50, ProgressEventHandler progressHandler = null)
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
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);
}
@ -32,19 +33,20 @@ namespace ImageProcessorCore
/// Combines the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">
/// The image to blend with the currently processing image.
/// Disposal of this image is the responsibility of the developer.
/// </param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Blend(this Image source, ImageBase image, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null)
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
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;
try

22
src/ImageProcessorCore/Filters/BoxBlur.cs

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

22
src/ImageProcessorCore/Filters/Brightness.cs

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

38
src/ImageProcessorCore/Filters/ColorBlindness.cs

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

22
src/ImageProcessorCore/Filters/Contrast.cs

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

88
src/ImageProcessorCore/Filters/DetectEdges.cs

@ -1,37 +1,103 @@
// <copyright file="DetectEdges.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
// </copyright>
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Detects any edges within the image. Uses the <see cref="SobelProcessor"/> filter
/// operating in greyscale mode.
/// operating in Grayscale mode.
/// </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="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image DetectEdges(this Image source, ProgressEventHandler progressHandler = null)
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
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>
/// 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="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>
/// <returns>The <see cref="Image"/>.</returns>
public static Image DetectEdges(this Image source, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null)
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
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);
}
@ -45,8 +111,10 @@ namespace ImageProcessorCore
/// </param>
/// <param name="filter">The filter for detecting edges.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null)
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
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;

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.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
// </copyright>
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies greyscale toning to the image.
/// Applies Grayscale toning to 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="mode">The formula to apply to perform the operation.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Greyscale(this Image source, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null)
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
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>
/// Applies greyscale toning to the image.
/// Applies Grayscale toning to 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="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Greyscale(this Image source, Rectangle rectangle, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null)
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
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)new GreyscaleBt709Processor()
: new GreyscaleBt601Processor();
IImageProcessor<T, TP> processor = mode == GrayscaleMode.Bt709
? (IImageProcessor<T, TP>)new GrayscaleBt709Processor<T, TP>()
: new GrayscaleBt601Processor<T, TP>();
processor.OnProgress += progressHandler;

22
src/ImageProcessorCore/Filters/GuassianBlur.cs

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

22
src/ImageProcessorCore/Filters/GuassianSharpen.cs

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

22
src/ImageProcessorCore/Filters/Hue.cs

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

10
src/ImageProcessorCore/Filters/Invert.cs

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

22
src/ImageProcessorCore/Filters/Kodachrome.cs

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

22
src/ImageProcessorCore/Filters/Lomograph.cs

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

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

@ -6,7 +6,7 @@
namespace ImageProcessorCore
{
/// <summary>
/// Enumerates the various types of color blindness.
/// Enumerates the various types of defined color blindness filters.
/// </summary>
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.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
namespace ImageProcessorCore
{
/// <summary>
/// Provides enumeration over the various greyscale methods available.
/// Enumerates the various types of defined Grayscale filters.
/// </summary>
public enum GreyscaleMode
public enum GrayscaleMode
{
/// <summary>
/// ITU-R Recommendation BT.709

26
src/ImageProcessorCore/Filters/Pixelate.cs

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

22
src/ImageProcessorCore/Filters/Polaroid.cs

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

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

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

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

@ -6,12 +6,15 @@
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// Sets the background color of the image.
/// </summary>
public class BackgroundColorProcessor : ImageProcessor
public class BackgroundColorProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{
/// <summary>
/// The epsilon for comparing floating point numbers.
@ -19,46 +22,47 @@ namespace ImageProcessorCore.Processors
private const float Epsilon = 0.001f;
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundColorProcessor"/> class.
/// Initializes a new instance of the <see cref="BackgroundColorProcessor{T,TP}"/> class.
/// </summary>
/// <param name="color">The <see cref="Color"/> to set the background color to.</param>
public BackgroundColorProcessor(Color color)
/// <param name="color">The <see cref="T"/> to set the background color to.</param>
public BackgroundColorProcessor(T color)
{
this.Value = Color.FromNonPremultiplied(color);
this.Value = color;
}
/// <summary>
/// Gets the background color value.
/// </summary>
public Color Value { get; }
public T Value { get; }
/// <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 sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
Color backgroundColor = this.Value;
Vector4 backgroundColor = this.Value.ToVector4();
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
Bootstrapper.Instance.ParallelOptions,
y =>
{
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
{
Color color = sourcePixels[x, y];
float a = color.A;
Vector4 color = sourcePixels[x, y].ToVector4();
float a = color.W;
if (a < 1 && a > 0)
{
color = Color.Lerp(color, backgroundColor, .5f);
color = Vector4.Lerp(color, backgroundColor, .5f);
}
if (Math.Abs(a) < Epsilon)
@ -66,7 +70,9 @@ namespace ImageProcessorCore.Processors
color = backgroundColor;
}
targetPixels[x, y] = color;
T packed = default(T);
packed.PackVector(color);
targetPixels[x, y] = packed;
}
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.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to perform binary threshold filtering against an
/// <see cref="Image"/>. The image will be converted to greyscale before thresholding
/// An <see cref="IImageProcessor{T,TP}"/> to perform binary threshold filtering against an
/// <see cref="Image"/>. The image will be converted to Grayscale before thresholding
/// occurs.
/// </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>
/// Initializes a new instance of the <see cref="ThresholdProcessor"/> class.
@ -22,10 +25,19 @@ namespace ImageProcessorCore.Processors
/// <exception cref="ArgumentException">
/// <paramref name="threshold"/> is less than 0 or is greater than 1.
/// </exception>
public ThresholdProcessor(float threshold)
public BinaryThresholdProcessor(float threshold)
{
// TODO: Check limit.
Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(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>
@ -36,46 +48,50 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// The color to use for pixels that are above the threshold.
/// </summary>
public Color UpperColor => Color.White;
public T UpperColor { get; set; }
/// <summary>
/// The color to use for pixels that fall below the threshold.
/// </summary>
public Color LowerColor => Color.Black;
public T LowerColor { get; set; }
/// <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/>
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;
Color upper = this.UpperColor;
Color lower = this.LowerColor;
T upper = this.UpperColor;
T lower = this.LowerColor;
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
Bootstrapper.Instance.ParallelOptions,
y =>
{
if (y >= sourceY && y < sourceBottom)
{
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.
targetPixels[x, y] = color.B >= threshold ? upper : lower;
// Any channel will do since it's Grayscale.
targetPixels[x, y] = color.ToVector4().X >= threshold ? upper : lower;
}
this.OnRowProcessed();
}

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

@ -5,17 +5,22 @@
namespace ImageProcessorCore.Processors
{
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// Combines two images together by blending the pixels.
/// </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>
/// The image to blend.
/// </summary>
private readonly ImageBase blend;
private readonly ImageBase<T, TP> blend;
/// <summary>
/// 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.
/// </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));
this.blend = image;
@ -38,7 +43,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; }
/// <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 sourceBottom = sourceRectangle.Bottom;
@ -47,9 +52,9 @@ namespace ImageProcessorCore.Processors
Rectangle bounds = this.blend.Bounds;
float alpha = this.Value / 100f;
using (PixelAccessor toBlendPixels = this.blend.Lock())
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> toBlendPixels = this.blend.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
@ -60,21 +65,23 @@ namespace ImageProcessorCore.Processors
{
for (int x = startX; x < endX; x++)
{
Color color = sourcePixels[x, y];
Vector4 color = sourcePixels[x, y].ToVector4();
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
float alphaFactor = alpha > 0 ? alpha : blendedColor.A;
color = Color.Lerp(color, blendedColor, alphaFactor);
float alphaFactor = alpha > 0 ? alpha : blendedColor.W;
color = Vector4.Lerp(color, blendedColor, alphaFactor);
}
}
targetPixels[x, y] = color;
T packed = default(T);
packed.PackVector(color);
targetPixels[x, y] = packed;
}
this.OnRowProcessed();

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

@ -5,14 +5,17 @@
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the brightness of an <see cref="Image"/>.
/// An <see cref="IImageProcessor{T,TP}"/> to change the brightness of an <see cref="Image{T,TP}"/>.
/// </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>
/// Initializes a new instance of the <see cref="BrightnessProcessor"/> class.
@ -33,7 +36,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; }
/// <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;
int sourceY = sourceRectangle.Y;
@ -41,25 +44,31 @@ namespace ImageProcessorCore.Processors
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
Bootstrapper.Instance.ParallelOptions,
y =>
{
if (y >= sourceY && y < sourceBottom)
{
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();
vector3 += new Vector3(brightness);
T packed = default(T);
packed.PackVector(vector.Compress());
targetPixels[x, y] = Color.Compress(new Color(vector3, color.A));
targetPixels[x, y] = packed;
}
this.OnRowProcessed();
}
});

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Converts the colors of the image to their black and white equivalent.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness.
/// </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/>
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;
/// <summary>
/// The color matrix filter.
/// The color matrix filter. Inherit from this class to perform operation involving color matrices.
/// </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/>
public abstract Matrix4x4 Matrix { get; }
/// <inheritdoc/>
public virtual bool Compand => true;
public override bool Compand { get; set; } = true;
/// <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 endX = sourceRectangle.Right;
Matrix4x4 matrix = this.Matrix;
bool compand = this.Compand;
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
Bootstrapper.Instance.ParallelOptions,
y =>
{
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();
@ -49,20 +55,24 @@ namespace ImageProcessorCore.Processors
/// </summary>
/// <param name="color">The source color.</param>
/// <param name="matrix">The matrix.</param>
/// <param name="compand">Whether to compand the color during processing.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </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)
{
color = Color.Expand(color);
vector = vector.Expand();
}
Vector3 transformed = Vector3.Transform(color.ToVector3(), matrix);
return compand ? Color.Compress(new Color(transformed, color.A)) : new Color(transformed, color.A);
Vector3 transformed = Vector3.Transform(new Vector3(vector.X, vector.Y, vector.Z), matrix);
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.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -8,10 +8,14 @@ namespace ImageProcessorCore.Processors
using System.Numerics;
/// <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"/>.
/// </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/>
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.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -8,10 +8,12 @@ namespace ImageProcessorCore.Processors
using System.Numerics;
/// <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"/>.
/// </summary>
public class GreyscaleBt709Processor : ColorMatrixFilter
public class GrayscaleBt709Processor<T, TP> : ColorMatrixFilter<T, TP>
where T : IPackedVector<TP>
where TP : struct
{
/// <inheritdoc/>
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.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>
/// The <see cref="Matrix4x4"/> used to alter the image.
@ -31,23 +38,8 @@ namespace ImageProcessorCore.Processors
}
this.Angle = angle;
}
/// <summary>
/// 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);
float radians = ImageMaths.DegreesToRadians(angle);
double cosradians = Math.Cos(radians);
double sinradians = Math.Sin(radians);
@ -77,5 +69,16 @@ namespace ImageProcessorCore.Processors
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
/// alter the image pixels.
/// </summary>
public interface IColorMatrixFilter : IImageProcessor
public interface IColorMatrixFilter<T, TP> : IImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{
/// <summary>
/// Gets the <see cref="Matrix4x4"/> used to alter the image.
/// </summary>
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>
/// Converts the colors of the image recreating an old Kodachrome camera effect.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Converts the colors of the image recreating an old Lomograph effect.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()
@ -24,9 +28,11 @@ namespace ImageProcessorCore.Processors
};
/// <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>
/// Converts the colors of the image recreating an old Polaroid effect.
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()
@ -30,12 +34,17 @@ namespace ImageProcessorCore.Processors
};
/// <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);
new GlowProcessor
T packedV = default(T);
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,
RadiusY = target.Width / 4f
}

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

@ -5,13 +5,16 @@
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
/// <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>
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>
/// The saturation to be applied to the image.
@ -34,14 +37,7 @@ namespace ImageProcessorCore.Processors
{
Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(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;
// Stop at -1 to prevent inversion.
@ -71,5 +67,8 @@ namespace ImageProcessorCore.Processors
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.
/// The formula used matches the svg specification. <see href="http://www.w3.org/TR/filter-effects/#sepiaEquivalent"/>
/// </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/>
public override Matrix4x4 Matrix => new Matrix4x4()

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

@ -5,14 +5,17 @@
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the contrast of an <see cref="Image"/>.
/// An <see cref="IImageProcessor{T,TP}"/> to change the contrast of an <see cref="Image"/>.
/// </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>
/// Initializes a new instance of the <see cref="ContrastProcessor"/> class.
@ -33,7 +36,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; }
/// <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;
int sourceY = sourceRectangle.Y;
@ -43,23 +46,26 @@ namespace ImageProcessorCore.Processors
Vector4 contrastVector = new Vector4(contrast, contrast, contrast, 1);
Vector4 shiftVector = new Vector4(.5f, .5f, .5f, 1);
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
Bootstrapper.Instance.ParallelOptions,
y =>
{
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
{
Vector4 color = Color.Expand(sourcePixels[x, y]).ToVector4();
color -= shiftVector;
color *= contrastVector;
color += shiftVector;
targetPixels[x, y] = Color.Compress(new Color(color));
Vector4 vector = (sourcePixels[x, y]).ToVector4().Expand();
vector -= shiftVector;
vector *= contrastVector;
vector += shiftVector;
T packed = default(T);
packed.PackVector(vector.Compress());
targetPixels[x, y] = packed;
}
this.OnRowProcessed();
}

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

@ -8,7 +8,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Applies a Box blur filter to the image.
/// </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>
/// The maximum size of the kernal in either direction.
@ -43,7 +47,7 @@ namespace ImageProcessorCore.Processors
public override float[,] KernelY => this.kernelY;
/// <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)
{

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

@ -6,12 +6,17 @@
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// Defines a filter that uses two one-dimensional matrices to perform convolution against an image.
/// </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>
/// Gets the horizontal gradient operator.
@ -24,7 +29,7 @@ namespace ImageProcessorCore.Processors
public abstract float[,] KernelY { get; }
/// <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[,] kernelY = this.KernelY;
@ -42,12 +47,13 @@ namespace ImageProcessorCore.Processors
int maxY = sourceBottom - 1;
int maxX = endX - 1;
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
Bootstrapper.Instance.ParallelOptions,
y =>
{
if (y >= sourceY && y < sourceBottom)
@ -76,10 +82,10 @@ namespace ImageProcessorCore.Processors
offsetX = offsetX.Clamp(0, maxX);
Color currentColor = sourcePixels[offsetX, offsetY];
float r = currentColor.R;
float g = currentColor.G;
float b = currentColor.B;
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.X;
float g = currentColor.Y;
float b = currentColor.Z;
if (fy < kernelXHeight)
{
@ -101,8 +107,10 @@ namespace ImageProcessorCore.Processors
float green = (float)Math.Sqrt((gX * gX) + (gY * gY));
float blue = (float)Math.Sqrt((bX * bX) + (bY * bY));
Color targetColor = targetPixels[x, y];
targetPixels[x, y] = new Color(red, green, blue, targetColor.A);
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();
}

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

@ -5,12 +5,17 @@
namespace ImageProcessorCore.Processors
{
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// Defines a filter that uses two one-dimensional matrices to perform two-pass convolution against an image.
/// </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>
/// Gets the horizontal gradient operator.
@ -23,24 +28,18 @@ namespace ImageProcessorCore.Processors
public abstract float[,] KernelY { get; }
/// <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[,] 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(target, firstPass, sourceRectangle, startY, endY, kernelY);
}
/// <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.
/// </summary>
/// <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="endY">The index of the row within the source image to end processing.</param>
/// <param name="kernel">The kernel operator.</param>
private void ApplyConvolution(
ImageBase target,
ImageBase source,
Rectangle sourceRectangle,
int startY,
int endY,
float[,] kernel)
private void ApplyConvolution(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle sourceRectangle, int startY, int endY, float[,] kernel)
{
int kernelHeight = kernel.GetLength(0);
int kernelWidth = kernel.GetLength(1);
@ -70,17 +63,18 @@ namespace ImageProcessorCore.Processors
int maxY = sourceBottom - 1;
int maxX = endX - 1;
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
Bootstrapper.Instance.ParallelOptions,
y =>
{
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.
for (int fy = 0; fy < kernelHeight; fy++)
@ -97,12 +91,14 @@ namespace ImageProcessorCore.Processors
offsetX = offsetX.Clamp(0, maxX);
Color currentColor = sourcePixels[offsetX, offsetY];
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
destination += kernel[fy, fx] * currentColor;
}
}
targetPixels[x, y] = destination;
T packed = default(T);
packed.PackVector(destination);
targetPixels[x, y] = packed;
}
this.OnRowProcessed();

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

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

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
/// one-dimensional matrices.
/// </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/>
public bool Greyscale { get; set; }
public bool Grayscale { get; set; }
/// <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
/// two dimensional matrix.
/// </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/>
public bool Greyscale { get; set; }
public bool Grayscale { get; set; }
/// <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>
/// Provides properties and methods allowing the detection of edges within an image.
/// </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>
/// 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>
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.
/// <see href="http://edgedetection.webs.com/"/>
/// </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/>
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.
/// <see href="http://en.wikipedia.org/wiki/Kirsch_operator"/>
/// </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/>
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.
/// <see href="http://en.wikipedia.org/wiki/Discrete_Laplace_operator"/>
/// </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/>
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.
/// <see href="http://en.wikipedia.org/wiki/Discrete_Laplace_operator"/>
/// </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/>
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.
/// <see href="http://fourier.eng.hmc.edu/e161/lectures/gradient/node8.html"/>
/// </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/>
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.
/// <see href="http://en.wikipedia.org/wiki/Prewitt_operator"/>
/// </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/>
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.
/// <see href="http://en.wikipedia.org/wiki/Roberts_cross"/>
/// </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/>
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.
/// <see href="http://en.wikipedia.org/wiki/Sobel_operator#Alternative_operators"/>
/// </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/>
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.
/// <see href="http://en.wikipedia.org/wiki/Sobel_operator"/>
/// </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/>
public override float[,] KernelX => new float[,]

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Applies a Gaussian blur filter to the image.
/// </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>
/// The maximum size of the kernal in either direction.
@ -77,7 +81,7 @@ namespace ImageProcessorCore.Processors
public override float[,] KernelY => this.kernelY;
/// <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)
{

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

@ -10,7 +10,11 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Applies a Gaussian sharpening filter to the image.
/// </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>
/// The maximum size of the kernal in either direction.
@ -79,7 +83,7 @@ namespace ImageProcessorCore.Processors
public override float[,] KernelY => this.kernelY;
/// <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)
{

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

@ -12,12 +12,24 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Creates a glow effect on the image
/// </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>
/// Gets or sets the glow color to apply.
/// </summary>
public Color Color { get; set; } = Color.White;
public T GlowColor { get; set; }
/// <summary>
/// Gets or sets the the x-radius.
@ -30,29 +42,34 @@ namespace ImageProcessorCore.Processors
public float RadiusY { get; set; }
/// <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 endX = sourceRectangle.Right;
Color glowColor = this.Color;
T glowColor = this.GlowColor;
Vector2 centre = Rectangle.Center(targetRectangle).ToVector2();
float rX = this.RadiusX > 0 ? this.RadiusX : targetRectangle.Width / 2f;
float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f;
float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY);
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
Bootstrapper.Instance.ParallelOptions,
y =>
{
for (int x = startX; x < endX; x++)
{
// TODO: Premultiply?
float distance = Vector2.Distance(centre, new Vector2(x, y));
Color sourceColor = sourcePixels[x, y];
targetPixels[x, y] = Color.Lerp(glowColor, sourceColor, .5f * (distance / maxDistance));
Vector4 sourceColor = sourcePixels[x, y].ToVector4();
Vector4 result = Vector4.Lerp(glowColor.ToVector4(), sourceColor, .5f * (distance / maxDistance));
T packed = default(T);
packed.PackVector(result);
targetPixels[x, y] = packed;
}
this.OnRowProcessed();

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

@ -9,12 +9,14 @@ namespace ImageProcessorCore.Processors
using System.Threading.Tasks;
/// <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>
public class InvertProcessor : ImageProcessor
public class InvertProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{
/// <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 sourceBottom = sourceRectangle.Bottom;
@ -22,21 +24,25 @@ namespace ImageProcessorCore.Processors
int endX = sourceRectangle.Right;
Vector3 inverseVector = Vector3.One;
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
Bootstrapper.Instance.ParallelOptions,
y =>
{
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
{
Color color = sourcePixels[x, y];
Vector3 vector = inverseVector - color.ToVector3();
targetPixels[x, y] = new Color(vector, color.A);
Vector4 color = sourcePixels[x, y].ToVector4();
Vector3 vector = inverseVector - new Vector3(color.X, color.Y, color.Z);
T packed = default(T);
packed.PackVector(new Vector4(vector, color.W));
targetPixels[x, y] = packed;
}
this.OnRowProcessed();
@ -46,3 +52,4 @@ namespace ImageProcessorCore.Processors
}
}
}

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

@ -5,20 +5,23 @@
namespace ImageProcessorCore.Processors
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>.
/// An <see cref="IImageProcessor{T,TP}"/> to invert the colors of an <see cref="Image{T,TP}"/>.
/// </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>
/// Initializes a new instance of the <see cref="PixelateProcessor"/> class.
/// Initializes a new instance of the <see cref="PixelateProcessor{T,TP}"/> class.
/// </summary>
/// <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.
/// </exception>
public PixelateProcessor(int size)
@ -33,7 +36,7 @@ namespace ImageProcessorCore.Processors
public int Value { get; }
/// <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 sourceBottom = sourceRectangle.Bottom;
@ -45,8 +48,8 @@ namespace ImageProcessorCore.Processors
// Get the range on the y-plane to choose from.
IEnumerable<int> range = EnumerableExtensions.SteppedRange(startY, i => i < endY, size);
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.ForEach(
range,
@ -73,7 +76,7 @@ namespace ImageProcessorCore.Processors
// Get the pixel color in the centre of the soon to be pixelated area.
// 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 (int l = y; l < y + size && l < sourceBottom; l++)

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

@ -12,12 +12,24 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Creates a vignette effect on the image
/// </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>
/// Gets or sets the vignette color to apply.
/// </summary>
public Color Color { get; set; } = Color.Black;
public T VignetteColor { get; set; }
/// <summary>
/// Gets or sets the the x-radius.
@ -30,29 +42,34 @@ namespace ImageProcessorCore.Processors
public float RadiusY { get; set; }
/// <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 endX = sourceRectangle.Right;
Color vignetteColor = this.Color;
T vignetteColor = this.VignetteColor;
Vector2 centre = Rectangle.Center(targetRectangle).ToVector2();
float rX = this.RadiusX > 0 ? this.RadiusX : targetRectangle.Width / 2f;
float rY = this.RadiusY > 0 ? this.RadiusY : targetRectangle.Height / 2f;
float maxDistance = (float)Math.Sqrt(rX * rX + rY * rY);
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
Bootstrapper.Instance.ParallelOptions,
y =>
{
for (int x = startX; x < endX; x++)
{
float distance = Vector2.Distance(centre, new Vector2(x, y));
Color sourceColor = sourcePixels[x, y];
targetPixels[x, y] = Color.Lerp(vignetteColor, sourceColor, 1 - .9f * distance / maxDistance);
Vector4 sourceColor = sourcePixels[x, y].ToVector4();
Vector4 result = Vector4.Lerp(vignetteColor.ToVector4(), sourceColor, 1 - .9f * (distance / maxDistance));
T packed = default(T);
packed.PackVector(result);
targetPixels[x, y] = packed;
}
this.OnRowProcessed();
});

22
src/ImageProcessorCore/Filters/Saturation.cs

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

18
src/ImageProcessorCore/Filters/Sepia.cs

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

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

@ -69,13 +69,14 @@ namespace ImageProcessorCore.Formats
return isBmp;
}
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase"/>.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to decode to.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public void Decode(Image image, Stream stream)
/// <inheritdoc/>
public void Decode<T, TP>(Image<T, TP> image, Stream stream)
where T : IPackedVector<TP>
where TP : struct
{
Guard.NotNull(image, "image");
Guard.NotNull(stream, "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
/// the data to image.
/// </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.
/// 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>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="image"/> is null.</para>
/// <para>- or -</para>
/// <para><paramref name="stream"/> is null.</para>
/// </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;
@ -110,14 +114,14 @@ namespace ImageProcessorCore.Formats
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(
$"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)
{
@ -130,25 +134,19 @@ namespace ImageProcessorCore.Formats
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)
{
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)
{
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)
{
this.ReadRgbPalette(
imageData,
palette,
this.infoHeader.Width,
this.infoHeader.Height,
this.infoHeader.BitsPerPixel,
inverted);
this.ReadRgbPalette<T, TP>(imageData, palette, this.infoHeader.Width, this.infoHeader.Height, this.infoHeader.BitsPerPixel, inverted);
}
break;
@ -169,6 +167,7 @@ namespace ImageProcessorCore.Formats
/// </summary>
/// <param name="y">The y- value representing the current row.</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>
private static int Invert(int y, int height, bool inverted)
{
@ -189,12 +188,17 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Reads the color palette from the stream.
/// </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="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</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)
int ppb = 8 / bits;
@ -218,6 +222,7 @@ namespace ImageProcessorCore.Formats
Parallel.For(
0,
height,
Bootstrapper.Instance.ParallelOptions,
y =>
{
int rowOffset = y * (arrayWidth + alignment);
@ -234,14 +239,12 @@ namespace ImageProcessorCore.Formats
for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++)
{
int colorIndex = ((data[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
int arrayOffset = ((row * width) + (colOffset + shift)) * 4;
// We divide by 255 as we will store the colors in our floating point format.
// Stored in r-> g-> b-> a order.
imageData[arrayOffset] = colors[colorIndex + 2] / 255f; // r
imageData[arrayOffset + 1] = colors[colorIndex + 1] / 255f; // g
imageData[arrayOffset + 2] = colors[colorIndex] / 255f; // b
imageData[arrayOffset + 3] = 1; // a
int arrayOffset = (row * width) + (colOffset + shift);
// Stored in b-> g-> r order.
T packed = default(T);
packed.PackBytes(colors[colorIndex + 2], colors[colorIndex + 1], colors[colorIndex], 255);
imageData[arrayOffset] = packed;
}
}
});
@ -250,14 +253,19 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Reads the 16 bit color palette from the stream
/// </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="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.
const float ScaleR = 0.25F; // (256 / 32) / 32
const float ScaleG = 0.0625F; // (256 / 64) / 64
const int ScaleR = 8; // 256/32
const int ScaleG = 4; // 256/64
int alignment;
byte[] data = this.GetImageArray(width, height, 2, out alignment);
@ -265,6 +273,7 @@ namespace ImageProcessorCore.Formats
Parallel.For(
0,
height,
Bootstrapper.Instance.ParallelOptions,
y =>
{
int rowOffset = y * ((width * 2) + alignment);
@ -278,17 +287,16 @@ namespace ImageProcessorCore.Formats
short temp = BitConverter.ToInt16(data, offset);
float r = ((temp & Rgb16RMask) >> 11) * ScaleR;
float g = ((temp & Rgb16GMask) >> 5) * ScaleG;
float b = (temp & Rgb16BMask) * ScaleR;
byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR);
byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG);
byte b = (byte)((temp & Rgb16BMask) * ScaleR);
int arrayOffset = ((row * width) + x) * 4;
int arrayOffset = ((row * width) + x);
// Stored in r-> g-> b-> a order.
imageData[arrayOffset] = r;
imageData[arrayOffset + 1] = g;
imageData[arrayOffset + 2] = b;
imageData[arrayOffset + 3] = 1;
// Stored in b-> g-> r order.
T packed = default(T);
packed.PackBytes(r, g, b, 255);
imageData[arrayOffset] = packed;
}
});
}
@ -296,10 +304,15 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Reads the 24 bit color palette from the stream
/// </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="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;
byte[] data = this.GetImageArray(width, height, 3, out alignment);
@ -307,6 +320,7 @@ namespace ImageProcessorCore.Formats
Parallel.For(
0,
height,
Bootstrapper.Instance.ParallelOptions,
y =>
{
int rowOffset = y * ((width * 3) + alignment);
@ -317,14 +331,13 @@ namespace ImageProcessorCore.Formats
for (int x = 0; x < width; x++)
{
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.
// Stored in r-> g-> b-> a order.
imageData[arrayOffset] = data[offset + 2] / 255f;
imageData[arrayOffset + 1] = data[offset + 1] / 255f;
imageData[arrayOffset + 2] = data[offset] / 255f;
imageData[arrayOffset + 3] = 1;
// Stored in b-> g-> r-> a order.
T packed = default(T);
packed.PackBytes(data[offset + 2], data[offset + 1], data[offset], 255);
imageData[arrayOffset] = packed;
}
});
}
@ -332,10 +345,15 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Reads the 32 bit color palette from the stream
/// </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="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;
byte[] data = this.GetImageArray(width, height, 4, out alignment);
@ -343,6 +361,7 @@ namespace ImageProcessorCore.Formats
Parallel.For(
0,
height,
Bootstrapper.Instance.ParallelOptions,
y =>
{
int rowOffset = y * ((width * 4) + alignment);
@ -353,14 +372,12 @@ namespace ImageProcessorCore.Formats
for (int x = 0; x < width; x++)
{
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 r-> g-> b-> a order.
imageData[arrayOffset] = data[offset + 2] / 255f;
imageData[arrayOffset + 1] = data[offset + 1] / 255f;
imageData[arrayOffset + 2] = data[offset] / 255f;
imageData[arrayOffset + 3] = 1; // TODO: Can we use our real alpha here?
// Stored in b-> g-> r-> a order.
T packed = default(T);
packed.PackBytes(data[offset + 2], data[offset + 1], data[offset], data[offset + 3]);
imageData[arrayOffset] = packed;
}
});
}

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

@ -43,7 +43,9 @@ namespace ImageProcessorCore.Formats
}
/// <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();
encoder.Encode(image, stream, this.BitsPerPixel);

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

@ -8,12 +8,11 @@ namespace ImageProcessorCore.Formats
using System;
using System.IO;
using ImageProcessorCore.IO;
using IO;
/// <summary>
/// Image encoder for writing an image to a stream as a Windows bitmap.
/// </summary>
/// <remarks>The encoder can currently only write 24-bit rgb images to streams.</remarks>
internal sealed class BmpEncoderCore
{
/// <summary>
@ -22,12 +21,16 @@ namespace ImageProcessorCore.Formats
private BmpBitsPerPixel bmpBitsPerPixel;
/// <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>
/// <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="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(stream, nameof(stream));
@ -36,6 +39,7 @@ namespace ImageProcessorCore.Formats
int rowWidth = image.Width;
// TODO: Check this for varying file formats.
int amount = (image.Width * (int)this.bmpBitsPerPixel) % 4;
if (amount != 0)
{
@ -117,13 +121,16 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Writes the pixel data to the binary stream.
/// </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.
/// </param>
/// <param name="image">
/// The <see cref="ImageBase"/> containing pixel data.
/// The <see cref="ImageBase{T,TP}"/> containing pixel data.
/// </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.
int amount = (image.Width * (int)this.bmpBitsPerPixel) % 4;
@ -132,7 +139,7 @@ namespace ImageProcessorCore.Formats
amount = 4 - amount;
}
using (PixelAccessor pixels = image.Lock())
using (IPixelAccessor<T, TP> pixels = image.Lock())
{
switch (this.bmpBitsPerPixel)
{
@ -150,22 +157,22 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Writes the 32bit color palette to the stream.
/// </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="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>
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 x = 0; x < pixels.Width; x++)
{
// Limit the output range and multiply out from our floating point.
// Convert back to b-> g-> r-> a order.
// Convert to non-premultiplied color.
Bgra32 color = Color.ToNonPremultiplied(pixels[x, y]);
// We can take advantage of BGRA here
writer.Write(color.Bgra);
byte[] bytes = pixels[x, y].ToBytes();
writer.Write(new[] { bytes[2], bytes[1], bytes[0], bytes[3] });
}
// Pad
@ -179,22 +186,21 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Writes the 24bit color palette to the stream.
/// </summary>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor"/> containing pixel data.</param>
/// <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="pixels">The <see cref="IPixelAccessor"/> containing pixel data.</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 x = 0; x < pixels.Width; x++)
{
// Limit the output range and multiply out from our floating point.
// Convert back to b-> g-> r-> a order.
// Convert to non-premultiplied color.
Bgra32 color = Color.ToNonPremultiplied(pixels[x, y]);
// Allocate 1 array instead of allocating 3.
writer.Write(new[] { color.B, color.G, color.R });
// Convert back to b-> g-> r order.
byte[] bytes = pixels[x, y].ToBytes();
writer.Write(new[] { bytes[2], bytes[1], bytes[0] });
}
// Pad

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

@ -54,14 +54,12 @@ namespace ImageProcessorCore.Formats
header[5] == 0x61; // a
}
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase"/>.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to decode to.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public void Decode(Image image, Stream stream)
/// <inheritdoc/>
public void Decode<T, TP>(Image<T, TP> image, Stream stream)
where T : IPackedVector<TP>
where TP : struct
{
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>
/// Performs the gif decoding operation.
/// </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>
/// The image to decode the information to.
/// </summary>
private Image decodedImage;
private Image<T, TP> decodedImage;
/// <summary>
/// The currently loaded stream.
@ -31,7 +35,7 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// The current frame.
/// </summary>
private float[] currentFrame;
private T[] currentFrame;
/// <summary>
/// The logical screen descriptor.
@ -48,7 +52,7 @@ namespace ImageProcessorCore.Formats
/// </summary>
/// <param name="image">The image to decode to.</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;
@ -175,10 +179,10 @@ namespace ImageProcessorCore.Formats
$"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(
$"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)
{
this.currentFrame = new float[imageWidth * imageHeight * 4];
this.currentFrame = new T[imageWidth * imageHeight];
}
float[] lastFrame = null;
T[] lastFrame = null;
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{
lastFrame = new float[imageWidth * imageHeight * 4];
lastFrame = new T[imageWidth * imageHeight];
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++)
{
offset = ((writeY * imageWidth) + x) * 4;
offset = (writeY * imageWidth) + x;
int index = indices[i];
if (this.graphicsControlExtension == null ||
this.graphicsControlExtension.TransparencyFlag == false ||
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.
// Gifs don't store alpha transparency so we don't need to convert to
// premultiplied.
int indexOffset = index * 3;
this.currentFrame[offset + 0] = colorTable[indexOffset] / 255f; // r
this.currentFrame[offset + 1] = colorTable[indexOffset + 1] / 255f; // g
this.currentFrame[offset + 2] = colorTable[indexOffset + 2] / 255f; // b
this.currentFrame[offset + 3] = 1; // a
T pixel = default(T);
pixel.PackBytes(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2], 255);
this.currentFrame[offset] = pixel;
}
i++;
}
}
float[] pixels = new float[imageWidth * imageHeight * 4];
T[] pixels = new T[imageWidth * imageHeight];
Array.Copy(this.currentFrame, pixels, pixels.Length);
ImageBase currentImage;
ImageBase<T, TP> currentImage;
if (this.decodedImage.Pixels == null)
{
@ -386,7 +387,7 @@ namespace ImageProcessorCore.Formats
}
else
{
ImageFrame frame = new ImageFrame();
ImageFrame<T, TP> frame = new ImageFrame<T, TP>();
currentImage = frame;
currentImage.SetPixels(imageWidth, imageHeight, pixels);
@ -408,13 +409,10 @@ namespace ImageProcessorCore.Formats
{
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.
this.currentFrame[offset] = 0;
this.currentFrame[offset + 1] = 0;
this.currentFrame[offset + 2] = 0;
this.currentFrame[offset + 3] = 0;
this.currentFrame[offset] = default(T);
}
}
}

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

@ -47,7 +47,9 @@ namespace ImageProcessorCore.Formats
}
/// <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
{

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

@ -10,8 +10,8 @@ namespace ImageProcessorCore.Formats
using System.Linq;
using System.Threading.Tasks;
using ImageProcessorCore.IO;
using ImageProcessorCore.Quantizers;
using IO;
using Quantizers;
/// <summary>
/// Performs the gif encoding operation.
@ -40,20 +40,24 @@ namespace ImageProcessorCore.Formats
public IQuantizer Quantizer { get; set; }
/// <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>
/// <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>
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(stream, nameof(stream));
Image image = (Image)imageBase;
Image<T, TP> image = (Image<T, TP>)imageBase;
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.
@ -67,7 +71,7 @@ namespace ImageProcessorCore.Formats
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(this.Quality);
// 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.
this.WriteHeader(writer);
@ -85,9 +89,9 @@ namespace ImageProcessorCore.Formats
if (image.Frames.Any())
{
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.WriteImageDescriptor(frame, writer);
this.WriteColorTable(quantizedFrame, writer);
@ -111,10 +115,14 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Writes the logical screen descriptor to the stream.
/// </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="writer">The writer to write to the stream with.</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
{
@ -180,10 +188,14 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Writes the graphics control extension to the stream.
/// </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="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.
bool hasTransparent = transparencyIndex > -1;
@ -224,9 +236,13 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Writes the image descriptor to the stream.
/// </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>
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
// TODO: Can we capture this?
@ -247,12 +263,16 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Writes the color table to the stream.
/// </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>
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.
Bgra32[] palette = image.Palette;
T[] palette = image.Palette;
int pixelCount = palette.Length;
// Get max colors for bit depth.
@ -263,11 +283,11 @@ namespace ImageProcessorCore.Formats
i =>
{
int offset = i * 3;
Bgra32 color = palette[i];
byte[] color = palette[i].ToBytes();
colorTable[offset] = color.R;
colorTable[offset + 1] = color.G;
colorTable[offset + 2] = color.B;
colorTable[offset] = color[0];
colorTable[offset + 1] = color[1];
colorTable[offset + 2] = color[2];
});
writer.Write(colorTable, 0, colorTableLength);
@ -276,9 +296,13 @@ namespace ImageProcessorCore.Formats
/// <summary>
/// Writes the image pixel data to the stream.
/// </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>
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;

10
src/ImageProcessorCore/Formats/IImageDecoder.cs

@ -39,10 +39,14 @@ namespace ImageProcessorCore.Formats
bool IsSupportedFileFormat(byte[] header);
/// <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>
/// <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>
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