diff --git a/ImageSharp.46.sln b/ImageSharp.46.sln
new file mode 100644
index 0000000000..fe112b9eac
--- /dev/null
+++ b/ImageSharp.46.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp46", "src\ImageSharp46\ImageSharp46.csproj", "{FBA0B5F6-09C2-4317-8EF6-6ADB9B20E6B1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Tests46", "tests\ImageSharp.Tests46\ImageSharp.Tests46.csproj", "{635E0A15-3893-4763-A7F6-FCCFF85BCCA4}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FBA0B5F6-09C2-4317-8EF6-6ADB9B20E6B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FBA0B5F6-09C2-4317-8EF6-6ADB9B20E6B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FBA0B5F6-09C2-4317-8EF6-6ADB9B20E6B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FBA0B5F6-09C2-4317-8EF6-6ADB9B20E6B1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {635E0A15-3893-4763-A7F6-FCCFF85BCCA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {635E0A15-3893-4763-A7F6-FCCFF85BCCA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {635E0A15-3893-4763-A7F6-FCCFF85BCCA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {635E0A15-3893-4763-A7F6-FCCFF85BCCA4}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/ImageSharp46/Bootstrapper.cs b/src/ImageSharp46/Bootstrapper.cs
new file mode 100644
index 0000000000..35a4a52253
--- /dev/null
+++ b/src/ImageSharp46/Bootstrapper.cs
@@ -0,0 +1,69 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Threading.Tasks;
+
+ using Formats;
+
+ ///
+ /// Provides initialization code which allows extending the library.
+ ///
+ public class Bootstrapper
+ {
+ ///
+ /// A new instance Initializes a new instance of the class.
+ /// with lazy initialization.
+ ///
+ private static readonly Lazy Lazy = new Lazy(() => new Bootstrapper());
+
+ ///
+ /// The default list of supported
+ ///
+ private readonly List imageFormats;
+
+ ///
+ /// Prevents a default instance of the class from being created.
+ ///
+ private Bootstrapper()
+ {
+ this.imageFormats = new List
+ {
+ new BmpFormat(),
+ new JpegFormat(),
+ new PngFormat(),
+ new GifFormat()
+ };
+ }
+
+ ///
+ /// Gets the current bootstrapper instance.
+ ///
+ public static Bootstrapper Instance => Lazy.Value;
+
+ ///
+ /// Gets the collection of supported
+ ///
+ public IReadOnlyCollection ImageFormats => new ReadOnlyCollection(this.imageFormats);
+
+ ///
+ /// Gets or sets the global parallel options for processing tasks in parallel.
+ ///
+ public ParallelOptions ParallelOptions { get; set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
+
+ ///
+ /// Adds a new to the collection of supported image formats.
+ ///
+ /// The new format to add.
+ public void AddImageFormat(IImageFormat format)
+ {
+ this.imageFormats.Add(format);
+ }
+ }
+}
diff --git a/src/ImageSharp46/Colors/Color.cs b/src/ImageSharp46/Colors/Color.cs
new file mode 100644
index 0000000000..c4ea2d7623
--- /dev/null
+++ b/src/ImageSharp46/Colors/Color.cs
@@ -0,0 +1,408 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Globalization;
+ using System.Numerics;
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// 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.
+ ///
+ public partial struct Color : IPackedPixel, IEquatable
+ {
+ private const int RedShift = 0;
+ private const int GreenShift = 8;
+ private const int BlueShift = 16;
+ private const int AlphaShift = 24;
+
+ ///
+ /// The maximum byte value.
+ ///
+ private static readonly Vector4 MaxBytes = new Vector4(255);
+
+ ///
+ /// The half vector value.
+ ///
+ private static readonly Vector4 Half = new Vector4(0.5f);
+
+ ///
+ /// The packed value.
+ ///
+ private uint packedValue;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The alpha component.
+ public Color(byte r, byte g, byte b, byte a = 255)
+ {
+ this.packedValue = Pack(r, g, b, a);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The hexadecimal representation of the combined color components arranged
+ /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
+ ///
+ public Color(string hex)
+ {
+ Guard.NotNullOrEmpty(hex, nameof(hex));
+
+ hex = ToRgbaHex(hex);
+
+ if (hex == null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out this.packedValue))
+ {
+ throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex));
+ }
+
+ // Order parsed from hex string will be backwards, so reverse it.
+ this.packedValue = Pack(this.A, this.B, this.G, this.R);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The alpha component.
+ public Color(float r, float g, float b, float a = 1)
+ {
+ this.packedValue = Pack(r, g, b, a);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The vector containing the components for the packed vector.
+ ///
+ public Color(Vector3 vector)
+ {
+ this.packedValue = Pack(ref vector);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The vector containing the components for the packed vector.
+ ///
+ public Color(Vector4 vector)
+ {
+ this.packedValue = Pack(ref vector);
+ }
+
+ ///
+ /// Gets or sets the red component.
+ ///
+ public byte R
+ {
+ get
+ {
+ return (byte)(this.packedValue >> RedShift);
+ }
+
+ set
+ {
+ this.packedValue = this.packedValue & 0xFFFFFF00 | (uint)value << RedShift;
+ }
+ }
+
+ ///
+ /// Gets or sets the green component.
+ ///
+ public byte G
+ {
+ get
+ {
+ return (byte)(this.packedValue >> GreenShift);
+ }
+
+ set
+ {
+ this.packedValue = this.packedValue & 0xFFFF00FF | (uint)value << GreenShift;
+ }
+ }
+
+ ///
+ /// Gets or sets the blue component.
+ ///
+ public byte B
+ {
+ get
+ {
+ return (byte)(this.packedValue >> BlueShift);
+ }
+
+ set
+ {
+ this.packedValue = this.packedValue & 0xFF00FFFF | (uint)value << BlueShift;
+ }
+ }
+
+ ///
+ /// Gets or sets the alpha component.
+ ///
+ public byte A
+ {
+ get
+ {
+ return (byte)(this.packedValue >> AlphaShift);
+ }
+
+ set
+ {
+ this.packedValue = this.packedValue & 0x00FFFFFF | (uint)value << AlphaShift;
+ }
+ }
+
+ ///
+ public uint PackedValue
+ {
+ get
+ {
+ return this.packedValue;
+ }
+
+ set
+ {
+ this.packedValue = value;
+ }
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the parameter is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(Color left, Color right)
+ {
+ return left.packedValue == right.packedValue;
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
+ ///
+ /// True if the parameter is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(Color left, Color right)
+ {
+ return left.packedValue != right.packedValue;
+ }
+
+ ///
+ /// Creates a new instance of the struct.
+ ///
+ ///
+ /// The hexadecimal representation of the combined color components arranged
+ /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
+ ///
+ ///
+ /// The .
+ ///
+ public static Color FromHex(string hex)
+ {
+ return new Color(hex);
+ }
+
+ ///
+ public void PackFromBytes(byte x, byte y, byte z, byte w)
+ {
+ this.packedValue = Pack(x, y, z, w);
+ }
+
+ ///
+ /// Converts the value of this instance to a hexadecimal string.
+ ///
+ /// A hexadecimal string representation of the value.
+ public string ToHex()
+ {
+ uint hexOrder = Pack(this.A, this.B, this.G, this.R);
+ return hexOrder.ToString("X8");
+ }
+
+ ///
+ public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ {
+ switch (componentOrder)
+ {
+ case ComponentOrder.ZYX:
+ bytes[startIndex] = this.B;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.R;
+ break;
+ case ComponentOrder.ZYXW:
+ bytes[startIndex] = this.B;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.R;
+ bytes[startIndex + 3] = this.A;
+ break;
+ case ComponentOrder.XYZ:
+ bytes[startIndex] = this.R;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.B;
+ break;
+ case ComponentOrder.XYZW:
+ bytes[startIndex] = this.R;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.B;
+ bytes[startIndex + 3] = this.A;
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+ }
+
+ ///
+ public void PackFromVector4(Vector4 vector)
+ {
+ this.packedValue = Pack(ref vector);
+ }
+
+ ///
+ public Vector4 ToVector4()
+ {
+ return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return (obj is Color) && this.Equals((Color)obj);
+ }
+
+ ///
+ public bool Equals(Color other)
+ {
+ return this.packedValue == other.packedValue;
+ }
+
+ ///
+ /// Gets a string representation of the packed vector.
+ ///
+ /// A string representation of the packed vector.
+ public override string ToString()
+ {
+ return this.ToVector4().ToString();
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.packedValue.GetHashCode();
+ }
+
+ ///
+ /// Packs a into a uint.
+ ///
+ /// The vector containing the values to pack.
+ /// The containing the packed values.
+ private static uint Pack(ref Vector4 vector)
+ {
+ vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One);
+ vector *= MaxBytes;
+ vector += Half;
+ return (uint)(((byte)vector.X << RedShift)
+ | ((byte)vector.Y << GreenShift)
+ | ((byte)vector.Z << BlueShift)
+ | (byte)vector.W << AlphaShift);
+ }
+
+ ///
+ /// Packs a into a uint.
+ ///
+ /// The vector containing the values to pack.
+ /// The containing the packed values.
+ private static uint Pack(ref Vector3 vector)
+ {
+ Vector4 value = new Vector4(vector, 1);
+ return Pack(ref value);
+ }
+
+ ///
+ /// Packs the four floats into a .
+ ///
+ /// The x-component
+ /// The y-component
+ /// The z-component
+ /// The w-component
+ /// The
+ private static uint Pack(float x, float y, float z, float w)
+ {
+ Vector4 value = new Vector4(x, y, z, w);
+ return Pack(ref value);
+ }
+
+ ///
+ /// Packs the four floats into a .
+ ///
+ /// The x-component
+ /// The y-component
+ /// The z-component
+ /// The w-component
+ /// The
+ private static uint Pack(byte x, byte y, byte z, byte w)
+ {
+ return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift);
+ }
+
+ ///
+ /// Converts the specified hex value to an rrggbbaa hex value.
+ ///
+ /// The hex value to convert.
+ ///
+ /// A rrggbbaa hex value.
+ ///
+ private static string ToRgbaHex(string hex)
+ {
+ hex = hex.StartsWith("#") ? hex.Substring(1) : hex;
+
+ if (hex.Length == 8)
+ {
+ return hex;
+ }
+
+ if (hex.Length == 6)
+ {
+ return hex + "FF";
+ }
+
+ if (hex.Length < 3 || hex.Length > 4)
+ {
+ return null;
+ }
+
+ string red = char.ToString(hex[0]);
+ string green = char.ToString(hex[1]);
+ string blue = char.ToString(hex[2]);
+ string alpha = hex.Length == 3 ? "F" : char.ToString(hex[3]);
+
+ return red + red + green + green + blue + blue + alpha + alpha;
+ }
+ }
+}
diff --git a/src/ImageSharp46/Colors/ColorConstants.cs b/src/ImageSharp46/Colors/ColorConstants.cs
new file mode 100644
index 0000000000..f3b01f466e
--- /dev/null
+++ b/src/ImageSharp46/Colors/ColorConstants.cs
@@ -0,0 +1,179 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Provides useful color definitions.
+ ///
+ public static class ColorConstants
+ {
+ ///
+ /// Provides a lazy, one time method of returning the colors.
+ ///
+ private static readonly Lazy SafeColors = new Lazy(GetWebSafeColors);
+
+ ///
+ /// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4.
+ ///
+ public static Color[] WebSafeColors => SafeColors.Value;
+
+ ///
+ /// Returns an array of web safe colors.
+ ///
+ ///
+ private static Color[] GetWebSafeColors()
+ {
+ return new List
+ {
+ Color.AliceBlue,
+ Color.AntiqueWhite,
+ Color.Aqua,
+ Color.Aquamarine,
+ Color.Azure,
+ Color.Beige,
+ Color.Bisque,
+ Color.Black,
+ Color.BlanchedAlmond,
+ Color.Blue,
+ Color.BlueViolet,
+ Color.Brown,
+ Color.BurlyWood,
+ Color.CadetBlue,
+ Color.Chartreuse,
+ Color.Chocolate,
+ Color.Coral,
+ Color.CornflowerBlue,
+ Color.Cornsilk,
+ Color.Crimson,
+ Color.Cyan,
+ Color.DarkBlue,
+ Color.DarkCyan,
+ Color.DarkGoldenrod,
+ Color.DarkGray,
+ Color.DarkGreen,
+ Color.DarkKhaki,
+ Color.DarkMagenta,
+ Color.DarkOliveGreen,
+ Color.DarkOrange,
+ Color.DarkOrchid,
+ Color.DarkRed,
+ Color.DarkSalmon,
+ Color.DarkSeaGreen,
+ Color.DarkSlateBlue,
+ Color.DarkSlateGray,
+ Color.DarkTurquoise,
+ Color.DarkViolet,
+ Color.DeepPink,
+ Color.DeepSkyBlue,
+ Color.DimGray,
+ Color.DodgerBlue,
+ Color.Firebrick,
+ Color.FloralWhite,
+ Color.ForestGreen,
+ Color.Fuchsia,
+ Color.Gainsboro,
+ Color.GhostWhite,
+ Color.Gold,
+ Color.Goldenrod,
+ Color.Gray,
+ Color.Green,
+ Color.GreenYellow,
+ Color.Honeydew,
+ Color.HotPink,
+ Color.IndianRed,
+ Color.Indigo,
+ Color.Ivory,
+ Color.Khaki,
+ Color.Lavender,
+ Color.LavenderBlush,
+ Color.LawnGreen,
+ Color.LemonChiffon,
+ Color.LightBlue,
+ Color.LightCoral,
+ Color.LightCyan,
+ Color.LightGoldenrodYellow,
+ Color.LightGray,
+ Color.LightGreen,
+ Color.LightPink,
+ Color.LightSalmon,
+ Color.LightSeaGreen,
+ Color.LightSkyBlue,
+ Color.LightSlateGray,
+ Color.LightSteelBlue,
+ Color.LightYellow,
+ Color.Lime,
+ Color.LimeGreen,
+ Color.Linen,
+ Color.Magenta,
+ Color.Maroon,
+ Color.MediumAquamarine,
+ Color.MediumBlue,
+ Color.MediumOrchid,
+ Color.MediumPurple,
+ Color.MediumSeaGreen,
+ Color.MediumSlateBlue,
+ Color.MediumSpringGreen,
+ Color.MediumTurquoise,
+ Color.MediumVioletRed,
+ Color.MidnightBlue,
+ Color.MintCream,
+ Color.MistyRose,
+ Color.Moccasin,
+ Color.NavajoWhite,
+ Color.Navy,
+ Color.OldLace,
+ Color.Olive,
+ Color.OliveDrab,
+ Color.Orange,
+ Color.OrangeRed,
+ Color.Orchid,
+ Color.PaleGoldenrod,
+ Color.PaleGreen,
+ Color.PaleTurquoise,
+ Color.PaleVioletRed,
+ Color.PapayaWhip,
+ Color.PeachPuff,
+ Color.Peru,
+ Color.Pink,
+ Color.Plum,
+ Color.PowderBlue,
+ Color.Purple,
+ Color.RebeccaPurple,
+ Color.Red,
+ Color.RosyBrown,
+ Color.RoyalBlue,
+ Color.SaddleBrown,
+ Color.Salmon,
+ Color.SandyBrown,
+ Color.SeaGreen,
+ Color.SeaShell,
+ Color.Sienna,
+ Color.Silver,
+ Color.SkyBlue,
+ Color.SlateBlue,
+ Color.SlateGray,
+ Color.Snow,
+ Color.SpringGreen,
+ Color.SteelBlue,
+ Color.Tan,
+ Color.Teal,
+ Color.Thistle,
+ Color.Tomato,
+ Color.Transparent,
+ Color.Turquoise,
+ Color.Violet,
+ Color.Wheat,
+ Color.White,
+ Color.WhiteSmoke,
+ Color.Yellow,
+ Color.YellowGreen
+ }.ToArray();
+ }
+ }
+}
diff --git a/src/ImageSharp46/Colors/ColorDefinitions.cs b/src/ImageSharp46/Colors/ColorDefinitions.cs
new file mode 100644
index 0000000000..46e0f87090
--- /dev/null
+++ b/src/ImageSharp46/Colors/ColorDefinitions.cs
@@ -0,0 +1,728 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// 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.
+ ///
+ ///
+ /// 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.
+ ///
+ public partial struct Color
+ {
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F0F8FF.
+ ///
+ public static readonly Color AliceBlue = new Color(240, 248, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FAEBD7.
+ ///
+ public static readonly Color AntiqueWhite = new Color(250, 235, 215, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #00FFFF.
+ ///
+ public static readonly Color Aqua = new Color(0, 255, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #7FFFD4.
+ ///
+ public static readonly Color Aquamarine = new Color(127, 255, 212, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F0FFFF.
+ ///
+ public static readonly Color Azure = new Color(240, 255, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F5F5DC.
+ ///
+ public static readonly Color Beige = new Color(245, 245, 220, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFE4C4.
+ ///
+ public static readonly Color Bisque = new Color(255, 228, 196, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #000000.
+ ///
+ public static readonly Color Black = new Color(0, 0, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFEBCD.
+ ///
+ public static readonly Color BlanchedAlmond = new Color(255, 235, 205, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #0000FF.
+ ///
+ public static readonly Color Blue = new Color(0, 0, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #8A2BE2.
+ ///
+ public static readonly Color BlueViolet = new Color(138, 43, 226, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #A52A2A.
+ ///
+ public static readonly Color Brown = new Color(165, 42, 42, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #DEB887.
+ ///
+ public static readonly Color BurlyWood = new Color(222, 184, 135, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #5F9EA0.
+ ///
+ public static readonly Color CadetBlue = new Color(95, 158, 160, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #7FFF00.
+ ///
+ public static readonly Color Chartreuse = new Color(127, 255, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #D2691E.
+ ///
+ public static readonly Color Chocolate = new Color(210, 105, 30, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FF7F50.
+ ///
+ public static readonly Color Coral = new Color(255, 127, 80, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #6495ED.
+ ///
+ public static readonly Color CornflowerBlue = new Color(100, 149, 237, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFF8DC.
+ ///
+ public static readonly Color Cornsilk = new Color(255, 248, 220, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #DC143C.
+ ///
+ public static readonly Color Crimson = new Color(220, 20, 60, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #00FFFF.
+ ///
+ public static readonly Color Cyan = new Color(0, 255, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #00008B.
+ ///
+ public static readonly Color DarkBlue = new Color(0, 0, 139, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #008B8B.
+ ///
+ public static readonly Color DarkCyan = new Color(0, 139, 139, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #B8860B.
+ ///
+ public static readonly Color DarkGoldenrod = new Color(184, 134, 11, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #A9A9A9.
+ ///
+ public static readonly Color DarkGray = new Color(169, 169, 169, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #006400.
+ ///
+ public static readonly Color DarkGreen = new Color(0, 100, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #BDB76B.
+ ///
+ public static readonly Color DarkKhaki = new Color(189, 183, 107, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #8B008B.
+ ///
+ public static readonly Color DarkMagenta = new Color(139, 0, 139, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #556B2F.
+ ///
+ public static readonly Color DarkOliveGreen = new Color(85, 107, 47, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FF8C00.
+ ///
+ public static readonly Color DarkOrange = new Color(255, 140, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #9932CC.
+ ///
+ public static readonly Color DarkOrchid = new Color(153, 50, 204, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #8B0000.
+ ///
+ public static readonly Color DarkRed = new Color(139, 0, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #E9967A.
+ ///
+ public static readonly Color DarkSalmon = new Color(233, 150, 122, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #8FBC8B.
+ ///
+ public static readonly Color DarkSeaGreen = new Color(143, 188, 139, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #483D8B.
+ ///
+ public static readonly Color DarkSlateBlue = new Color(72, 61, 139, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #2F4F4F.
+ ///
+ public static readonly Color DarkSlateGray = new Color(47, 79, 79, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #00CED1.
+ ///
+ public static readonly Color DarkTurquoise = new Color(0, 206, 209, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #9400D3.
+ ///
+ public static readonly Color DarkViolet = new Color(148, 0, 211, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FF1493.
+ ///
+ public static readonly Color DeepPink = new Color(255, 20, 147, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #00BFFF.
+ ///
+ public static readonly Color DeepSkyBlue = new Color(0, 191, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #696969.
+ ///
+ public static readonly Color DimGray = new Color(105, 105, 105, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #1E90FF.
+ ///
+ public static readonly Color DodgerBlue = new Color(30, 144, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #B22222.
+ ///
+ public static readonly Color Firebrick = new Color(178, 34, 34, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFFAF0.
+ ///
+ public static readonly Color FloralWhite = new Color(255, 250, 240, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #228B22.
+ ///
+ public static readonly Color ForestGreen = new Color(34, 139, 34, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FF00FF.
+ ///
+ public static readonly Color Fuchsia = new Color(255, 0, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #DCDCDC.
+ ///
+ public static readonly Color Gainsboro = new Color(220, 220, 220, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F8F8FF.
+ ///
+ public static readonly Color GhostWhite = new Color(248, 248, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFD700.
+ ///
+ public static readonly Color Gold = new Color(255, 215, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #DAA520.
+ ///
+ public static readonly Color Goldenrod = new Color(218, 165, 32, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #808080.
+ ///
+ public static readonly Color Gray = new Color(128, 128, 128, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #008000.
+ ///
+ public static readonly Color Green = new Color(0, 128, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #ADFF2F.
+ ///
+ public static readonly Color GreenYellow = new Color(173, 255, 47, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F0FFF0.
+ ///
+ public static readonly Color Honeydew = new Color(240, 255, 240, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FF69B4.
+ ///
+ public static readonly Color HotPink = new Color(255, 105, 180, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #CD5C5C.
+ ///
+ public static readonly Color IndianRed = new Color(205, 92, 92, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #4B0082.
+ ///
+ public static readonly Color Indigo = new Color(75, 0, 130, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFFFF0.
+ ///
+ public static readonly Color Ivory = new Color(255, 255, 240, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F0E68C.
+ ///
+ public static readonly Color Khaki = new Color(240, 230, 140, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #E6E6FA.
+ ///
+ public static readonly Color Lavender = new Color(230, 230, 250, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFF0F5.
+ ///
+ public static readonly Color LavenderBlush = new Color(255, 240, 245, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #7CFC00.
+ ///
+ public static readonly Color LawnGreen = new Color(124, 252, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFFACD.
+ ///
+ public static readonly Color LemonChiffon = new Color(255, 250, 205, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #ADD8E6.
+ ///
+ public static readonly Color LightBlue = new Color(173, 216, 230, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F08080.
+ ///
+ public static readonly Color LightCoral = new Color(240, 128, 128, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #E0FFFF.
+ ///
+ public static readonly Color LightCyan = new Color(224, 255, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FAFAD2.
+ ///
+ public static readonly Color LightGoldenrodYellow = new Color(250, 250, 210, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #D3D3D3.
+ ///
+ public static readonly Color LightGray = new Color(211, 211, 211, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #90EE90.
+ ///
+ public static readonly Color LightGreen = new Color(144, 238, 144, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFB6C1.
+ ///
+ public static readonly Color LightPink = new Color(255, 182, 193, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFA07A.
+ ///
+ public static readonly Color LightSalmon = new Color(255, 160, 122, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #20B2AA.
+ ///
+ public static readonly Color LightSeaGreen = new Color(32, 178, 170, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #87CEFA.
+ ///
+ public static readonly Color LightSkyBlue = new Color(135, 206, 250, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #778899.
+ ///
+ public static readonly Color LightSlateGray = new Color(119, 136, 153, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #B0C4DE.
+ ///
+ public static readonly Color LightSteelBlue = new Color(176, 196, 222, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFFFE0.
+ ///
+ public static readonly Color LightYellow = new Color(255, 255, 224, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #00FF00.
+ ///
+ public static readonly Color Lime = new Color(0, 255, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #32CD32.
+ ///
+ public static readonly Color LimeGreen = new Color(50, 205, 50, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FAF0E6.
+ ///
+ public static readonly Color Linen = new Color(250, 240, 230, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FF00FF.
+ ///
+ public static readonly Color Magenta = new Color(255, 0, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #800000.
+ ///
+ public static readonly Color Maroon = new Color(128, 0, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #66CDAA.
+ ///
+ public static readonly Color MediumAquamarine = new Color(102, 205, 170, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #0000CD.
+ ///
+ public static readonly Color MediumBlue = new Color(0, 0, 205, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #BA55D3.
+ ///
+ public static readonly Color MediumOrchid = new Color(186, 85, 211, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #9370DB.
+ ///
+ public static readonly Color MediumPurple = new Color(147, 112, 219, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #3CB371.
+ ///
+ public static readonly Color MediumSeaGreen = new Color(60, 179, 113, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #7B68EE.
+ ///
+ public static readonly Color MediumSlateBlue = new Color(123, 104, 238, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #00FA9A.
+ ///
+ public static readonly Color MediumSpringGreen = new Color(0, 250, 154, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #48D1CC.
+ ///
+ public static readonly Color MediumTurquoise = new Color(72, 209, 204, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #C71585.
+ ///
+ public static readonly Color MediumVioletRed = new Color(199, 21, 133, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #191970.
+ ///
+ public static readonly Color MidnightBlue = new Color(25, 25, 112, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F5FFFA.
+ ///
+ public static readonly Color MintCream = new Color(245, 255, 250, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFE4E1.
+ ///
+ public static readonly Color MistyRose = new Color(255, 228, 225, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFE4B5.
+ ///
+ public static readonly Color Moccasin = new Color(255, 228, 181, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFDEAD.
+ ///
+ public static readonly Color NavajoWhite = new Color(255, 222, 173, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #000080.
+ ///
+ public static readonly Color Navy = new Color(0, 0, 128, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FDF5E6.
+ ///
+ public static readonly Color OldLace = new Color(253, 245, 230, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #808000.
+ ///
+ public static readonly Color Olive = new Color(128, 128, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #6B8E23.
+ ///
+ public static readonly Color OliveDrab = new Color(107, 142, 35, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFA500.
+ ///
+ public static readonly Color Orange = new Color(255, 165, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FF4500.
+ ///
+ public static readonly Color OrangeRed = new Color(255, 69, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #DA70D6.
+ ///
+ public static readonly Color Orchid = new Color(218, 112, 214, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #EEE8AA.
+ ///
+ public static readonly Color PaleGoldenrod = new Color(238, 232, 170, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #98FB98.
+ ///
+ public static readonly Color PaleGreen = new Color(152, 251, 152, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #AFEEEE.
+ ///
+ public static readonly Color PaleTurquoise = new Color(175, 238, 238, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #DB7093.
+ ///
+ public static readonly Color PaleVioletRed = new Color(219, 112, 147, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFEFD5.
+ ///
+ public static readonly Color PapayaWhip = new Color(255, 239, 213, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFDAB9.
+ ///
+ public static readonly Color PeachPuff = new Color(255, 218, 185, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #CD853F.
+ ///
+ public static readonly Color Peru = new Color(205, 133, 63, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFC0CB.
+ ///
+ public static readonly Color Pink = new Color(255, 192, 203, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #DDA0DD.
+ ///
+ public static readonly Color Plum = new Color(221, 160, 221, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #B0E0E6.
+ ///
+ public static readonly Color PowderBlue = new Color(176, 224, 230, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #800080.
+ ///
+ public static readonly Color Purple = new Color(128, 0, 128, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #0.
+ ///
+ public static readonly Color RebeccaPurple = new Color(102, 51, 153, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FF0000.
+ ///
+ public static readonly Color Red = new Color(255, 0, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #BC8F8F.
+ ///
+ public static readonly Color RosyBrown = new Color(188, 143, 143, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #4169E1.
+ ///
+ public static readonly Color RoyalBlue = new Color(65, 105, 225, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #8B4513.
+ ///
+ public static readonly Color SaddleBrown = new Color(139, 69, 19, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FA8072.
+ ///
+ public static readonly Color Salmon = new Color(250, 128, 114, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F4A460.
+ ///
+ public static readonly Color SandyBrown = new Color(244, 164, 96, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #2E8B57.
+ ///
+ public static readonly Color SeaGreen = new Color(46, 139, 87, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFF5EE.
+ ///
+ public static readonly Color SeaShell = new Color(255, 245, 238, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #A0522D.
+ ///
+ public static readonly Color Sienna = new Color(160, 82, 45, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #C0C0C0.
+ ///
+ public static readonly Color Silver = new Color(192, 192, 192, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #87CEEB.
+ ///
+ public static readonly Color SkyBlue = new Color(135, 206, 235, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #6A5ACD.
+ ///
+ public static readonly Color SlateBlue = new Color(106, 90, 205, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #708090.
+ ///
+ public static readonly Color SlateGray = new Color(112, 128, 144, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFFAFA.
+ ///
+ public static readonly Color Snow = new Color(255, 250, 250, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #00FF7F.
+ ///
+ public static readonly Color SpringGreen = new Color(0, 255, 127, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #4682B4.
+ ///
+ public static readonly Color SteelBlue = new Color(70, 130, 180, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #D2B48C.
+ ///
+ public static readonly Color Tan = new Color(210, 180, 140, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #008080.
+ ///
+ public static readonly Color Teal = new Color(0, 128, 128, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #D8BFD8.
+ ///
+ public static readonly Color Thistle = new Color(216, 191, 216, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FF6347.
+ ///
+ public static readonly Color Tomato = new Color(255, 99, 71, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFFFFF.
+ ///
+ public static readonly Color Transparent = new Color(255, 255, 255, 0);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #40E0D0.
+ ///
+ public static readonly Color Turquoise = new Color(64, 224, 208, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #EE82EE.
+ ///
+ public static readonly Color Violet = new Color(238, 130, 238, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F5DEB3.
+ ///
+ public static readonly Color Wheat = new Color(245, 222, 179, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFFFFF.
+ ///
+ public static readonly Color White = new Color(255, 255, 255, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #F5F5F5.
+ ///
+ public static readonly Color WhiteSmoke = new Color(245, 245, 245, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #FFFF00.
+ ///
+ public static readonly Color Yellow = new Color(255, 255, 0, 255);
+
+ ///
+ /// Represents a matching the W3C definition that has an hex value of #9ACD32.
+ ///
+ public static readonly Color YellowGreen = new Color(154, 205, 50, 255);
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp46/Colors/ColorTransforms.cs b/src/ImageSharp46/Colors/ColorTransforms.cs
new file mode 100644
index 0000000000..e710b69e99
--- /dev/null
+++ b/src/ImageSharp46/Colors/ColorTransforms.cs
@@ -0,0 +1,74 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Numerics;
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// 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.
+ ///
+ public partial struct Color
+ {
+ ///
+ /// Blends two colors by multiplication.
+ ///
+ /// The source color is multiplied by the destination color and replaces the destination.
+ /// The resultant color is always at least as dark as either the source or destination color.
+ /// Multiplying any color with black results in black. Multiplying any color with white preserves the
+ /// original color.
+ ///
+ ///
+ /// The source color.
+ /// The destination color.
+ ///
+ /// The .
+ ///
+ public static Color Multiply(Color source, Color destination)
+ {
+ if (destination == Color.Black)
+ {
+ return Color.Black;
+ }
+
+ if (destination == Color.White)
+ {
+ return source;
+ }
+
+ // 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);
+ }
+
+ ///
+ /// Linearly interpolates from one color to another based on the given weighting.
+ ///
+ /// The first color value.
+ /// The second color value.
+ ///
+ /// 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.
+ ///
+ ///
+ /// The
+ ///
+ public static Color Lerp(Color from, Color to, float amount)
+ {
+ return new Color(Vector4.Lerp(from.ToVector4(), to.ToVector4(), amount));
+ }
+ }
+}
diff --git a/src/ImageSharp46/Colors/ColorspaceTransforms.cs b/src/ImageSharp46/Colors/ColorspaceTransforms.cs
new file mode 100644
index 0000000000..0c70dd98b1
--- /dev/null
+++ b/src/ImageSharp46/Colors/ColorspaceTransforms.cs
@@ -0,0 +1,292 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Numerics;
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// 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.
+ ///
+ public partial struct Color
+ {
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ private const float Epsilon = 0.001F;
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(Bgra32 color)
+ {
+ return new Color(color.R, color.G, color.B, color.A);
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(Cmyk cmykColor)
+ {
+ 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, 1);
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(YCbCr color)
+ {
+ byte y = color.Y;
+ int cb = color.Cb - 128;
+ int cr = color.Cr - 128;
+
+ byte r = (byte)(y + (1.402F * cr)).Clamp(0, 255);
+ byte g = (byte)(y - (0.34414F * cb) - (0.71414F * cr)).Clamp(0, 255);
+ byte b = (byte)(y + (1.772F * cb)).Clamp(0, 255);
+
+ return new Color(r, g, b);
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(CieXyz color)
+ {
+ float x = color.X / 100F;
+ float y = color.Y / 100F;
+ float z = color.Z / 100F;
+
+ // Then XYZ to RGB (multiplication by 100 was done above already)
+ float r = (x * 3.2406F) + (y * -1.5372F) + (z * -0.4986F);
+ float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F);
+ float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F);
+
+ Vector4 vector = new Vector4(r, g, b, 1).Compress();
+ return new Color(vector);
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(Hsv color)
+ {
+ float s = color.S;
+ float v = color.V;
+
+ if (Math.Abs(s) < Epsilon)
+ {
+ return new Color(v, v, v, 1);
+ }
+
+ float h = (Math.Abs(color.H - 360) < Epsilon) ? 0 : color.H / 60;
+ 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 r, g, b;
+ switch (i)
+ {
+ case 0:
+ r = v;
+ g = t;
+ b = p;
+ break;
+
+ case 1:
+ r = q;
+ g = v;
+ b = p;
+ break;
+
+ case 2:
+ r = p;
+ g = v;
+ b = t;
+ break;
+
+ case 3:
+ r = p;
+ g = q;
+ b = v;
+ break;
+
+ case 4:
+ r = t;
+ g = p;
+ b = v;
+ break;
+
+ default:
+ r = v;
+ g = p;
+ b = q;
+ break;
+ }
+
+ return new Color(r, g, b, 1);
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(Hsl color)
+ {
+ float rangedH = color.H / 360F;
+ float r = 0;
+ float g = 0;
+ float b = 0;
+ float s = color.S;
+ float l = color.L;
+
+ if (Math.Abs(l) > Epsilon)
+ {
+ if (Math.Abs(s) < Epsilon)
+ {
+ r = g = b = l;
+ }
+ else
+ {
+ float temp2 = (l < 0.5f) ? l * (1f + s) : l + s - (l * s);
+ float temp1 = (2f * l) - temp2;
+
+ r = GetColorComponent(temp1, temp2, rangedH + 0.3333333F);
+ g = GetColorComponent(temp1, temp2, rangedH);
+ b = GetColorComponent(temp1, temp2, rangedH - 0.3333333F);
+ }
+ }
+
+ return new Color(r, g, b, 1);
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(CieLab cieLabColor)
+ {
+ // First convert back to XYZ...
+ float y = (cieLabColor.L + 16F) / 116F;
+ float x = (cieLabColor.A / 500F) + y;
+ float z = y - (cieLabColor.B / 200F);
+
+ float x3 = x * x * x;
+ float y3 = y * y * y;
+ float z3 = z * z * z;
+
+ x = x3 > 0.008856F ? x3 : (x - 0.137931F) / 7.787F;
+ y = (cieLabColor.L > 7.999625F) ? y3 : (cieLabColor.L / 903.3F);
+ z = (z3 > 0.008856F) ? z3 : (z - 0.137931F) / 7.787F;
+
+ x *= 0.95047F;
+ z *= 1.08883F;
+
+ // Then XYZ to RGB (multiplication by 100 was done above already)
+ float r = (x * 3.2406F) + (y * -1.5372F) + (z * -0.4986F);
+ float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F);
+ float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F);
+
+ return new Color(new Vector4(r, g, b, 1F).Compress());
+ }
+
+ ///
+ /// Gets the color component from the given values.
+ ///
+ /// The first value.
+ /// The second value.
+ /// The third value.
+ ///
+ /// The .
+ ///
+ private static float GetColorComponent(float first, float second, float third)
+ {
+ third = MoveIntoRange(third);
+ if (third < 0.1666667F)
+ {
+ return first + ((second - first) * 6.0f * third);
+ }
+
+ if (third < 0.5)
+ {
+ return second;
+ }
+
+ if (third < 0.6666667F)
+ {
+ return first + ((second - first) * (0.6666667F - third) * 6.0f);
+ }
+
+ return first;
+ }
+
+ ///
+ /// Moves the specific value within the acceptable range for
+ /// conversion.
+ /// Used for converting colors to this type.
+ ///
+ /// The value to shift.
+ ///
+ /// The .
+ ///
+ private static float MoveIntoRange(float value)
+ {
+ if (value < 0.0)
+ {
+ value += 1.0f;
+ }
+ else if (value > 1.0)
+ {
+ value -= 1.0f;
+ }
+
+ return value;
+ }
+ }
+}
diff --git a/src/ImageSharp46/Colors/Colorspaces/Bgra32.cs b/src/ImageSharp46/Colors/Colorspaces/Bgra32.cs
new file mode 100644
index 0000000000..ff9203b03a
--- /dev/null
+++ b/src/ImageSharp46/Colors/Colorspaces/Bgra32.cs
@@ -0,0 +1,167 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+
+ ///
+ /// Represents an BGRA (blue, green, red, alpha) color.
+ ///
+ public struct Bgra32 : IEquatable
+ {
+ ///
+ /// Represents a 32 bit that has B, G, R, and A values set to zero.
+ ///
+ public static readonly Bgra32 Empty = default(Bgra32);
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private Vector4 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The blue component of this .
+ /// The green component of this .
+ /// The red component of this .
+ /// The alpha component of this .
+ public Bgra32(byte b, byte g, byte r, byte a = 255)
+ : this()
+ {
+ this.backingVector = Vector4.Clamp(new Vector4(b, g, r, a), Vector4.Zero, new Vector4(255));
+ }
+
+ ///
+ /// Gets the blue component of the color
+ ///
+ public byte B => (byte)this.backingVector.X;
+
+ ///
+ /// Gets the green component of the color
+ ///
+ public byte G => (byte)this.backingVector.Y;
+
+ ///
+ /// Gets the red component of the color
+ ///
+ public byte R => (byte)this.backingVector.Z;
+
+ ///
+ /// Gets the alpha component of the color
+ ///
+ public byte A => (byte)this.backingVector.W;
+
+ ///
+ /// Gets the integer representation of the color.
+ ///
+ public int Bgra => (this.R << 16) | (this.G << 8) | (this.B << 0) | (this.A << 24);
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.Equals(Empty);
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ ///
+ /// The instance of to convert.
+ ///
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Bgra32(Color color)
+ {
+ return new Bgra32(color.B, color.G, color.R, color.A);
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(Bgra32 left, Bgra32 right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(Bgra32 left, Bgra32 right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is Bgra32)
+ {
+ Bgra32 color = (Bgra32)obj;
+
+ return this.backingVector == color.backingVector;
+ }
+
+ return false;
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return GetHashCode(this);
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "Bgra32 [ Empty ]";
+ }
+
+ return $"Bgra32 [ B={this.B}, G={this.G}, R={this.R}, A={this.A} ]";
+ }
+
+ ///
+ public bool Equals(Bgra32 other)
+ {
+ return this.backingVector.Equals(other.backingVector);
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// The instance of to return the hash code for.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ private static int GetHashCode(Bgra32 color) => color.backingVector.GetHashCode();
+ }
+}
diff --git a/src/ImageSharp46/Colors/Colorspaces/CieLab.cs b/src/ImageSharp46/Colors/Colorspaces/CieLab.cs
new file mode 100644
index 0000000000..b0c042a3c4
--- /dev/null
+++ b/src/ImageSharp46/Colors/Colorspaces/CieLab.cs
@@ -0,0 +1,192 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+
+ ///
+ /// Represents an CIE LAB 1976 color.
+ ///
+ ///
+ public struct CieLab : IEquatable, IAlmostEquatable
+ {
+ ///
+ /// Represents a that has L, A, B values set to zero.
+ ///
+ public static readonly CieLab Empty = default(CieLab);
+
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ private const float Epsilon = 0.001f;
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private Vector3 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The lightness dimension.
+ /// The a (green - magenta) component.
+ /// The b (blue - yellow) component.
+ public CieLab(float l, float a, float b)
+ : this()
+ {
+ this.backingVector = Vector3.Clamp(new Vector3(l, a, b), new Vector3(0, -100, -100), new Vector3(100));
+ }
+
+ ///
+ /// Gets the lightness dimension.
+ /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).
+ ///
+ public float L => this.backingVector.X;
+
+ ///
+ /// Gets the a color component.
+ /// Negative is green, positive magenta.
+ ///
+ public float A => this.backingVector.Y;
+
+ ///
+ /// Gets the b color component.
+ /// Negative is blue, positive is yellow
+ ///
+ public float B => this.backingVector.Z;
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.Equals(Empty);
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ ///
+ /// The instance of to convert.
+ ///
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator CieLab(Color color)
+ {
+ // First convert to CIE XYZ
+ 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;
+ //y /= 1F;
+ z /= 1.08883F;
+
+ x = x > 0.008856F ? (float)Math.Pow(x, 0.3333333F) : ((903.3F * x) + 16F) / 116F;
+ y = y > 0.008856F ? (float)Math.Pow(y, 0.3333333F) : ((903.3F * y) + 16F) / 116F;
+ z = z > 0.008856F ? (float)Math.Pow(z, 0.3333333F) : ((903.3F * z) + 16F) / 116F;
+
+ float l = Math.Max(0, (116F * y) - 16F);
+ float a = 500F * (x - y);
+ float b = 200F * (y - z);
+
+ return new CieLab(l, a, b);
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(CieLab left, CieLab right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(CieLab left, CieLab right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return GetHashCode(this);
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "CieLab [Empty]";
+ }
+
+ return $"CieLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]";
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is CieLab)
+ {
+ return this.Equals((CieLab)obj);
+ }
+
+ return false;
+ }
+
+ ///
+ public bool Equals(CieLab other)
+ {
+ return this.AlmostEquals(other, Epsilon);
+ }
+
+ ///
+ public bool AlmostEquals(CieLab other, float precision)
+ {
+ Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
+
+ return result.X < precision
+ && result.Y < precision
+ && result.Z < precision;
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// The instance of to return the hash code for.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ private static int GetHashCode(CieLab color) => color.backingVector.GetHashCode();
+ }
+}
diff --git a/src/ImageSharp46/Colors/Colorspaces/CieXyz.cs b/src/ImageSharp46/Colors/Colorspaces/CieXyz.cs
new file mode 100644
index 0000000000..8e9d5f7965
--- /dev/null
+++ b/src/ImageSharp46/Colors/Colorspaces/CieXyz.cs
@@ -0,0 +1,184 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+
+ ///
+ /// Represents an CIE 1931 color
+ ///
+ ///
+ public struct CieXyz : IEquatable, IAlmostEquatable
+ {
+ ///
+ /// Represents a that has Y, Cb, and Cr values set to zero.
+ ///
+ public static readonly CieXyz Empty = default(CieXyz);
+
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ private const float Epsilon = 0.001f;
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private Vector3 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The y luminance component.
+ /// X is a mix (a linear combination) of cone response curves chosen to be nonnegative
+ /// Z is quasi-equal to blue stimulation, or the S cone of the human eye.
+ public CieXyz(float x, float y, float z)
+ : this()
+ {
+ // Not clamping as documentation about this space seems to indicate "usual" ranges
+ this.backingVector = new Vector3(x, y, z);
+ }
+
+ ///
+ /// Gets the Y luminance component.
+ /// A value ranging between 380 and 780.
+ ///
+ public float X => this.backingVector.X;
+
+ ///
+ /// Gets the Cb chroma component.
+ /// A value ranging between 380 and 780.
+ ///
+ public float Y => this.backingVector.Y;
+
+ ///
+ /// Gets the Cr chroma component.
+ /// A value ranging between 380 and 780.
+ ///
+ public float Z => this.backingVector.Z;
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.Equals(Empty);
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ ///
+ /// The instance of to convert.
+ ///
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator CieXyz(Color color)
+ {
+ 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);
+
+ x *= 100F;
+ y *= 100F;
+ z *= 100F;
+
+ return new CieXyz(x, y, z);
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(CieXyz left, CieXyz right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(CieXyz left, CieXyz right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return GetHashCode(this);
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "CieXyz [ Empty ]";
+ }
+
+ return $"CieXyz [ X={this.X:#0.##}, Y={this.Y:#0.##}, Z={this.Z:#0.##} ]";
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is CieXyz)
+ {
+ return this.Equals((CieXyz)obj);
+ }
+
+ return false;
+ }
+
+ ///
+ public bool Equals(CieXyz other)
+ {
+ return this.AlmostEquals(other, Epsilon);
+ }
+
+ ///
+ public bool AlmostEquals(CieXyz other, float precision)
+ {
+ Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
+
+ return result.X < precision
+ && result.Y < precision
+ && result.Z < precision;
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// The instance of to return the hash code for.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ private static int GetHashCode(CieXyz color) => color.backingVector.GetHashCode();
+ }
+}
diff --git a/src/ImageSharp46/Colors/Colorspaces/Cmyk.cs b/src/ImageSharp46/Colors/Colorspaces/Cmyk.cs
new file mode 100644
index 0000000000..15bea263cc
--- /dev/null
+++ b/src/ImageSharp46/Colors/Colorspaces/Cmyk.cs
@@ -0,0 +1,195 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+
+ ///
+ /// Represents an CMYK (cyan, magenta, yellow, keyline) color.
+ ///
+ public struct Cmyk : IEquatable, IAlmostEquatable
+ {
+ ///
+ /// Represents a that has C, M, Y, and K values set to zero.
+ ///
+ public static readonly Cmyk Empty = default(Cmyk);
+
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ private const float Epsilon = 0.001f;
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private Vector4 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The cyan component.
+ /// The magenta component.
+ /// The yellow component.
+ /// The keyline black component.
+ public Cmyk(float c, float m, float y, float k)
+ : this()
+ {
+ this.backingVector = Vector4.Clamp(new Vector4(c, m, y, k), Vector4.Zero, Vector4.One);
+ }
+
+ ///
+ /// Gets the cyan color component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float C => this.backingVector.X;
+
+ ///
+ /// Gets the magenta color component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float M => this.backingVector.Y;
+
+ ///
+ /// Gets the yellow color component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float Y => this.backingVector.Z;
+
+ ///
+ /// Gets the keyline black color component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float K => this.backingVector.W;
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.Equals(Empty);
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ ///
+ /// The instance of to convert.
+ ///
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Cmyk(Color color)
+ {
+ 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));
+
+ if (Math.Abs(k - 1.0f) <= Epsilon)
+ {
+ return new Cmyk(0, 0, 0, 1);
+ }
+
+ c = (c - k) / (1 - k);
+ m = (m - k) / (1 - k);
+ y = (y - k) / (1 - k);
+
+ return new Cmyk(c, m, y, k);
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(Cmyk left, Cmyk right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(Cmyk left, Cmyk right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return GetHashCode(this);
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "Cmyk [Empty]";
+ }
+
+ return $"Cmyk [ C={this.C:#0.##}, M={this.M:#0.##}, Y={this.Y:#0.##}, K={this.K:#0.##}]";
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is Cmyk)
+ {
+ return this.Equals((Cmyk)obj);
+ }
+
+ return false;
+ }
+
+ ///
+ public bool Equals(Cmyk other)
+ {
+ return this.AlmostEquals(other, Epsilon);
+ }
+
+ ///
+ public bool AlmostEquals(Cmyk other, float precision)
+ {
+ Vector4 result = Vector4.Abs(this.backingVector - other.backingVector);
+
+ return result.X < precision
+ && result.Y < precision
+ && result.Z < precision
+ && result.W < precision;
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// The instance of to return the hash code for.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ private static int GetHashCode(Cmyk color) => color.backingVector.GetHashCode();
+ }
+}
diff --git a/src/ImageSharp46/Colors/Colorspaces/Hsl.cs b/src/ImageSharp46/Colors/Colorspaces/Hsl.cs
new file mode 100644
index 0000000000..1cc54ec8c3
--- /dev/null
+++ b/src/ImageSharp46/Colors/Colorspaces/Hsl.cs
@@ -0,0 +1,213 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+
+ ///
+ /// Represents a Hsl (hue, saturation, lightness) color.
+ ///
+ public struct Hsl : IEquatable, IAlmostEquatable
+ {
+ ///
+ /// Represents a that has H, S, and L values set to zero.
+ ///
+ public static readonly Hsl Empty = default(Hsl);
+
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ private const float Epsilon = 0.001F;
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private Vector3 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The h hue component.
+ /// The s saturation component.
+ /// The l value (lightness) component.
+ public Hsl(float h, float s, float l)
+ {
+ this.backingVector = Vector3.Clamp(new Vector3(h, s, l), Vector3.Zero, new Vector3(360, 1, 1));
+ }
+
+ ///
+ /// Gets the hue component.
+ /// A value ranging between 0 and 360.
+ ///
+ public float H => this.backingVector.X;
+
+ ///
+ /// Gets the saturation component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float S => this.backingVector.Y;
+
+ ///
+ /// Gets the lightness component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float L => this.backingVector.Z;
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.Equals(Empty);
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Hsl(Color color)
+ {
+ 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));
+ float chroma = max - min;
+ float h = 0;
+ float s = 0;
+ float l = (max + min) / 2;
+
+ if (Math.Abs(chroma) < Epsilon)
+ {
+ return new Hsl(0, s, l);
+ }
+
+ if (Math.Abs(r - max) < Epsilon)
+ {
+ h = (g - b) / chroma;
+ }
+ else if (Math.Abs(g - max) < Epsilon)
+ {
+ h = 2 + ((b - r) / chroma);
+ }
+ else if (Math.Abs(b - max) < Epsilon)
+ {
+ h = 4 + ((r - g) / chroma);
+ }
+
+ h *= 60;
+ if (h < 0.0)
+ {
+ h += 360;
+ }
+
+ if (l <= .5f)
+ {
+ s = chroma / (max + min);
+ }
+ else
+ {
+ s = chroma / (2 - chroma);
+ }
+
+ return new Hsl(h, s, l);
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(Hsl left, Hsl right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(Hsl left, Hsl right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return GetHashCode(this);
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "Hsl [ Empty ]";
+ }
+
+ return $"Hsl [ H={this.H:#0.##}, S={this.S:#0.##}, L={this.L:#0.##} ]";
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is Hsl)
+ {
+ return this.Equals((Hsl)obj);
+ }
+
+ return false;
+ }
+
+ ///
+ public bool Equals(Hsl other)
+ {
+ return this.AlmostEquals(other, Epsilon);
+ }
+
+ ///
+ public bool AlmostEquals(Hsl other, float precision)
+ {
+ Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
+
+ return result.X < precision
+ && result.Y < precision
+ && result.Z < precision;
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// The instance of to return the hash code for.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ private static int GetHashCode(Hsl color) => color.backingVector.GetHashCode();
+ }
+}
diff --git a/src/ImageSharp46/Colors/Colorspaces/Hsv.cs b/src/ImageSharp46/Colors/Colorspaces/Hsv.cs
new file mode 100644
index 0000000000..b55e07e6bd
--- /dev/null
+++ b/src/ImageSharp46/Colors/Colorspaces/Hsv.cs
@@ -0,0 +1,206 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+
+ ///
+ /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
+ ///
+ public struct Hsv : IEquatable, IAlmostEquatable
+ {
+ ///
+ /// Represents a that has H, S, and V values set to zero.
+ ///
+ public static readonly Hsv Empty = default(Hsv);
+
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ private const float Epsilon = 0.001F;
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private Vector3 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The h hue component.
+ /// The s saturation component.
+ /// The v value (brightness) component.
+ public Hsv(float h, float s, float v)
+ {
+ this.backingVector = Vector3.Clamp(new Vector3(h, s, v), Vector3.Zero, new Vector3(360, 1, 1));
+ }
+
+ ///
+ /// Gets the hue component.
+ /// A value ranging between 0 and 360.
+ ///
+ public float H => this.backingVector.X;
+
+ ///
+ /// Gets the saturation component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float S => this.backingVector.Y;
+
+ ///
+ /// Gets the value (brightness) component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float V => this.backingVector.Z;
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.Equals(Empty);
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Hsv(Color color)
+ {
+ 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));
+ float chroma = max - min;
+ float h = 0;
+ float s = 0;
+ float v = max;
+
+ if (Math.Abs(chroma) < Epsilon)
+ {
+ return new Hsv(0, s, v);
+ }
+
+ if (Math.Abs(r - max) < Epsilon)
+ {
+ h = (g - b) / chroma;
+ }
+ else if (Math.Abs(g - max) < Epsilon)
+ {
+ h = 2 + ((b - r) / chroma);
+ }
+ else if (Math.Abs(b - max) < Epsilon)
+ {
+ h = 4 + ((r - g) / chroma);
+ }
+
+ h *= 60;
+ if (h < 0.0)
+ {
+ h += 360;
+ }
+
+ s = chroma / v;
+
+ return new Hsv(h, s, v);
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(Hsv left, Hsv right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(Hsv left, Hsv right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return GetHashCode(this);
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "Hsv [ Empty ]";
+ }
+
+ return $"Hsv [ H={this.H:#0.##}, S={this.S:#0.##}, V={this.V:#0.##} ]";
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is Hsv)
+ {
+ return this.Equals((Hsv)obj);
+ }
+
+ return false;
+ }
+
+ ///
+ public bool Equals(Hsv other)
+ {
+ return this.AlmostEquals(other, Epsilon);
+ }
+
+ ///
+ public bool AlmostEquals(Hsv other, float precision)
+ {
+ Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
+
+ return result.X < precision
+ && result.Y < precision
+ && result.Z < precision;
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// The instance of to return the hash code for.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ private static int GetHashCode(Hsv color) => color.backingVector.GetHashCode();
+ }
+}
diff --git a/src/ImageSharp46/Colors/Colorspaces/IAlmostEquatable.cs b/src/ImageSharp46/Colors/Colorspaces/IAlmostEquatable.cs
new file mode 100644
index 0000000000..1163657edb
--- /dev/null
+++ b/src/ImageSharp46/Colors/Colorspaces/IAlmostEquatable.cs
@@ -0,0 +1,30 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+
+ ///
+ /// Defines a generalized method that a value type or class implements to create
+ /// a type-specific method for determining approximate equality of instances.
+ ///
+ /// The type of objects to compare.
+ /// The object specifying the type to specify precision with.
+ public interface IAlmostEquatable
+ where TPrecision : struct, IComparable
+ {
+ ///
+ /// Indicates whether the current object is equal to another object of the same type
+ /// when compared to the specified precision level.
+ ///
+ /// An object to compare with this object.
+ /// The object specifying the level of precision.
+ ///
+ /// true if the current object is equal to the other parameter; otherwise, false.
+ ///
+ bool AlmostEquals(TColor other, TPrecision precision);
+ }
+}
diff --git a/src/ImageSharp46/Colors/Colorspaces/YCbCr.cs b/src/ImageSharp46/Colors/Colorspaces/YCbCr.cs
new file mode 100644
index 0000000000..c9a0872c51
--- /dev/null
+++ b/src/ImageSharp46/Colors/Colorspaces/YCbCr.cs
@@ -0,0 +1,157 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.ComponentModel;
+
+ ///
+ /// Represents an YCbCr (luminance, blue chroma, red chroma) color conforming to the full range standard used in digital imaging systems.
+ ///
+ ///
+ public struct YCbCr : IEquatable
+ {
+ ///
+ /// Represents a that has Y, Cb, and Cr values set to zero.
+ ///
+ public static readonly YCbCr Empty = default(YCbCr);
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The y luminance component.
+ /// The cb chroma component.
+ /// The cr chroma component.
+ public YCbCr(byte y, byte cb, byte cr)
+ : this()
+ {
+ this.Y = y;
+ this.Cb = cb;
+ this.Cr = cr;
+ }
+
+ ///
+ /// Gets the Y luminance component.
+ /// A value ranging between 0 and 255.
+ ///
+ public byte Y { get; }
+
+ ///
+ /// Gets the Cb chroma component.
+ /// A value ranging between 0 and 255.
+ ///
+ public byte Cb { get; }
+
+ ///
+ /// Gets the Cr chroma component.
+ /// A value ranging between 0 and 255.
+ ///
+ public byte Cr { get; }
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.Equals(Empty);
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ ///
+ /// The instance of to convert.
+ ///
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator YCbCr(Color color)
+ {
+ byte r = color.R;
+ byte g = color.G;
+ byte b = color.B;
+
+ byte y = (byte)((0.299F * r) + (0.587F * g) + (0.114F * b));
+ byte cb = (byte)(128 + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)));
+ byte cr = (byte)(128 + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)));
+
+ return new YCbCr(y, cb, cr);
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(YCbCr left, YCbCr right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(YCbCr left, YCbCr right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = this.Y.GetHashCode();
+ hashCode = (hashCode * 397) ^ this.Cb.GetHashCode();
+ hashCode = (hashCode * 397) ^ this.Cr.GetHashCode();
+ return hashCode;
+ }
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "YCbCr [ Empty ]";
+ }
+
+ return $"YCbCr [ Y={this.Y}, Cb={this.Cb}, Cr={this.Cr} ]";
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is YCbCr)
+ {
+ return this.Equals((YCbCr)obj);
+ }
+
+ return false;
+ }
+
+ ///
+ public bool Equals(YCbCr other)
+ {
+ return this.Y == other.Y && this.Cb == other.Cb && this.Cr == other.Cr;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp46/Colors/ComponentOrder.cs b/src/ImageSharp46/Colors/ComponentOrder.cs
new file mode 100644
index 0000000000..7372ab9bfa
--- /dev/null
+++ b/src/ImageSharp46/Colors/ComponentOrder.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// Enumerates the various component orders.
+ ///
+ public enum ComponentOrder
+ {
+ ///
+ /// Z-> Y-> X order. Equivalent to B-> G-> R in
+ ///
+ ZYX,
+
+ ///
+ /// Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in
+ ///
+ ZYXW,
+
+ ///
+ /// X-> Y-> Z order. Equivalent to R-> G-> B in
+ ///
+ XYZ,
+
+ ///
+ /// X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in
+ ///
+ XYZW,
+ }
+}
diff --git a/src/ImageSharp46/Colors/PackedPixel/IPackedBytes.cs b/src/ImageSharp46/Colors/PackedPixel/IPackedBytes.cs
new file mode 100644
index 0000000000..5793ea1ba7
--- /dev/null
+++ b/src/ImageSharp46/Colors/PackedPixel/IPackedBytes.cs
@@ -0,0 +1,31 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// An interface that converts packed vector types to and from values,
+ /// allowing multiple encodings to be manipulated in a generic manner.
+ ///
+ public interface IPackedBytes
+ {
+ ///
+ /// Gets the packed representation from the gives bytes.
+ ///
+ /// The x-component.
+ /// The y-component.
+ /// The z-component.
+ /// The w-component.
+ void PackFromBytes(byte x, byte y, byte z, byte w);
+
+ ///
+ /// Sets the packed representation into the gives bytes.
+ ///
+ /// The bytes to set the color in.
+ /// The starting index of the .
+ /// The order of the components.
+ void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder);
+ }
+}
diff --git a/src/ImageSharp46/Colors/PackedPixel/IPackedPixel.cs b/src/ImageSharp46/Colors/PackedPixel/IPackedPixel.cs
new file mode 100644
index 0000000000..54602380f7
--- /dev/null
+++ b/src/ImageSharp46/Colors/PackedPixel/IPackedPixel.cs
@@ -0,0 +1,16 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// An interface that represents a packed pixel type.
+ ///
+ /// The packed format. uint, long, float.
+ public interface IPackedPixel : IPackedVector, IPackedBytes
+ where TPacked : struct
+ {
+ }
+}
diff --git a/src/ImageSharp46/Colors/PackedPixel/IPackedVector.cs b/src/ImageSharp46/Colors/PackedPixel/IPackedVector.cs
new file mode 100644
index 0000000000..cdf250a01f
--- /dev/null
+++ b/src/ImageSharp46/Colors/PackedPixel/IPackedVector.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System.Numerics;
+
+ ///
+ /// An interface that converts packed vector types to and from values,
+ /// allowing multiple encodings to be manipulated in a generic manner.
+ ///
+ /// The packed format. uint, long, float.
+ public interface IPackedVector : IPackedVector
+ where TPacked : struct
+ {
+ ///
+ /// Gets or sets the packed representation of the value.
+ ///
+ TPacked PackedValue { get; set; }
+ }
+
+ ///
+ /// An interface that converts packed vector types to and from values.
+ ///
+ public interface IPackedVector
+ {
+ ///
+ /// Sets the packed representation from a .
+ ///
+ /// The vector to create the packed representation from.
+ void PackFromVector4(Vector4 vector);
+
+ ///
+ /// Expands the packed representation into a .
+ /// The vector components are typically expanded in least to greatest significance order.
+ ///
+ /// The .
+ Vector4 ToVector4();
+ }
+}
diff --git a/src/ImageSharp46/Colors/RgbaComponent.cs b/src/ImageSharp46/Colors/RgbaComponent.cs
new file mode 100644
index 0000000000..ed85fb86bb
--- /dev/null
+++ b/src/ImageSharp46/Colors/RgbaComponent.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// Enumerates the RGBA (red, green, blue, alpha) color components.
+ ///
+ public enum RgbaComponent
+ {
+ ///
+ /// The red component.
+ ///
+ R = 0,
+
+ ///
+ /// The green component.
+ ///
+ G = 1,
+
+ ///
+ /// The blue component.
+ ///
+ B = 2,
+
+ ///
+ /// The alpha component.
+ ///
+ A = 3
+ }
+}
diff --git a/src/ImageSharp46/Common/Exceptions/ImageFormatException.cs b/src/ImageSharp46/Common/Exceptions/ImageFormatException.cs
new file mode 100644
index 0000000000..70491ba22e
--- /dev/null
+++ b/src/ImageSharp46/Common/Exceptions/ImageFormatException.cs
@@ -0,0 +1,45 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+
+ ///
+ /// The exception that is thrown when the library tries to load
+ /// an image, which has an invalid format.
+ ///
+ public class ImageFormatException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ImageFormatException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the name of the
+ /// parameter that causes this exception.
+ ///
+ /// The error message that explains the reason for this exception.
+ public ImageFormatException(string errorMessage)
+ : base(errorMessage)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with a specified
+ /// error message and the exception that is the cause of this exception.
+ ///
+ /// The error message that explains the reason for this exception.
+ /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic)
+ /// if no inner exception is specified.
+ public ImageFormatException(string errorMessage, Exception innerException)
+ : base(errorMessage, innerException)
+ {
+ }
+ }
+}
diff --git a/src/ImageSharp46/Common/Exceptions/ImageProcessingException.cs b/src/ImageSharp46/Common/Exceptions/ImageProcessingException.cs
new file mode 100644
index 0000000000..a59be9ca8f
--- /dev/null
+++ b/src/ImageSharp46/Common/Exceptions/ImageProcessingException.cs
@@ -0,0 +1,44 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+
+ ///
+ /// The exception that is thrown when an error occurs when applying a process to an image.
+ ///
+ public class ImageProcessingException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ImageProcessingException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the name of the
+ /// parameter that causes this exception.
+ ///
+ /// The error message that explains the reason for this exception.
+ public ImageProcessingException(string errorMessage)
+ : base(errorMessage)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with a specified
+ /// error message and the exception that is the cause of this exception.
+ ///
+ /// The error message that explains the reason for this exception.
+ /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic)
+ /// if no inner exception is specified.
+ public ImageProcessingException(string errorMessage, Exception innerException)
+ : base(errorMessage, innerException)
+ {
+ }
+ }
+}
diff --git a/src/ImageSharp46/Common/Extensions/ByteExtensions.cs b/src/ImageSharp46/Common/Extensions/ByteExtensions.cs
new file mode 100644
index 0000000000..00eab47fc1
--- /dev/null
+++ b/src/ImageSharp46/Common/Extensions/ByteExtensions.cs
@@ -0,0 +1,60 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+
+ ///
+ /// Extension methods for the struct.
+ ///
+ internal static class ByteExtensions
+ {
+ ///
+ /// Converts a byte array to a new array where each value in the original array is represented
+ /// by a the specified number of bits.
+ ///
+ /// The bytes to convert from. Cannot be null.
+ /// The number of bits per value.
+ /// The resulting array. Is never null.
+ /// is null.
+ /// is less than or equals than zero.
+ public static byte[] ToArrayByBitsLength(this byte[] bytes, int bits)
+ {
+ Guard.NotNull(bytes, "bytes");
+ Guard.MustBeGreaterThan(bits, 0, "bits");
+
+ byte[] result;
+
+ if (bits < 8)
+ {
+ result = new byte[bytes.Length * 8 / bits];
+
+ // BUGFIX I dont think it should be there, but I am not sure if it breaks something else
+ // int factor = (int)Math.Pow(2, bits) - 1;
+ int mask = 0xFF >> (8 - bits);
+ int resultOffset = 0;
+
+ foreach (byte b in bytes)
+ {
+ for (int shift = 0; shift < 8; shift += bits)
+ {
+ int colorIndex = (b >> (8 - bits - shift)) & mask; // * (255 / factor);
+
+ result[resultOffset] = (byte)colorIndex;
+
+ resultOffset++;
+ }
+ }
+ }
+ else
+ {
+ result = bytes;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/ImageSharp46/Common/Extensions/ComparableExtensions.cs b/src/ImageSharp46/Common/Extensions/ComparableExtensions.cs
new file mode 100644
index 0000000000..8f056ff9d4
--- /dev/null
+++ b/src/ImageSharp46/Common/Extensions/ComparableExtensions.cs
@@ -0,0 +1,169 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+
+ ///
+ /// Extension methods for classes that implement .
+ ///
+ internal static class ComparableExtensions
+ {
+ ///
+ /// Restricts a to be within a specified range.
+ ///
+ /// The The value to clamp.
+ /// The minimum value. If value is less than min, min will be returned.
+ /// The maximum value. If value is greater than max, max will be returned.
+ ///
+ /// The representing the clamped value.
+ ///
+ public static byte Clamp(this byte value, byte min, byte max)
+ {
+ // Order is important here as someone might set min to higher than max.
+ if (value > max)
+ {
+ return max;
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Restricts a to be within a specified range.
+ ///
+ /// The The value to clamp.
+ /// The minimum value. If value is less than min, min will be returned.
+ /// The maximum value. If value is greater than max, max will be returned.
+ ///
+ /// The representing the clamped value.
+ ///
+ public static uint Clamp(this uint value, uint min, uint max)
+ {
+ if (value > max)
+ {
+ return max;
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Restricts a to be within a specified range.
+ ///
+ /// The The value to clamp.
+ /// The minimum value. If value is less than min, min will be returned.
+ /// The maximum value. If value is greater than max, max will be returned.
+ ///
+ /// The representing the clamped value.
+ ///
+ public static int Clamp(this int value, int min, int max)
+ {
+ if (value > max)
+ {
+ return max;
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Restricts a to be within a specified range.
+ ///
+ /// The The value to clamp.
+ /// The minimum value. If value is less than min, min will be returned.
+ /// The maximum value. If value is greater than max, max will be returned.
+ ///
+ /// The representing the clamped value.
+ ///
+ public static float Clamp(this float value, float min, float max)
+ {
+ if (value > max)
+ {
+ return max;
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Restricts a to be within a specified range.
+ ///
+ /// The The value to clamp.
+ /// The minimum value. If value is less than min, min will be returned.
+ /// The maximum value. If value is greater than max, max will be returned.
+ ///
+ /// The representing the clamped value.
+ ///
+ public static double Clamp(this double value, double min, double max)
+ {
+ if (value > max)
+ {
+ return max;
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Converts an to a first restricting the value between the
+ /// minimum and maximum allowable ranges.
+ ///
+ /// The this method extends.
+ /// The
+ public static byte ToByte(this int value)
+ {
+ return (byte)value.Clamp(0, 255);
+ }
+
+ ///
+ /// Converts an to a first restricting the value between the
+ /// minimum and maximum allowable ranges.
+ ///
+ /// The this method extends.
+ /// The
+ public static byte ToByte(this float value)
+ {
+ return (byte)value.Clamp(0, 255);
+ }
+
+ ///
+ /// Converts an to a first restricting the value between the
+ /// minimum and maximum allowable ranges.
+ ///
+ /// The this method extends.
+ /// The
+ public static byte ToByte(this double value)
+ {
+ return (byte)value.Clamp(0, 255);
+ }
+ }
+}
diff --git a/src/ImageSharp46/Common/Extensions/EnumerableExtensions.cs b/src/ImageSharp46/Common/Extensions/EnumerableExtensions.cs
new file mode 100644
index 0000000000..af8e50c7be
--- /dev/null
+++ b/src/ImageSharp46/Common/Extensions/EnumerableExtensions.cs
@@ -0,0 +1,88 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Encapsulates a series of time saving extension methods to the interface.
+ ///
+ public static class EnumerableExtensions
+ {
+ ///
+ /// Generates a sequence of integral numbers within a specified range.
+ ///
+ ///
+ /// The start index, inclusive.
+ ///
+ ///
+ /// The end index, exclusive.
+ ///
+ ///
+ /// The incremental step.
+ ///
+ ///
+ /// The that contains a range of sequential integral numbers.
+ ///
+ public static IEnumerable SteppedRange(int fromInclusive, int toExclusive, int step)
+ {
+ // Borrowed from Enumerable.Range
+ long num = (fromInclusive + toExclusive) - 1L;
+ if ((toExclusive < 0) || (num > 0x7fffffffL))
+ {
+ throw new ArgumentOutOfRangeException(nameof(toExclusive));
+ }
+
+ return RangeIterator(fromInclusive, i => i < toExclusive, step);
+ }
+
+ ///
+ /// Generates a sequence of integral numbers within a specified range.
+ ///
+ ///
+ /// The start index, inclusive.
+ ///
+ ///
+ /// A method that has one parameter and returns a calculating the end index
+ ///
+ ///
+ /// The incremental step.
+ ///
+ ///
+ /// The that contains a range of sequential integral numbers.
+ ///
+ public static IEnumerable SteppedRange(int fromInclusive, Func toDelegate, int step)
+ {
+ return RangeIterator(fromInclusive, toDelegate, step);
+ }
+
+ ///
+ /// Generates a sequence of integral numbers within a specified range.
+ ///
+ ///
+ /// The start index, inclusive.
+ ///
+ ///
+ /// A method that has one parameter and returns a calculating the end index
+ ///
+ ///
+ /// The incremental step.
+ ///
+ ///
+ /// The that contains a range of sequential integral numbers.
+ ///
+ private static IEnumerable RangeIterator(int fromInclusive, Func toDelegate, int step)
+ {
+ int i = fromInclusive;
+ while (toDelegate(i))
+ {
+ yield return i;
+ i += step;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp46/Common/Extensions/Vector4Extensions.cs b/src/ImageSharp46/Common/Extensions/Vector4Extensions.cs
new file mode 100644
index 0000000000..23fce0173c
--- /dev/null
+++ b/src/ImageSharp46/Common/Extensions/Vector4Extensions.cs
@@ -0,0 +1,83 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+
+ ///
+ /// Extension methods for the struct.
+ ///
+ public static class Vector4Extensions
+ {
+ ///
+ /// Compresses a linear color signal to its sRGB equivalent.
+ ///
+ ///
+ ///
+ /// The whose signal to compress.
+ /// The .
+ public static Vector4 Compress(this Vector4 linear)
+ {
+ // TODO: Is there a faster way to do this?
+ return new Vector4(Compress(linear.X), Compress(linear.Y), Compress(linear.Z), linear.W);
+ }
+
+ ///
+ /// Expands an sRGB color signal to its linear equivalent.
+ ///
+ ///
+ ///
+ /// The whose signal to expand.
+ /// The .
+ public static Vector4 Expand(this Vector4 gamma)
+ {
+ // TODO: Is there a faster way to do this?
+ return new Vector4(Expand(gamma.X), Expand(gamma.Y), Expand(gamma.Z), gamma.W);
+ }
+
+ ///
+ /// Gets the compressed sRGB value from an linear signal.
+ ///
+ ///
+ ///
+ /// The signal value to compress.
+ ///
+ /// The .
+ ///
+ [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;
+ }
+
+ ///
+ /// Gets the expanded linear value from an sRGB signal.
+ ///
+ ///
+ ///
+ /// The signal value to expand.
+ ///
+ /// The .
+ ///
+ [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);
+ }
+ }
+}
diff --git a/src/ImageSharp46/Common/Helpers/Guard.cs b/src/ImageSharp46/Common/Helpers/Guard.cs
new file mode 100644
index 0000000000..ecb497d3e0
--- /dev/null
+++ b/src/ImageSharp46/Common/Helpers/Guard.cs
@@ -0,0 +1,205 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Diagnostics;
+
+ ///
+ /// Provides methods to protect against invalid parameters.
+ ///
+ [DebuggerStepThrough]
+ internal static class Guard
+ {
+ ///
+ /// Verifies, that the method parameter with specified object value is not null
+ /// and throws an exception if it is found to be so.
+ ///
+ /// The target object, which cannot be null.
+ /// The name of the parameter that is to be checked.
+ /// The error message, if any to add to the exception.
+ /// is null
+ public static void NotNull(object target, string parameterName, string message = "")
+ {
+ if (target == null)
+ {
+ if (!string.IsNullOrWhiteSpace(message))
+ {
+ throw new ArgumentNullException(parameterName, message);
+ }
+
+ throw new ArgumentNullException(parameterName);
+ }
+ }
+
+ ///
+ /// Verifies, that the string method parameter with specified object value and message
+ /// is not null, not empty and does not contain only blanks and throws an exception
+ /// if the object is null.
+ ///
+ /// The target string, which should be checked against being null or empty.
+ /// Name of the parameter.
+ /// is null.
+ /// is empty or contains only blanks.
+ public static void NotNullOrEmpty(string target, string parameterName)
+ {
+ if (target == null)
+ {
+ throw new ArgumentNullException(parameterName);
+ }
+
+ if (string.IsNullOrWhiteSpace(target))
+ {
+ throw new ArgumentException("Value cannot be null or empty and cannot contain only blanks.", parameterName);
+ }
+ }
+
+ ///
+ /// Verifies that the specified value is less than a maximum value
+ /// and throws an exception if it is not.
+ ///
+ /// The target value, which should be validated.
+ /// The maximum value.
+ /// The name of the parameter that is to be checked.
+ /// The type of the value.
+ ///
+ /// is greater than the maximum value.
+ ///
+ public static void MustBeLessThan(TValue value, TValue max, string parameterName)
+ where TValue : IComparable
+ {
+ if (value.CompareTo(max) >= 0)
+ {
+ throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than {max}.");
+ }
+ }
+
+ ///
+ /// Verifies that the specified value is less than or equal to a maximum value
+ /// and throws an exception if it is not.
+ ///
+ /// The target value, which should be validated.
+ /// The maximum value.
+ /// The name of the parameter that is to be checked.
+ /// The type of the value.
+ ///
+ /// is greater than the maximum value.
+ ///
+ public static void MustBeLessThanOrEqualTo(TValue value, TValue max, string parameterName)
+ where TValue : IComparable
+ {
+ if (value.CompareTo(max) > 0)
+ {
+ throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than or equal to {max}.");
+ }
+ }
+
+ ///
+ /// Verifies that the specified value is greater than a minimum value
+ /// and throws an exception if it is not.
+ ///
+ /// The target value, which should be validated.
+ /// The minimum value.
+ /// The name of the parameter that is to be checked.
+ /// The type of the value.
+ ///
+ /// is less than the minimum value.
+ ///
+ public static void MustBeGreaterThan(TValue value, TValue min, string parameterName)
+ where TValue : IComparable
+ {
+ if (value.CompareTo(min) <= 0)
+ {
+ throw new ArgumentOutOfRangeException(
+ parameterName,
+ $"Value must be greater than {min}.");
+ }
+ }
+
+ ///
+ /// Verifies that the specified value is greater than or equal to a minimum value
+ /// and throws an exception if it is not.
+ ///
+ /// The target value, which should be validated.
+ /// The minimum value.
+ /// The name of the parameter that is to be checked.
+ /// The type of the value.
+ ///
+ /// is less than the minimum value.
+ ///
+ public static void MustBeGreaterThanOrEqualTo(TValue value, TValue min, string parameterName)
+ where TValue : IComparable
+ {
+ if (value.CompareTo(min) < 0)
+ {
+ throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min}.");
+ }
+ }
+
+ ///
+ /// Verifies that the specified value is greater than or equal to a minimum value and less than
+ /// or equal to a maximum value and throws an exception if it is not.
+ ///
+ /// The target value, which should be validated.
+ /// The minimum value.
+ /// The maximum value.
+ /// The name of the parameter that is to be checked.
+ /// The type of the value.
+ ///
+ /// is less than the minimum value of greater than the maximum value.
+ ///
+ public static void MustBeBetweenOrEqualTo(TValue value, TValue min, TValue max, string parameterName)
+ where TValue : IComparable
+ {
+ if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0)
+ {
+ throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min} and less than or equal to {max}.");
+ }
+ }
+
+ ///
+ /// Verifies, that the method parameter with specified target value is true
+ /// and throws an exception if it is found to be so.
+ ///
+ ///
+ /// The target value, which cannot be false.
+ ///
+ ///
+ /// The name of the parameter that is to be checked.
+ ///
+ ///
+ /// The error message, if any to add to the exception.
+ ///
+ ///
+ /// is false
+ ///
+ public static void IsTrue(bool target, string parameterName, string message)
+ {
+ if (!target)
+ {
+ throw new ArgumentException(message, parameterName);
+ }
+ }
+
+ ///
+ /// Verifies, that the method parameter with specified target value is false
+ /// and throws an exception if it is found to be so.
+ ///
+ /// The target value, which cannot be true.
+ /// The name of the parameter that is to be checked.
+ /// The error message, if any to add to the exception.
+ ///
+ /// is true
+ ///
+ public static void IsFalse(bool target, string parameterName, string message)
+ {
+ if (target)
+ {
+ throw new ArgumentException(message, parameterName);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp46/Common/Helpers/ImageMaths.cs b/src/ImageSharp46/Common/Helpers/ImageMaths.cs
new file mode 100644
index 0000000000..9b3c623825
--- /dev/null
+++ b/src/ImageSharp46/Common/Helpers/ImageMaths.cs
@@ -0,0 +1,293 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Linq;
+ using System.Numerics;
+
+ ///
+ /// Provides common mathematical methods.
+ ///
+ internal static class ImageMaths
+ {
+ ///
+ /// Returns how many bits are required to store the specified number of colors.
+ /// Performs a Log2() on the value.
+ ///
+ /// The number of colors.
+ ///
+ /// The
+ ///
+ public static int GetBitsNeededForColorDepth(int colors)
+ {
+ return (int)Math.Ceiling(Math.Log(colors, 2));
+ }
+
+ ///
+ /// Implementation of 1D Gaussian G(x) function
+ ///
+ /// The x provided to G(x).
+ /// The spread of the blur.
+ /// The Gaussian G(x)
+ public static float Gaussian(float x, float sigma)
+ {
+ const float Numerator = 1.0f;
+ float denominator = (float)(Math.Sqrt(2 * Math.PI) * sigma);
+
+ float exponentNumerator = -x * x;
+ float exponentDenominator = (float)(2 * Math.Pow(sigma, 2));
+
+ float left = Numerator / denominator;
+ float right = (float)Math.Exp(exponentNumerator / exponentDenominator);
+
+ return left * right;
+ }
+
+ ///
+ /// Returns the result of a B-C filter against the given value.
+ ///
+ ///
+ /// The value to process.
+ /// The B-Spline curve variable.
+ /// The Cardinal curve variable.
+ ///
+ /// The .
+ ///
+ public static float GetBcValue(float x, float b, float c)
+ {
+ float temp;
+
+ if (x < 0F)
+ {
+ x = -x;
+ }
+
+ temp = x * x;
+ if (x < 1F)
+ {
+ x = ((12 - (9 * b) - (6 * c)) * (x * temp)) + ((-18 + (12 * b) + (6 * c)) * temp) + (6 - (2 * b));
+ return x / 6F;
+ }
+
+ 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 / 6F;
+ }
+
+ return 0F;
+ }
+
+ ///
+ /// Gets the result of a sine cardinal function for the given value.
+ ///
+ /// The value to calculate the result for.
+ ///
+ /// The .
+ ///
+ public static float SinC(float x)
+ {
+ const float Epsilon = .00001F;
+
+ if (Math.Abs(x) > Epsilon)
+ {
+ x *= (float)Math.PI;
+ return Clean((float)Math.Sin(x) / x);
+ }
+
+ return 1.0f;
+ }
+
+ ///
+ /// Returns the given degrees converted to radians.
+ ///
+ /// The angle in degrees.
+ ///
+ /// The representing the degree as radians.
+ ///
+ public static float DegreesToRadians(float degrees)
+ {
+ return degrees * (float)(Math.PI / 180);
+ }
+
+ ///
+ /// Gets the bounding from the given points.
+ ///
+ ///
+ /// The designating the top left position.
+ ///
+ ///
+ /// The designating the bottom right position.
+ ///
+ ///
+ /// The bounding .
+ ///
+ public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight)
+ {
+ return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
+ }
+
+ ///
+ /// Gets the bounding from the given matrix.
+ ///
+ /// The source rectangle.
+ /// The transformation matrix.
+ ///
+ /// The .
+ ///
+ public static Rectangle GetBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix)
+ {
+ Vector2 leftTop = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix);
+ Vector2 rightTop = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix);
+ Vector2 leftBottom = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix);
+ Vector2 rightBottom = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix);
+
+ Vector2[] allCorners = { leftTop, rightTop, leftBottom, rightBottom };
+ float extentX = allCorners.Select(v => v.X).Max() - allCorners.Select(v => v.X).Min();
+ float extentY = allCorners.Select(v => v.Y).Max() - allCorners.Select(v => v.Y).Min();
+ return new Rectangle(0, 0, (int)extentX, (int)extentY);
+ }
+
+ ///
+ /// Finds the bounding rectangle based on the first instance of any color component other
+ /// than the given one.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The to search within.
+ /// The color component value to remove.
+ /// The channel to test against.
+ ///
+ /// The .
+ ///
+ public static Rectangle GetFilteredBoundingRectangle(ImageBase bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ const float Epsilon = .00001f;
+ int width = bitmap.Width;
+ int height = bitmap.Height;
+ Point topLeft = default(Point);
+ Point bottomRight = default(Point);
+
+ Func, 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].ToVector4().X - b) > Epsilon;
+ break;
+
+ case RgbaComponent.G:
+ delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Y - b) > Epsilon;
+ break;
+
+ case RgbaComponent.B:
+ delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Z - b) > Epsilon;
+ break;
+
+ default:
+ delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().W - b) > Epsilon;
+ break;
+ }
+
+ Func, int> getMinY = pixels =>
+ {
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ if (delegateFunc(pixels, x, y, componentValue))
+ {
+ return y;
+ }
+ }
+ }
+
+ return 0;
+ };
+
+ Func, int> getMaxY = pixels =>
+ {
+ for (int y = height - 1; y > -1; y--)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ if (delegateFunc(pixels, x, y, componentValue))
+ {
+ return y;
+ }
+ }
+ }
+
+ return height;
+ };
+
+ Func, int> getMinX = pixels =>
+ {
+ for (int x = 0; x < width; x++)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ if (delegateFunc(pixels, x, y, componentValue))
+ {
+ return x;
+ }
+ }
+ }
+
+ return 0;
+ };
+
+ Func, int> getMaxX = pixels =>
+ {
+ for (int x = width - 1; x > -1; x--)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ if (delegateFunc(pixels, x, y, componentValue))
+ {
+ return x;
+ }
+ }
+ }
+
+ return height;
+ };
+
+ using (PixelAccessor bitmapPixels = bitmap.Lock())
+ {
+ topLeft.Y = getMinY(bitmapPixels);
+ topLeft.X = getMinX(bitmapPixels);
+ bottomRight.Y = (getMaxY(bitmapPixels) + 1).Clamp(0, height);
+ bottomRight.X = (getMaxX(bitmapPixels) + 1).Clamp(0, width);
+ }
+
+ return GetBoundingRectangle(topLeft, bottomRight);
+ }
+
+ ///
+ /// Ensures that any passed double is correctly rounded to zero
+ ///
+ /// The value to clean.
+ ///
+ /// The
+ /// .
+ private static float Clean(float x)
+ {
+ const float Epsilon = .00001F;
+
+ if (Math.Abs(x) < Epsilon)
+ {
+ return 0F;
+ }
+
+ return x;
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Alpha.cs b/src/ImageSharp46/Filters/Alpha.cs
new file mode 100644
index 0000000000..a985165adf
--- /dev/null
+++ b/src/ImageSharp46/Filters/Alpha.cs
@@ -0,0 +1,48 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Alters the alpha component of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The new opacity of the image. Must be between 0 and 100.
+ /// The .
+ public static Image Alpha(this Image source, int percent)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Alpha(source, percent, source.Bounds);
+ }
+
+ ///
+ /// Alters the alpha component of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The new opacity of the image. Must be between 0 and 100.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Alpha(this Image source, int percent, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new AlphaProcessor(percent));
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/BackgroundColor.cs b/src/ImageSharp46/Filters/BackgroundColor.cs
new file mode 100644
index 0000000000..47cbee4fb4
--- /dev/null
+++ b/src/ImageSharp46/Filters/BackgroundColor.cs
@@ -0,0 +1,30 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Replaces the background color of image with the given one.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The color to set as the background.
+ /// The .
+ public static Image BackgroundColor(this Image source, TColor color)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(source.Bounds, new BackgroundColorProcessor(color));
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/BinaryThreshold.cs b/src/ImageSharp46/Filters/BinaryThreshold.cs
new file mode 100644
index 0000000000..d97441fe24
--- /dev/null
+++ b/src/ImageSharp46/Filters/BinaryThreshold.cs
@@ -0,0 +1,48 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Applies binerization to the image splitting the pixels at the given threshold.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The threshold to apply binerization of the image. Must be between 0 and 1.
+ /// The .
+ public static Image BinaryThreshold(this Image source, float threshold)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return BinaryThreshold(source, threshold, source.Bounds);
+ }
+
+ ///
+ /// Applies binerization to the image splitting the pixels at the given threshold.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The threshold to apply binerization of the image. Must be between 0 and 1.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image BinaryThreshold(this Image source, float threshold, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new BinaryThresholdProcessor(threshold));
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/BlackWhite.cs b/src/ImageSharp46/Filters/BlackWhite.cs
new file mode 100644
index 0000000000..7e6c6601e3
--- /dev/null
+++ b/src/ImageSharp46/Filters/BlackWhite.cs
@@ -0,0 +1,46 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Applies black and white toning to the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The .
+ public static Image BlackWhite(this Image source)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return BlackWhite(source, source.Bounds);
+ }
+
+ ///
+ /// Applies black and white toning to the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image BlackWhite(this Image source, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new BlackWhiteProcessor());
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Blend.cs b/src/ImageSharp46/Filters/Blend.cs
new file mode 100644
index 0000000000..448fb7aa0d
--- /dev/null
+++ b/src/ImageSharp46/Filters/Blend.cs
@@ -0,0 +1,50 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Combines the given image together with the current one by blending their pixels.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The image to blend with the currently processing image.
+ /// The opacity of the image image to blend. Must be between 0 and 100.
+ /// The .
+ public static Image Blend(this Image source, ImageBase image, int percent = 50)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Blend(source, image, percent, source.Bounds);
+ }
+
+ ///
+ /// Combines the given image together with the current one by blending their pixels.
+ ///
+ /// The image this method extends.
+ /// The image to blend with the currently processing image.
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The opacity of the image image to blend. Must be between 0 and 100.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Blend(this Image source, ImageBase image, int percent, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new BlendProcessor(image, percent));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp46/Filters/Brightness.cs b/src/ImageSharp46/Filters/Brightness.cs
new file mode 100644
index 0000000000..2e167c54f3
--- /dev/null
+++ b/src/ImageSharp46/Filters/Brightness.cs
@@ -0,0 +1,48 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Alters the brightness component of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The new brightness of the image. Must be between -100 and 100.
+ /// The .
+ public static Image Brightness(this Image source, int amount)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Brightness(source, amount, source.Bounds);
+ }
+
+ ///
+ /// Alters the brightness component of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The new brightness of the image. Must be between -100 and 100.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Brightness(this Image source, int amount, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new BrightnessProcessor(amount));
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/ColorBlindness.cs b/src/ImageSharp46/Filters/ColorBlindness.cs
new file mode 100644
index 0000000000..e1a14e0f0d
--- /dev/null
+++ b/src/ImageSharp46/Filters/ColorBlindness.cs
@@ -0,0 +1,85 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Applies the given colorblindness simulator to the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The type of color blindness simulator to apply.
+ /// The .
+ public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return ColorBlindness(source, colorBlindness, source.Bounds);
+ }
+
+ ///
+ /// Applies the given colorblindness simulator to the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The type of color blindness simulator to apply.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ IImageFilter processor;
+
+ switch (colorBlindness)
+ {
+ case ImageSharp.ColorBlindness.Achromatomaly:
+ processor = new AchromatomalyProcessor();
+ break;
+
+ case ImageSharp.ColorBlindness.Achromatopsia:
+ processor = new AchromatopsiaProcessor();
+ break;
+
+ case ImageSharp.ColorBlindness.Deuteranomaly:
+ processor = new DeuteranomalyProcessor();
+ break;
+
+ case ImageSharp.ColorBlindness.Deuteranopia:
+ processor = new DeuteranopiaProcessor();
+ break;
+
+ case ImageSharp.ColorBlindness.Protanomaly:
+ processor = new ProtanomalyProcessor();
+ break;
+
+ case ImageSharp.ColorBlindness.Protanopia:
+ processor = new ProtanopiaProcessor();
+ break;
+
+ case ImageSharp.ColorBlindness.Tritanomaly:
+ processor = new TritanomalyProcessor();
+ break;
+
+ default:
+ processor = new TritanopiaProcessor();
+ break;
+ }
+
+ return source.Process(rectangle, processor);
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Contrast.cs b/src/ImageSharp46/Filters/Contrast.cs
new file mode 100644
index 0000000000..6f9c45cc60
--- /dev/null
+++ b/src/ImageSharp46/Filters/Contrast.cs
@@ -0,0 +1,48 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Alters the contrast component of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The new contrast of the image. Must be between -100 and 100.
+ /// The .
+ public static Image Contrast(this Image source, int amount)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Contrast(source, amount, source.Bounds);
+ }
+
+ ///
+ /// Alters the contrast component of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The new contrast of the image. Must be between -100 and 100.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Contrast(this Image source, int amount, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new ContrastProcessor(amount));
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Glow.cs b/src/ImageSharp46/Filters/Glow.cs
new file mode 100644
index 0000000000..1b829850dc
--- /dev/null
+++ b/src/ImageSharp46/Filters/Glow.cs
@@ -0,0 +1,102 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Applies a radial glow effect to an image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The .
+ public static Image Glow(this Image source)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Glow(source, default(TColor), source.Bounds.Width * .5F, source.Bounds);
+ }
+
+ ///
+ /// Applies a radial glow effect to an image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The color to set as the glow.
+ /// The .
+ public static Image Glow(this Image source, TColor color)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Glow(source, color, source.Bounds.Width * .5F, source.Bounds);
+ }
+
+ ///
+ /// Applies a radial glow effect to an image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The the radius.
+ /// The .
+ public static Image Glow(this Image source, float radius)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Glow(source, default(TColor), radius, source.Bounds);
+ }
+
+ ///
+ /// Applies a radial glow effect to an image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Glow(this Image source, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Glow(source, default(TColor), 0, rectangle);
+ }
+
+ ///
+ /// Applies a radial glow effect to an image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The color to set as the glow.
+ /// The the radius.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Glow(this Image source, TColor color, float radius, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ GlowProcessor processor = new GlowProcessor { Radius = radius, };
+
+ if (!color.Equals(default(TColor)))
+ {
+ processor.GlowColor = color;
+ }
+
+ return source.Process(rectangle, processor);
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Grayscale.cs b/src/ImageSharp46/Filters/Grayscale.cs
new file mode 100644
index 0000000000..ce827e54c7
--- /dev/null
+++ b/src/ImageSharp46/Filters/Grayscale.cs
@@ -0,0 +1,52 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Applies Grayscale toning to the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The formula to apply to perform the operation.
+ /// The .
+ public static Image Grayscale(this Image source, GrayscaleMode mode = GrayscaleMode.Bt709)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Grayscale(source, source.Bounds, mode);
+ }
+
+ ///
+ /// Applies Grayscale toning to the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The formula to apply to perform the operation.
+ /// The .
+ public static Image Grayscale(this Image source, Rectangle rectangle, GrayscaleMode mode = GrayscaleMode.Bt709)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ IImageFilter processor = mode == GrayscaleMode.Bt709
+ ? (IImageFilter)new GrayscaleBt709Processor()
+ : new GrayscaleBt601Processor();
+
+ return source.Process(rectangle, processor);
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Hue.cs b/src/ImageSharp46/Filters/Hue.cs
new file mode 100644
index 0000000000..c146667a0a
--- /dev/null
+++ b/src/ImageSharp46/Filters/Hue.cs
@@ -0,0 +1,48 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Alters the hue component of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The angle in degrees to adjust the image.
+ /// The .
+ public static Image Hue(this Image source, float degrees)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Hue(source, degrees, source.Bounds);
+ }
+
+ ///
+ /// Alters the hue component of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The angle in degrees to adjust the image.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Hue(this Image source, float degrees, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new HueProcessor(degrees));
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Invert.cs b/src/ImageSharp46/Filters/Invert.cs
new file mode 100644
index 0000000000..a948c511d6
--- /dev/null
+++ b/src/ImageSharp46/Filters/Invert.cs
@@ -0,0 +1,46 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Inverts the colors of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The .
+ public static Image Invert(this Image source)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Invert(source, source.Bounds);
+ }
+
+ ///
+ /// Inverts the colors of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Invert(this Image source, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new InvertProcessor());
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Kodachrome.cs b/src/ImageSharp46/Filters/Kodachrome.cs
new file mode 100644
index 0000000000..f47bfa5c37
--- /dev/null
+++ b/src/ImageSharp46/Filters/Kodachrome.cs
@@ -0,0 +1,46 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Alters the colors of the image recreating an old Kodachrome camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The .
+ public static Image Kodachrome(this Image source)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Kodachrome(source, source.Bounds);
+ }
+
+ ///
+ /// Alters the colors of the image recreating an old Kodachrome camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Kodachrome(this Image source, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new KodachromeProcessor());
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Lomograph.cs b/src/ImageSharp46/Filters/Lomograph.cs
new file mode 100644
index 0000000000..84fccd374b
--- /dev/null
+++ b/src/ImageSharp46/Filters/Lomograph.cs
@@ -0,0 +1,46 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Alters the colors of the image recreating an old Lomograph camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The .
+ public static Image Lomograph(this Image source)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Lomograph(source, source.Bounds);
+ }
+
+ ///
+ /// Alters the colors of the image recreating an old Lomograph camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Lomograph(this Image source, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new LomographProcessor());
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Options/ColorBlindness.cs b/src/ImageSharp46/Filters/Options/ColorBlindness.cs
new file mode 100644
index 0000000000..1e0bc596b2
--- /dev/null
+++ b/src/ImageSharp46/Filters/Options/ColorBlindness.cs
@@ -0,0 +1,53 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// Enumerates the various types of defined color blindness filters.
+ ///
+ public enum ColorBlindness
+ {
+ ///
+ /// Partial color desensitivity.
+ ///
+ Achromatomaly,
+
+ ///
+ /// Complete color desensitivity (Monochrome)
+ ///
+ Achromatopsia,
+
+ ///
+ /// Green weak
+ ///
+ Deuteranomaly,
+
+ ///
+ /// Green blind
+ ///
+ Deuteranopia,
+
+ ///
+ /// Red weak
+ ///
+ Protanomaly,
+
+ ///
+ /// Red blind
+ ///
+ Protanopia,
+
+ ///
+ /// Blue weak
+ ///
+ Tritanomaly,
+
+ ///
+ /// Blue blind
+ ///
+ Tritanopia
+ }
+}
diff --git a/src/ImageSharp46/Filters/Options/EdgeDetection.cs b/src/ImageSharp46/Filters/Options/EdgeDetection.cs
new file mode 100644
index 0000000000..67fad0de41
--- /dev/null
+++ b/src/ImageSharp46/Filters/Options/EdgeDetection.cs
@@ -0,0 +1,63 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// Enumerates the various types of defined edge detection filters.
+ ///
+ public enum EdgeDetection
+ {
+ ///
+ /// The Kayyali operator filter.
+ ///
+ Kayyali,
+
+ ///
+ /// The Kirsch operator filter.
+ ///
+ Kirsch,
+
+ ///
+ /// The Lapacian3X3 operator filter.
+ ///
+ Lapacian3X3,
+
+ ///
+ /// The Lapacian5X5 operator filter.
+ ///
+ Lapacian5X5,
+
+ ///
+ /// The LaplacianOfGaussian operator filter.
+ ///
+ LaplacianOfGaussian,
+
+ ///
+ /// The Prewitt operator filter.
+ ///
+ Prewitt,
+
+ ///
+ /// The RobertsCross operator filter.
+ ///
+ RobertsCross,
+
+ ///
+ /// The Robinson operator filter.
+ ///
+ Robinson,
+
+ ///
+ /// The Scharr operator filter.
+ ///
+ Scharr,
+
+ ///
+ /// The Sobel operator filter.
+ ///
+ Sobel
+ }
+}
diff --git a/src/ImageSharp46/Filters/Options/GrayscaleMode.cs b/src/ImageSharp46/Filters/Options/GrayscaleMode.cs
new file mode 100644
index 0000000000..6ecd5bb365
--- /dev/null
+++ b/src/ImageSharp46/Filters/Options/GrayscaleMode.cs
@@ -0,0 +1,23 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// Enumerates the various types of defined Grayscale filters.
+ ///
+ public enum GrayscaleMode
+ {
+ ///
+ /// ITU-R Recommendation BT.709
+ ///
+ Bt709,
+
+ ///
+ /// ITU-R Recommendation BT.601
+ ///
+ Bt601
+ }
+}
diff --git a/src/ImageSharp46/Filters/Polaroid.cs b/src/ImageSharp46/Filters/Polaroid.cs
new file mode 100644
index 0000000000..f77c95a4ba
--- /dev/null
+++ b/src/ImageSharp46/Filters/Polaroid.cs
@@ -0,0 +1,46 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Alters the colors of the image recreating an old Polaroid camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ /// The .
+ public static Image Polaroid(this Image source)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return Polaroid(source, source.Bounds);
+ }
+
+ ///
+ /// Alters the colors of the image recreating an old Polaroid camera effect.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static Image Polaroid(this Image source, Rectangle rectangle)
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ return source.Process(rectangle, new PolaroidProcessor());
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Processors/AlphaProcessor.cs b/src/ImageSharp46/Filters/Processors/AlphaProcessor.cs
new file mode 100644
index 0000000000..08c05acdd2
--- /dev/null
+++ b/src/ImageSharp46/Filters/Processors/AlphaProcessor.cs
@@ -0,0 +1,85 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Processors
+{
+ using System;
+ using System.Numerics;
+ using System.Threading.Tasks;
+
+ ///
+ /// An to change the alpha component of an .
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ public class AlphaProcessor : ImageFilter
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The percentage to adjust the opacity of the image. Must be between 0 and 100.
+ ///
+ /// is less than 0 or is greater than 100.
+ ///
+ public AlphaProcessor(int percent)
+ {
+ Guard.MustBeBetweenOrEqualTo(percent, 0, 100, nameof(percent));
+ this.Value = percent;
+ }
+
+ ///
+ /// Gets the alpha value.
+ ///
+ public int Value { get; }
+
+ ///
+ protected override void Apply(ImageBase source, Rectangle sourceRectangle, int startY, int endY)
+ {
+ float alpha = this.Value / 100F;
+ int startX = sourceRectangle.X;
+ int endX = sourceRectangle.Right;
+
+ // Align start/end positions.
+ int minX = Math.Max(0, startX);
+ int maxX = Math.Min(source.Width, endX);
+ int minY = Math.Max(0, startY);
+ int maxY = Math.Min(source.Height, endY);
+
+ // Reset offset if necessary.
+ if (minX > 0)
+ {
+ startX = 0;
+ }
+
+ if (minY > 0)
+ {
+ startY = 0;
+ }
+
+ Vector4 alphaVector = new Vector4(1, 1, 1, alpha);
+
+ using (PixelAccessor sourcePixels = source.Lock())
+ {
+ Parallel.For(
+ minY,
+ maxY,
+ this.ParallelOptions,
+ y =>
+ {
+ int offsetY = y - startY;
+ for (int x = minX; x < maxX; x++)
+ {
+ int offsetX = x - startX;
+ TColor packed = default(TColor);
+ packed.PackFromVector4(sourcePixels[offsetX, offsetY].ToVector4() * alphaVector);
+ sourcePixels[offsetX, offsetY] = packed;
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp46/Filters/Processors/BackgroundColorProcessor.cs b/src/ImageSharp46/Filters/Processors/BackgroundColorProcessor.cs
new file mode 100644
index 0000000000..37dd580fa1
--- /dev/null
+++ b/src/ImageSharp46/Filters/Processors/BackgroundColorProcessor.cs
@@ -0,0 +1,98 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Processors
+{
+ using System;
+ using System.Numerics;
+ using System.Threading.Tasks;
+
+ ///
+ /// Sets the background color of the image.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ public class BackgroundColorProcessor : ImageFilter
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ private const float Epsilon = 0.001f;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The to set the background color to.
+ public BackgroundColorProcessor(TColor color)
+ {
+ this.Value = color;
+ }
+
+ ///
+ /// Gets the background color value.
+ ///
+ public TColor Value { get; }
+
+ ///
+ protected override void Apply(ImageBase source, Rectangle sourceRectangle, int startY, int endY)
+ {
+ int startX = sourceRectangle.X;
+ int endX = sourceRectangle.Right;
+
+ // Align start/end positions.
+ int minX = Math.Max(0, startX);
+ int maxX = Math.Min(source.Width, endX);
+ int minY = Math.Max(0, startY);
+ int maxY = Math.Min(source.Height, endY);
+
+ // Reset offset if necessary.
+ if (minX > 0)
+ {
+ startX = 0;
+ }
+
+ if (minY > 0)
+ {
+ startY = 0;
+ }
+
+ Vector4 backgroundColor = this.Value.ToVector4();
+
+ using (PixelAccessor sourcePixels = source.Lock())
+ {
+ Parallel.For(
+ minY,
+ maxY,
+ this.ParallelOptions,
+ y =>
+ {
+ int offsetY = y - startY;
+ for (int x = minX; x < maxX; x++)
+ {
+ int offsetX = x - startX;
+ Vector4 color = sourcePixels[offsetX, offsetY].ToVector4();
+ float a = color.W;
+
+ if (a < 1 && a > 0)
+ {
+ color = Vector4.Lerp(color, backgroundColor, .5F);
+ }
+
+ if (Math.Abs(a) < Epsilon)
+ {
+ color = backgroundColor;
+ }
+
+ TColor packed = default(TColor);
+ packed.PackFromVector4(color);
+ sourcePixels[offsetX, offsetY] = packed;
+ }
+ });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp46/Filters/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp46/Filters/Processors/Binarization/BinaryThresholdProcessor.cs
new file mode 100644
index 0000000000..09c6a96f3d
--- /dev/null
+++ b/src/ImageSharp46/Filters/Processors/Binarization/BinaryThresholdProcessor.cs
@@ -0,0 +1,111 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Processors
+{
+ using System;
+ using System.Threading.Tasks;
+
+ ///
+ /// An to perform binary threshold filtering against an
+ /// . The image will be converted to grayscale before thresholding occurs.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ public class BinaryThresholdProcessor : ImageFilter
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The threshold to split the image. Must be between 0 and 1.
+ ///
+ /// is less than 0 or is greater than 1.
+ ///
+ public BinaryThresholdProcessor(float threshold)
+ {
+ // TODO: Check limit.
+ Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold));
+ this.Value = threshold;
+
+ TColor upper = default(TColor);
+ upper.PackFromVector4(Color.White.ToVector4());
+ this.UpperColor = upper;
+
+ TColor lower = default(TColor);
+ lower.PackFromVector4(Color.Black.ToVector4());
+ this.LowerColor = lower;
+ }
+
+ ///
+ /// Gets the threshold value.
+ ///
+ public float Value { get; }
+
+ ///
+ /// Gets or sets the color to use for pixels that are above the threshold.
+ ///
+ public TColor UpperColor { get; set; }
+
+ ///
+ /// Gets or sets the color to use for pixels that fall below the threshold.
+ ///
+ public TColor LowerColor { get; set; }
+
+ ///
+ protected override void OnApply(ImageBase source, Rectangle sourceRectangle)
+ {
+ new GrayscaleBt709Processor().Apply(source, sourceRectangle);
+ }
+
+ ///
+ protected override void Apply(ImageBase source, Rectangle sourceRectangle, int startY, int endY)
+ {
+ float threshold = this.Value;
+ TColor upper = this.UpperColor;
+ TColor lower = this.LowerColor;
+ int startX = sourceRectangle.X;
+ int endX = sourceRectangle.Right;
+
+ // Align start/end positions.
+ int minX = Math.Max(0, startX);
+ int maxX = Math.Min(source.Width, endX);
+ int minY = Math.Max(0, startY);
+ int maxY = Math.Min(source.Height, endY);
+
+ // Reset offset if necessary.
+ if (minX > 0)
+ {
+ startX = 0;
+ }
+
+ if (minY > 0)
+ {
+ startY = 0;
+ }
+
+ using (PixelAccessor sourcePixels = source.Lock())
+ {
+ Parallel.For(
+ minY,
+ maxY,
+ this.ParallelOptions,
+ y =>
+ {
+ int offsetY = y - startY;
+ for (int x = minX; x < maxX; x++)
+ {
+ int offsetX = x - startX;
+ TColor color = sourcePixels[offsetX, offsetY];
+
+ // Any channel will do since it's Grayscale.
+ sourcePixels[offsetX, offsetY] = color.ToVector4().X >= threshold ? upper : lower;
+ }
+ });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp46/Filters/Processors/BlendProcessor.cs b/src/ImageSharp46/Filters/Processors/BlendProcessor.cs
new file mode 100644
index 0000000000..4f326361fe
--- /dev/null
+++ b/src/ImageSharp46/Filters/Processors/BlendProcessor.cs
@@ -0,0 +1,106 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Processors
+{
+ using System;
+ using System.Numerics;
+ using System.Threading.Tasks;
+
+ ///
+ /// Combines two images together by blending the pixels.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ public class BlendProcessor : ImageFilter
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ ///
+ /// The image to blend.
+ ///
+ private readonly ImageBase blend;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The image to blend with the currently processing image.
+ /// Disposal of this image is the responsibility of the developer.
+ ///
+ /// The opacity of the image to blend. Between 0 and 100.
+ public BlendProcessor(ImageBase image, int alpha = 100)
+ {
+ Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha));
+ this.blend = image;
+ this.Value = alpha;
+ }
+
+ ///
+ /// Gets the alpha percentage value.
+ ///
+ public int Value { get; }
+
+ ///
+ protected override void Apply(ImageBase source, Rectangle sourceRectangle, int startY, int endY)
+ {
+ int startX = sourceRectangle.X;
+ int endX = sourceRectangle.Right;
+ Rectangle bounds = this.blend.Bounds;
+
+ // Align start/end positions.
+ int minX = Math.Max(0, startX);
+ int maxX = Math.Min(source.Width, endX);
+ int minY = Math.Max(0, startY);
+ int maxY = Math.Min(source.Height, endY);
+
+ // Reset offset if necessary.
+ if (minX > 0)
+ {
+ startX = 0;
+ }
+
+ if (minY > 0)
+ {
+ startY = 0;
+ }
+
+ float alpha = this.Value / 100F;
+
+ using (PixelAccessor toBlendPixels = this.blend.Lock())
+ using (PixelAccessor sourcePixels = source.Lock())
+ {
+ Parallel.For(
+ minY,
+ maxY,
+ this.ParallelOptions,
+ y =>
+ {
+ int offsetY = y - startY;
+ for (int x = minX; x < maxX; x++)
+ {
+ int offsetX = x - startX;
+ Vector4 color = sourcePixels[offsetX, offsetY].ToVector4();
+
+ if (bounds.Contains(offsetX, offsetY))
+ {
+ Vector4 blendedColor = toBlendPixels[offsetX, offsetY].ToVector4();
+
+ if (blendedColor.W > 0)
+ {
+ // Lerping colors is dependent on the alpha of the blended color
+ color = Vector4.Lerp(color, blendedColor, alpha > 0 ? alpha : blendedColor.W);
+ }
+ }
+
+ TColor packed = default(TColor);
+ packed.PackFromVector4(color);
+ sourcePixels[offsetX, offsetY] = packed;
+ }
+ });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp46/Filters/Processors/BrightnessProcessor.cs b/src/ImageSharp46/Filters/Processors/BrightnessProcessor.cs
new file mode 100644
index 0000000000..b74759b6b2
--- /dev/null
+++ b/src/ImageSharp46/Filters/Processors/BrightnessProcessor.cs
@@ -0,0 +1,90 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Processors
+{
+ using System;
+ using System.Numerics;
+ using System.Threading.Tasks;
+
+ ///
+ /// An to change the brightness of an .
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ public class BrightnessProcessor : ImageFilter
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The new brightness of the image. Must be between -100 and 100.
+ ///
+ /// is less than -100 or is greater than 100.
+ ///
+ public BrightnessProcessor(int brightness)
+ {
+ Guard.MustBeBetweenOrEqualTo(brightness, -100, 100, nameof(brightness));
+ this.Value = brightness;
+ }
+
+ ///
+ /// Gets the brightness value.
+ ///
+ public int Value { get; }
+
+ ///
+ protected override void Apply(ImageBase source, Rectangle sourceRectangle, int startY, int endY)
+ {
+ float brightness = this.Value / 100F;
+ int startX = sourceRectangle.X;
+ int endX = sourceRectangle.Right;
+
+ // Align start/end positions.
+ int minX = Math.Max(0, startX);
+ int maxX = Math.Min(source.Width, endX);
+ int minY = Math.Max(0, startY);
+ int maxY = Math.Min(source.Height, endY);
+
+ // Reset offset if necessary.
+ if (minX > 0)
+ {
+ startX = 0;
+ }
+
+ if (minY > 0)
+ {
+ startY = 0;
+ }
+
+ using (PixelAccessor sourcePixels = source.Lock())
+ {
+ Parallel.For(
+ minY,
+ maxY,
+ this.ParallelOptions,
+ y =>
+ {
+ int offsetY = y - startY;
+ for (int x = minX; x < maxX; x++)
+ {
+ int offsetX = x - startX;
+
+ // TODO: Check this with other formats.
+ Vector4 vector = sourcePixels[offsetX, offsetY].ToVector4().Expand();
+ Vector3 transformed = new Vector3(vector.X, vector.Y, vector.Z) + new Vector3(brightness);
+ vector = new Vector4(transformed, vector.W);
+
+ TColor packed = default(TColor);
+ packed.PackFromVector4(vector.Compress());
+
+ sourcePixels[offsetX, offsetY] = packed;
+ }
+ });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp46/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs b/src/ImageSharp46/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs
new file mode 100644
index 0000000000..cbb351aafd
--- /dev/null
+++ b/src/ImageSharp46/Filters/Processors/ColorMatrix/BlackWhiteProcessor.cs
@@ -0,0 +1,36 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image to their black and white equivalent.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ public class BlackWhiteProcessor : ColorMatrixFilter
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 1.5f,
+ M12 = 1.5f,
+ M13 = 1.5f,
+ M21 = 1.5f,
+ M22 = 1.5f,
+ M23 = 1.5f,
+ M31 = 1.5f,
+ M32 = 1.5f,
+ M33 = 1.5f,
+ M41 = -1f,
+ M42 = -1f,
+ M43 = -1f,
+ };
+ }
+}
diff --git a/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs b/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs
new file mode 100644
index 0000000000..012954c2ff
--- /dev/null
+++ b/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs
@@ -0,0 +1,36 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ public class AchromatomalyProcessor : ColorMatrixFilter
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = .618f,
+ M12 = .163f,
+ M13 = .163f,
+ M21 = .320f,
+ M22 = .775f,
+ M23 = .320f,
+ M31 = .062f,
+ M32 = .062f,
+ M33 = .516f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs b/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs
new file mode 100644
index 0000000000..6ce50ba433
--- /dev/null
+++ b/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs
@@ -0,0 +1,36 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ public class AchromatopsiaProcessor : ColorMatrixFilter
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = .299f,
+ M12 = .299f,
+ M13 = .299f,
+ M21 = .587f,
+ M22 = .587f,
+ M23 = .587f,
+ M31 = .114f,
+ M32 = .114f,
+ M33 = .114f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs b/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs
new file mode 100644
index 0000000000..e1fbac60a5
--- /dev/null
+++ b/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ public class DeuteranomalyProcessor : ColorMatrixFilter
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ ///
+ public override Matrix4x4 Matrix => new Matrix4x4()
+ {
+ M11 = 0.8f,
+ M12 = 0.258f,
+ M21 = 0.2f,
+ M22 = 0.742f,
+ M23 = 0.142f,
+ M33 = 0.858f
+ };
+
+ ///
+ public override bool Compand => false;
+ }
+}
diff --git a/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs b/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs
new file mode 100644
index 0000000000..35e76b235d
--- /dev/null
+++ b/src/ImageSharp46/Filters/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs
@@ -0,0 +1,33 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness.
+ ///
+ /// The pixel format.
+ /// The packed format. uint, long, float.
+ public class DeuteranopiaProcessor : ColorMatrixFilter
+ where TColor : struct, IPackedPixel
+ where TPacked : struct
+ {
+ ///