mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 7d1bd38103443c71e8bfd1b0f0188e280302b410 Former-commit-id: 720bc4cad102119f67274389f0f15be803b24ce8 Former-commit-id: b1cf87199086fc50dcdd98b915de13e8c871c688af/merge-core
28 changed files with 2578 additions and 353 deletions
@ -0,0 +1,179 @@ |
|||
// <copyright file="ColorConstants.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
/// <summary>
|
|||
/// Provides useful color definitions.
|
|||
/// </summary>
|
|||
public static class ColorConstants |
|||
{ |
|||
/// <summary>
|
|||
/// Provides a lazy, one time method of returning the colors.
|
|||
/// </summary>
|
|||
private static readonly Lazy<Color[]> SafeColors = new Lazy<Color[]>(GetWebSafeColors); |
|||
|
|||
/// <summary>
|
|||
/// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4.
|
|||
/// </summary>
|
|||
public static Color[] WebSafeColors => SafeColors.Value; |
|||
|
|||
/// <summary>
|
|||
/// Returns an array of web safe colors.
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
private static Color[] GetWebSafeColors() |
|||
{ |
|||
return new List<Color> |
|||
{ |
|||
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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
// <copyright file="ColorTransforms.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
|
|||
/// The color components are stored in red, green, blue, and alpha order.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
|
|||
/// as it avoids the need to create new values for modification operations.
|
|||
/// </remarks>
|
|||
public partial struct Color |
|||
{ |
|||
/// <summary>
|
|||
/// Blends two colors by multiplication.
|
|||
/// <remarks>
|
|||
/// 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.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
/// <param name="source">The source color.</param>
|
|||
/// <param name="destination">The destination color.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Color"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Linearly interpolates from one color to another based on the given weighting.
|
|||
/// </summary>
|
|||
/// <param name="from">The first color value.</param>
|
|||
/// <param name="to">The second color value.</param>
|
|||
/// <param name="amount">
|
|||
/// A value between 0 and 1 indicating the weight of the second source vector.
|
|||
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="Color"/>
|
|||
/// </returns>
|
|||
public static Color Lerp(Color from, Color to, float amount) |
|||
{ |
|||
return new Color(Vector4.Lerp(from.ToVector4(), to.ToVector4(), amount)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,292 @@ |
|||
// <copyright file="ColorspaceTransforms.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
|
|||
/// The color components are stored in red, green, blue, and alpha order.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
|
|||
/// as it avoids the need to create new values for modification operations.
|
|||
/// </remarks>
|
|||
public partial struct Color |
|||
{ |
|||
/// <summary>
|
|||
/// The epsilon for comparing floating point numbers.
|
|||
/// </summary>
|
|||
private const float Epsilon = 0.001F; |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
|
|||
/// <see cref="Bgra32"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">The instance of <see cref="Color"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Bgra32"/>.
|
|||
/// </returns>
|
|||
public static implicit operator Color(Bgra32 color) |
|||
{ |
|||
return new Color(color.R, color.G, color.B, color.A); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Cmyk"/> to a
|
|||
/// <see cref="Color"/>.
|
|||
/// </summary>
|
|||
/// <param name="cmykColor">The instance of <see cref="Cmyk"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Color"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="YCbCr"/> to a
|
|||
/// <see cref="Color"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">The instance of <see cref="YCbCr"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Color"/>.
|
|||
/// </returns>
|
|||
public static implicit operator Color(YCbCr color) |
|||
{ |
|||
float y = color.Y; |
|||
float cb = color.Cb - 128; |
|||
float cr = color.Cr - 128; |
|||
|
|||
byte r = (byte)(y + (1.402 * cr)).Clamp(0, 255); |
|||
byte g = (byte)(y - (0.34414 * cb) - (0.71414 * cr)).Clamp(0, 255); |
|||
byte b = (byte)(y + (1.772 * cb)).Clamp(0, 255); |
|||
|
|||
return new Color(r, g, b, 255); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="CieXyz"/> to a
|
|||
/// <see cref="Color"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">The instance of <see cref="CieXyz"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Color"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Hsv"/> to a
|
|||
/// <see cref="Color"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">The instance of <see cref="Hsv"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Color"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Hsl"/> to a
|
|||
/// <see cref="Color"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">The instance of <see cref="Hsl"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Color"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="CieLab"/> to a
|
|||
/// <see cref="Color"/>.
|
|||
/// </summary>
|
|||
/// <param name="cieLabColor">The instance of <see cref="CieLab"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Color"/>.
|
|||
/// </returns>
|
|||
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()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the color component from the given values.
|
|||
/// </summary>
|
|||
/// <param name="first">The first value.</param>
|
|||
/// <param name="second">The second value.</param>
|
|||
/// <param name="third">The third value.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/>.
|
|||
/// </returns>
|
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Moves the specific value within the acceptable range for
|
|||
/// conversion.
|
|||
/// <remarks>Used for converting <see cref="Hsl"/> colors to this type.</remarks>
|
|||
/// </summary>
|
|||
/// <param name="value">The value to shift.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/>.
|
|||
/// </returns>
|
|||
private static float MoveIntoRange(float value) |
|||
{ |
|||
if (value < 0.0) |
|||
{ |
|||
value += 1.0f; |
|||
} |
|||
else if (value > 1.0) |
|||
{ |
|||
value -= 1.0f; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,167 @@ |
|||
// <copyright file="Bgra32.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Represents an BGRA (blue, green, red, alpha) color.
|
|||
/// </summary>
|
|||
public struct Bgra32 : IEquatable<Bgra32> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a 32 bit <see cref="Bgra32"/> that has B, G, R, and A values set to zero.
|
|||
/// </summary>
|
|||
public static readonly Bgra32 Empty = default(Bgra32); |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private Vector4 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="b">The blue component of this <see cref="Bgra32"/>.</param>
|
|||
/// <param name="g">The green component of this <see cref="Bgra32"/>.</param>
|
|||
/// <param name="r">The red component of this <see cref="Bgra32"/>.</param>
|
|||
/// <param name="a">The alpha component of this <see cref="Bgra32"/>.</param>
|
|||
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)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the blue component of the color
|
|||
/// </summary>
|
|||
public byte B => (byte)this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the green component of the color
|
|||
/// </summary>
|
|||
public byte G => (byte)this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the red component of the color
|
|||
/// </summary>
|
|||
public byte R => (byte)this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets the alpha component of the color
|
|||
/// </summary>
|
|||
public byte A => (byte)this.backingVector.W; |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="Bgra32"/> integer representation of the color.
|
|||
/// </summary>
|
|||
public int Bgra => (this.R << 16) | (this.G << 8) | (this.B << 0) | (this.A << 24); |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="Bgra32"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.Equals(Empty); |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
|
|||
/// <see cref="Bgra32"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Color"/> to convert.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Bgra32"/>.
|
|||
/// </returns>
|
|||
public static implicit operator Bgra32(Color color) |
|||
{ |
|||
return new Bgra32(color.B, color.G, color.R, color.A); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Bgra32"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Bgra32"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Bgra32"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(Bgra32 left, Bgra32 right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Bgra32"/> objects for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Bgra32"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Bgra32"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(Bgra32 left, Bgra32 right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is Bgra32) |
|||
{ |
|||
Bgra32 color = (Bgra32)obj; |
|||
|
|||
return this.backingVector == color.backingVector; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return GetHashCode(this); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "Bgra32 [ Empty ]"; |
|||
} |
|||
|
|||
return $"Bgra32 [ B={this.B}, G={this.G}, R={this.R}, A={this.A} ]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(Bgra32 other) |
|||
{ |
|||
return this.backingVector.Equals(other.backingVector); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this instance.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Bgra32"/> to return the hash code for.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A 32-bit signed integer that is the hash code for this instance.
|
|||
/// </returns>
|
|||
private static int GetHashCode(Bgra32 color) => color.backingVector.GetHashCode(); |
|||
} |
|||
} |
|||
@ -0,0 +1,192 @@ |
|||
// <copyright file="CieLab.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Represents an CIE LAB 1976 color.
|
|||
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
|
|||
/// </summary>
|
|||
public struct CieLab : IEquatable<CieLab>, IAlmostEquatable<CieLab, float> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="CieLab"/> that has L, A, B values set to zero.
|
|||
/// </summary>
|
|||
public static readonly CieLab Empty = default(CieLab); |
|||
|
|||
/// <summary>
|
|||
/// The epsilon for comparing floating point numbers.
|
|||
/// </summary>
|
|||
private const float Epsilon = 0.001f; |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private Vector3 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieLab"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="l">The lightness dimension.</param>
|
|||
/// <param name="a">The a (green - magenta) component.</param>
|
|||
/// <param name="b">The b (blue - yellow) component.</param>
|
|||
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)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the lightness dimension.
|
|||
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
|
|||
/// </summary>
|
|||
public float L => this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the a color component.
|
|||
/// <remarks>Negative is green, positive magenta.</remarks>
|
|||
/// </summary>
|
|||
public float A => this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the b color component.
|
|||
/// <remarks>Negative is blue, positive is yellow</remarks>
|
|||
/// </summary>
|
|||
public float B => this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="CieLab"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.Equals(Empty); |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
|
|||
/// <see cref="CieLab"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Color"/> to convert.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="CieLab"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="CieLab"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="CieLab"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="CieLab"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(CieLab left, CieLab right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="CieLab"/> objects for inequality
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="CieLab"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="CieLab"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(CieLab left, CieLab right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return GetHashCode(this); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "CieLab [Empty]"; |
|||
} |
|||
|
|||
return $"CieLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is CieLab) |
|||
{ |
|||
return this.Equals((CieLab)obj); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(CieLab other) |
|||
{ |
|||
return this.AlmostEquals(other, Epsilon); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this instance.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="CieLab"/> to return the hash code for.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A 32-bit signed integer that is the hash code for this instance.
|
|||
/// </returns>
|
|||
private static int GetHashCode(CieLab color) => color.backingVector.GetHashCode(); |
|||
} |
|||
} |
|||
@ -0,0 +1,184 @@ |
|||
// <copyright file="CieXyz.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Represents an CIE 1931 color
|
|||
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space"/>
|
|||
/// </summary>
|
|||
public struct CieXyz : IEquatable<CieXyz>, IAlmostEquatable<CieXyz, float> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="CieXyz"/> that has Y, Cb, and Cr values set to zero.
|
|||
/// </summary>
|
|||
public static readonly CieXyz Empty = default(CieXyz); |
|||
|
|||
/// <summary>
|
|||
/// The epsilon for comparing floating point numbers.
|
|||
/// </summary>
|
|||
private const float Epsilon = 0.001f; |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private Vector3 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyz"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="y">The y luminance component.</param>
|
|||
/// <param name="x">X is a mix (a linear combination) of cone response curves chosen to be nonnegative</param>
|
|||
/// <param name="z">Z is quasi-equal to blue stimulation, or the S cone of the human eye.</param>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the Y luminance component.
|
|||
/// <remarks>A value ranging between 380 and 780.</remarks>
|
|||
/// </summary>
|
|||
public float X => this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the Cb chroma component.
|
|||
/// <remarks>A value ranging between 380 and 780.</remarks>
|
|||
/// </summary>
|
|||
public float Y => this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the Cr chroma component.
|
|||
/// <remarks>A value ranging between 380 and 780.</remarks>
|
|||
/// </summary>
|
|||
public float Z => this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="CieXyz"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.Equals(Empty); |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
|
|||
/// <see cref="CieXyz"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Color"/> to convert.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="CieXyz"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="CieXyz"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="CieXyz"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="CieXyz"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(CieXyz left, CieXyz right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="CieXyz"/> objects for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="CieXyz"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="CieXyz"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(CieXyz left, CieXyz right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return GetHashCode(this); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "CieXyz [ Empty ]"; |
|||
} |
|||
|
|||
return $"CieXyz [ X={this.X:#0.##}, Y={this.Y:#0.##}, Z={this.Z:#0.##} ]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is CieXyz) |
|||
{ |
|||
return this.Equals((CieXyz)obj); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(CieXyz other) |
|||
{ |
|||
return this.AlmostEquals(other, Epsilon); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this instance.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Hsv"/> to return the hash code for.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A 32-bit signed integer that is the hash code for this instance.
|
|||
/// </returns>
|
|||
private static int GetHashCode(CieXyz color) => color.backingVector.GetHashCode(); |
|||
} |
|||
} |
|||
@ -0,0 +1,195 @@ |
|||
// <copyright file="Cmyk.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Represents an CMYK (cyan, magenta, yellow, keyline) color.
|
|||
/// </summary>
|
|||
public struct Cmyk : IEquatable<Cmyk>, IAlmostEquatable<Cmyk, float> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="Cmyk"/> that has C, M, Y, and K values set to zero.
|
|||
/// </summary>
|
|||
public static readonly Cmyk Empty = default(Cmyk); |
|||
|
|||
/// <summary>
|
|||
/// The epsilon for comparing floating point numbers.
|
|||
/// </summary>
|
|||
private const float Epsilon = 0.001f; |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private Vector4 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Cmyk"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="c">The cyan component.</param>
|
|||
/// <param name="m">The magenta component.</param>
|
|||
/// <param name="y">The yellow component.</param>
|
|||
/// <param name="k">The keyline black component.</param>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the cyan color component.
|
|||
/// <remarks>A value ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float C => this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the magenta color component.
|
|||
/// <remarks>A value ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float M => this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the yellow color component.
|
|||
/// <remarks>A value ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float Y => this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets the keyline black color component.
|
|||
/// <remarks>A value ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float K => this.backingVector.W; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="Cmyk"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.Equals(Empty); |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
|
|||
/// <see cref="Cmyk"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Bgra32"/> to convert.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Cmyk"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Cmyk"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Cmyk"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Cmyk"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(Cmyk left, Cmyk right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Cmyk"/> objects for inequality
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Cmyk"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Cmyk"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(Cmyk left, Cmyk right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return GetHashCode(this); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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.##}]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is Cmyk) |
|||
{ |
|||
return this.Equals((Cmyk)obj); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(Cmyk other) |
|||
{ |
|||
return this.AlmostEquals(other, Epsilon); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this instance.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Cmyk"/> to return the hash code for.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A 32-bit signed integer that is the hash code for this instance.
|
|||
/// </returns>
|
|||
private static int GetHashCode(Cmyk color) => color.backingVector.GetHashCode(); |
|||
} |
|||
} |
|||
@ -0,0 +1,213 @@ |
|||
// <copyright file="Hsl.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Represents a Hsl (hue, saturation, lightness) color.
|
|||
/// </summary>
|
|||
public struct Hsl : IEquatable<Hsl>, IAlmostEquatable<Hsl, float> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="Hsl"/> that has H, S, and L values set to zero.
|
|||
/// </summary>
|
|||
public static readonly Hsl Empty = default(Hsl); |
|||
|
|||
/// <summary>
|
|||
/// The epsilon for comparing floating point numbers.
|
|||
/// </summary>
|
|||
private const float Epsilon = 0.001F; |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private Vector3 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Hsl"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="h">The h hue component.</param>
|
|||
/// <param name="s">The s saturation component.</param>
|
|||
/// <param name="l">The l value (lightness) component.</param>
|
|||
public Hsl(float h, float s, float l) |
|||
{ |
|||
this.backingVector = Vector3.Clamp(new Vector3(h, s, l), Vector3.Zero, new Vector3(360, 1, 1)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the hue component.
|
|||
/// <remarks>A value ranging between 0 and 360.</remarks>
|
|||
/// </summary>
|
|||
public float H => this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the saturation component.
|
|||
/// <remarks>A value ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float S => this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the lightness component.
|
|||
/// <remarks>A value ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float L => this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="Hsl"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.Equals(Empty); |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
|
|||
/// <see cref="Hsl"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">The instance of <see cref="Color"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Hsl"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Hsl"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Hsl"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Hsl"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(Hsl left, Hsl right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Hsl"/> objects for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Hsl"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Hsl"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(Hsl left, Hsl right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return GetHashCode(this); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "Hsl [ Empty ]"; |
|||
} |
|||
|
|||
return $"Hsl [ H={this.H:#0.##}, S={this.S:#0.##}, L={this.L:#0.##} ]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is Hsl) |
|||
{ |
|||
return this.Equals((Hsl)obj); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(Hsl other) |
|||
{ |
|||
return this.AlmostEquals(other, Epsilon); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this instance.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Hsl"/> to return the hash code for.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A 32-bit signed integer that is the hash code for this instance.
|
|||
/// </returns>
|
|||
private static int GetHashCode(Hsl color) => color.backingVector.GetHashCode(); |
|||
} |
|||
} |
|||
@ -0,0 +1,206 @@ |
|||
// <copyright file="Hsv.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
|
|||
/// </summary>
|
|||
public struct Hsv : IEquatable<Hsv>, IAlmostEquatable<Hsv, float> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="Hsv"/> that has H, S, and V values set to zero.
|
|||
/// </summary>
|
|||
public static readonly Hsv Empty = default(Hsv); |
|||
|
|||
/// <summary>
|
|||
/// The epsilon for comparing floating point numbers.
|
|||
/// </summary>
|
|||
private const float Epsilon = 0.001F; |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private Vector3 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Hsv"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="h">The h hue component.</param>
|
|||
/// <param name="s">The s saturation component.</param>
|
|||
/// <param name="v">The v value (brightness) component.</param>
|
|||
public Hsv(float h, float s, float v) |
|||
{ |
|||
this.backingVector = Vector3.Clamp(new Vector3(h, s, v), Vector3.Zero, new Vector3(360, 1, 1)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the hue component.
|
|||
/// <remarks>A value ranging between 0 and 360.</remarks>
|
|||
/// </summary>
|
|||
public float H => this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the saturation component.
|
|||
/// <remarks>A value ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float S => this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the value (brightness) component.
|
|||
/// <remarks>A value ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float V => this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="Hsv"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.Equals(Empty); |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
|
|||
/// <see cref="Hsv"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">The instance of <see cref="Color"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Hsv"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Hsv"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Hsv"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Hsv"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(Hsv left, Hsv right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Hsv"/> objects for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Hsv"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Hsv"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(Hsv left, Hsv right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return GetHashCode(this); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "Hsv [ Empty ]"; |
|||
} |
|||
|
|||
return $"Hsv [ H={this.H:#0.##}, S={this.S:#0.##}, V={this.V:#0.##} ]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is Hsv) |
|||
{ |
|||
return this.Equals((Hsv)obj); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(Hsv other) |
|||
{ |
|||
return this.AlmostEquals(other, Epsilon); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this instance.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Hsv"/> to return the hash code for.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A 32-bit signed integer that is the hash code for this instance.
|
|||
/// </returns>
|
|||
private static int GetHashCode(Hsv color) => color.backingVector.GetHashCode(); |
|||
} |
|||
} |
|||
@ -1,285 +0,0 @@ |
|||
// <copyright file="ColorspaceTransforms.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore |
|||
{ |
|||
using System; |
|||
|
|||
/// <summary>
|
|||
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
|
|||
/// The color components are stored in red, green, blue, and alpha order.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
|
|||
/// as it avoids the need to create new values for modification operations.
|
|||
/// </remarks>
|
|||
public partial struct Color |
|||
{ |
|||
///// <summary>
|
|||
///// Allows the implicit conversion of an instance of <see cref="Color"/> to a
|
|||
///// <see cref="Bgra32"/>.
|
|||
///// </summary>
|
|||
///// <param name="color">The instance of <see cref="Color"/> to convert.</param>
|
|||
///// <returns>
|
|||
///// An instance of <see cref="Bgra32"/>.
|
|||
///// </returns>
|
|||
//public static implicit operator Color(Bgra32 color)
|
|||
//{
|
|||
// return new Color(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
|
|||
//}
|
|||
|
|||
///// <summary>
|
|||
///// Allows the implicit conversion of an instance of <see cref="Cmyk"/> to a
|
|||
///// <see cref="Color"/>.
|
|||
///// </summary>
|
|||
///// <param name="cmykColor">The instance of <see cref="Cmyk"/> to convert.</param>
|
|||
///// <returns>
|
|||
///// An instance of <see cref="Color"/>.
|
|||
///// </returns>
|
|||
//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);
|
|||
//}
|
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="YCbCr"/> to a
|
|||
/// <see cref="Color"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">The instance of <see cref="YCbCr"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Color"/>.
|
|||
/// </returns>
|
|||
public static implicit operator Color(YCbCr color) |
|||
{ |
|||
float y = color.Y; |
|||
float cb = color.Cb - 128; |
|||
float cr = color.Cr - 128; |
|||
|
|||
byte r = (byte)(y + (1.402 * cr)).Clamp(0, 255); |
|||
byte g = (byte)(y - (0.34414 * cb) - (0.71414 * cr)).Clamp(0, 255); |
|||
byte b = (byte)(y + (1.772 * cb)).Clamp(0, 255); |
|||
|
|||
return new Color(r, g, b, 255); |
|||
} |
|||
|
|||
///// <summary>
|
|||
///// Allows the implicit conversion of an instance of <see cref="CieXyz"/> to a
|
|||
///// <see cref="Color"/>.
|
|||
///// </summary>
|
|||
///// <param name="color">The instance of <see cref="CieXyz"/> to convert.</param>
|
|||
///// <returns>
|
|||
///// An instance of <see cref="Color"/>.
|
|||
///// </returns>
|
|||
//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);
|
|||
|
|||
// return Color.Compress(new Color(r, g, b));
|
|||
//}
|
|||
|
|||
///// <summary>
|
|||
///// Allows the implicit conversion of an instance of <see cref="Hsv"/> to a
|
|||
///// <see cref="Color"/>.
|
|||
///// </summary>
|
|||
///// <param name="color">The instance of <see cref="Hsv"/> to convert.</param>
|
|||
///// <returns>
|
|||
///// An instance of <see cref="Color"/>.
|
|||
///// </returns>
|
|||
//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);
|
|||
//}
|
|||
|
|||
///// <summary>
|
|||
///// Allows the implicit conversion of an instance of <see cref="Hsl"/> to a
|
|||
///// <see cref="Color"/>.
|
|||
///// </summary>
|
|||
///// <param name="color">The instance of <see cref="Hsl"/> to convert.</param>
|
|||
///// <returns>
|
|||
///// An instance of <see cref="Color"/>.
|
|||
///// </returns>
|
|||
//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);
|
|||
//}
|
|||
|
|||
///// <summary>
|
|||
///// Allows the implicit conversion of an instance of <see cref="CieLab"/> to a
|
|||
///// <see cref="Color"/>.
|
|||
///// </summary>
|
|||
///// <param name="cieLabColor">The instance of <see cref="CieLab"/> to convert.</param>
|
|||
///// <returns>
|
|||
///// An instance of <see cref="Color"/>.
|
|||
///// </returns>
|
|||
//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 Color.Compress(new Color(r, g, b));
|
|||
//}
|
|||
|
|||
/// <summary>
|
|||
/// Gets the color component from the given values.
|
|||
/// </summary>
|
|||
/// <param name="first">The first value.</param>
|
|||
/// <param name="second">The second value.</param>
|
|||
/// <param name="third">The third value.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/>.
|
|||
/// </returns>
|
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Moves the specific value within the acceptable range for
|
|||
/// conversion.
|
|||
/// <remarks>Used for converting <see cref="Hsl"/> colors to this type.</remarks>
|
|||
/// </summary>
|
|||
/// <param name="value">The value to shift.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/>.
|
|||
/// </returns>
|
|||
private static float MoveIntoRange(float value) |
|||
{ |
|||
if (value < 0.0) |
|||
{ |
|||
value += 1.0f; |
|||
} |
|||
else if (value > 1.0) |
|||
{ |
|||
value -= 1.0f; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,145 @@ |
|||
// <copyright file="PaletteQuantizer.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Quantizers |
|||
{ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
/// <summary>
|
|||
/// Encapsulates methods to create a quantized image based upon the given palette.
|
|||
/// <see href="http://msdn.microsoft.com/en-us/library/aa479306.aspx"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The pixel format.</typeparam>
|
|||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|||
public class PaletteQuantizer<T, TP> : Quantizer<T, TP> |
|||
where T : IPackedVector<TP> |
|||
where TP : struct |
|||
{ |
|||
/// <summary>
|
|||
/// A lookup table for colors
|
|||
/// </summary>
|
|||
private readonly ConcurrentDictionary<string, byte> colorMap = new ConcurrentDictionary<string, byte>(); |
|||
|
|||
/// <summary>
|
|||
/// List of all colors in the palette
|
|||
/// </summary>
|
|||
private T[] colors; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PaletteQuantizer{T,TP}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="palette">
|
|||
/// The color palette. If none is given this will default to the web safe colors defined
|
|||
/// in the CSS Color Module Level 4.
|
|||
/// </param>
|
|||
public PaletteQuantizer(T[] palette = null) |
|||
: base(true) |
|||
{ |
|||
if (palette == null) |
|||
{ |
|||
Color[] constants = ColorConstants.WebSafeColors; |
|||
List<T> safe = new List<T> { default(T) }; |
|||
foreach (Color c in constants) |
|||
{ |
|||
T packed = default(T); |
|||
packed.PackVector(c.ToVector4()); |
|||
safe.Add(packed); |
|||
} |
|||
|
|||
this.colors = safe.ToArray(); |
|||
} |
|||
else |
|||
{ |
|||
this.colors = palette; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override QuantizedImage<T, TP> Quantize(ImageBase<T, TP> image, int maxColors) |
|||
{ |
|||
Array.Resize(ref this.colors, maxColors.Clamp(1, 256)); |
|||
return base.Quantize(image, maxColors); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override byte QuantizePixel(T pixel) |
|||
{ |
|||
byte colorIndex = 0; |
|||
string colorHash = pixel.ToString(); |
|||
|
|||
// Check if the color is in the lookup table
|
|||
if (this.colorMap.ContainsKey(colorHash)) |
|||
{ |
|||
colorIndex = this.colorMap[colorHash]; |
|||
} |
|||
else |
|||
{ |
|||
// Not found - loop through the palette and find the nearest match.
|
|||
// Firstly check the alpha value - if less than the threshold, lookup the transparent color
|
|||
byte[] bytes = pixel.ToBytes(); |
|||
if (!(bytes[3] > this.Threshold)) |
|||
{ |
|||
// Transparent. Lookup the first color with an alpha value of 0
|
|||
for (int index = 0; index < this.colors.Length; index++) |
|||
{ |
|||
if (this.colors[index].ToBytes()[3] == 0) |
|||
{ |
|||
colorIndex = (byte)index; |
|||
this.TransparentIndex = colorIndex; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// Not transparent...
|
|||
int leastDistance = int.MaxValue; |
|||
int red = bytes[0]; |
|||
int green = bytes[1]; |
|||
int blue = bytes[3]; |
|||
|
|||
// Loop through the entire palette, looking for the closest color match
|
|||
for (int index = 0; index < this.colors.Length; index++) |
|||
{ |
|||
byte[] paletteColor = this.colors[index].ToBytes(); |
|||
int redDistance = paletteColor[0] - red; |
|||
int greenDistance = paletteColor[1] - green; |
|||
int blueDistance = paletteColor[2] - blue; |
|||
|
|||
int distance = (redDistance * redDistance) + |
|||
(greenDistance * greenDistance) + |
|||
(blueDistance * blueDistance); |
|||
|
|||
if (distance < leastDistance) |
|||
{ |
|||
colorIndex = (byte)index; |
|||
leastDistance = distance; |
|||
|
|||
// And if it's an exact match, exit the loop
|
|||
if (distance == 0) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Now I have the color, pop it into the cache for next time
|
|||
this.colorMap.TryAdd(colorHash, colorIndex); |
|||
} |
|||
|
|||
return colorIndex; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override List<T> GetPalette() |
|||
{ |
|||
return this.colors.ToList(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,109 @@ |
|||
// <copyright file="ColorTests.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
using System.Numerics; |
|||
|
|||
namespace ImageProcessorCore.Tests |
|||
{ |
|||
using System; |
|||
using Xunit; |
|||
|
|||
/// <summary>
|
|||
/// Tests the <see cref="Color"/> struct.
|
|||
/// </summary>
|
|||
public class ColorTests |
|||
{ |
|||
/// <summary>
|
|||
/// Tests the equality operators for equality.
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void AreEqual() |
|||
{ |
|||
Color color1 = new Color(0, 0, 0); |
|||
Color color2 = new Color(0, 0, 0, 1F); |
|||
Color color3 = new Color("#000"); |
|||
Color color4 = new Color("#000000"); |
|||
Color color5 = new Color("#FF000000"); |
|||
|
|||
Assert.Equal(color1, color2); |
|||
Assert.Equal(color1, color3); |
|||
Assert.Equal(color1, color4); |
|||
Assert.Equal(color1, color5); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the equality operators for inequality.
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void AreNotEqual() |
|||
{ |
|||
Color color1 = new Color(255, 0, 0, 255); |
|||
Color color2 = new Color(0, 0, 0, 255); |
|||
Color color3 = new Color("#000"); |
|||
Color color4 = new Color("#000000"); |
|||
Color color5 = new Color("#FF000000"); |
|||
|
|||
Assert.NotEqual(color1, color2); |
|||
Assert.NotEqual(color1, color3); |
|||
Assert.NotEqual(color1, color4); |
|||
Assert.NotEqual(color1, color5); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests whether the color constructor correctly assign properties.
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void ConstructorAssignsProperties() |
|||
{ |
|||
Color color1 = new Color(1, .1f, .133f, .864f); |
|||
Assert.Equal(255, color1.R); |
|||
Assert.Equal((byte)Math.Round(.1f * 255), color1.G); |
|||
Assert.Equal((byte)Math.Round(.133f * 255), color1.B); |
|||
Assert.Equal((byte)Math.Round(.864f * 255), color1.A); |
|||
|
|||
Color color2 = new Color(1, .1f, .133f); |
|||
Assert.Equal(255, color2.R); |
|||
Assert.Equal(Math.Round(.1f * 255), color2.G); |
|||
Assert.Equal(Math.Round(.133f * 255), color2.B); |
|||
Assert.Equal(255, color2.A); |
|||
|
|||
Color color3 = new Color("#FF0000"); |
|||
Assert.Equal(255, color3.R); |
|||
Assert.Equal(0, color3.G); |
|||
Assert.Equal(0, color3.B); |
|||
Assert.Equal(255, color3.A); |
|||
|
|||
//Color color4 = new Color(new Vector3(1, .1f, .133f));
|
|||
//Assert.Equal(1, color4.R, 1);
|
|||
//Assert.Equal(.1f, color4.G, 1);
|
|||
//Assert.Equal(.133f, color4.B, 3);
|
|||
//Assert.Equal(1, color4.A, 1);
|
|||
|
|||
//Color color5 = new Color(new Vector3(1, .1f, .133f), .5f);
|
|||
//Assert.Equal(1, color5.R, 1);
|
|||
//Assert.Equal(.1f, color5.G, 1);
|
|||
//Assert.Equal(.133f, color5.B, 3);
|
|||
//Assert.Equal(.5f, color5.A, 1);
|
|||
|
|||
Color color6 = new Color(new Vector4(1, .1f, .133f, .5f)); |
|||
Assert.Equal(255, color6.R); |
|||
Assert.Equal(Math.Round(.1f * 255), color6.G); |
|||
Assert.Equal(Math.Round(.133f * 255), color6.B); |
|||
Assert.Equal(Math.Round(.5f * 255), color6.A); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests to see that in the input hex matches that of the output.
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void ConvertHex() |
|||
{ |
|||
const string First = "FF000000"; |
|||
Color color = Color.Black; |
|||
string second = color.PackedValue().ToString("X"); |
|||
Assert.Equal(First, second); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,493 @@ |
|||
// <copyright file="ColorConversionTests.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Tests |
|||
{ |
|||
using System; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
|
|||
using Xunit; |
|||
|
|||
/// <summary>
|
|||
/// Test conversion between the various color structs.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Output values have been compared with <see cref="http://colormine.org/color-converter"/>
|
|||
/// and <see cref="http://www.colorhexa.com/"/> for accuracy.
|
|||
/// </remarks>
|
|||
public class ColorConversionTests |
|||
{ |
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="Color"/> to <see cref="YCbCr"/>.
|
|||
/// </summary>
|
|||
[Fact] |
|||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", |
|||
Justification = "Reviewed. Suppression is OK here.")] |
|||
public void ColorToYCbCr() |
|||
{ |
|||
// White
|
|||
Color color = Color.White; |
|||
YCbCr yCbCr = color; |
|||
|
|||
Assert.Equal(255, yCbCr.Y, 0); |
|||
Assert.Equal(128, yCbCr.Cb, 0); |
|||
Assert.Equal(128, yCbCr.Cr, 0); |
|||
|
|||
// Black
|
|||
Color color2 = Color.Black; |
|||
YCbCr yCbCr2 = color2; |
|||
Assert.Equal(0, yCbCr2.Y, 0); |
|||
Assert.Equal(128, yCbCr2.Cb, 0); |
|||
Assert.Equal(128, yCbCr2.Cr, 0); |
|||
|
|||
// Gray
|
|||
Color color3 = Color.Gray; |
|||
YCbCr yCbCr3 = color3; |
|||
Assert.Equal(128, yCbCr3.Y, 0); |
|||
Assert.Equal(128, yCbCr3.Cb, 0); |
|||
Assert.Equal(128, yCbCr3.Cr, 0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="YCbCr"/> to <see cref="Color"/>.
|
|||
/// </summary>
|
|||
[Fact] |
|||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", |
|||
Justification = "Reviewed. Suppression is OK here.")] |
|||
public void YCbCrToColor() |
|||
{ |
|||
// White
|
|||
YCbCr yCbCr = new YCbCr(255, 128, 128); |
|||
Color color = yCbCr; |
|||
|
|||
Assert.Equal(255, color.R); |
|||
Assert.Equal(255, color.G); |
|||
Assert.Equal(255, color.B); |
|||
Assert.Equal(255, color.A); |
|||
|
|||
// Black
|
|||
YCbCr yCbCr2 = new YCbCr(0, 128, 128); |
|||
Color color2 = yCbCr2; |
|||
|
|||
Assert.Equal(0, color2.R); |
|||
Assert.Equal(0, color2.G); |
|||
Assert.Equal(0, color2.B); |
|||
Assert.Equal(255, color2.A); |
|||
|
|||
// Gray
|
|||
YCbCr yCbCr3 = new YCbCr(128, 128, 128); |
|||
Color color3 = yCbCr3; |
|||
|
|||
Assert.Equal(128, color3.R); |
|||
Assert.Equal(128, color3.G); |
|||
Assert.Equal(128, color3.B); |
|||
Assert.Equal(255, color3.A); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="Color"/> to <see cref="CieXyz"/>.
|
|||
/// Comparison values obtained from
|
|||
/// http://colormine.org/convert/rgb-to-xyz
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void ColorToCieXyz() |
|||
{ |
|||
// White
|
|||
Color color = Color.White; |
|||
CieXyz ciexyz = color; |
|||
|
|||
Assert.Equal(95.05f, ciexyz.X, 3); |
|||
Assert.Equal(100.0f, ciexyz.Y, 3); |
|||
Assert.Equal(108.900f, ciexyz.Z, 3); |
|||
|
|||
// Black
|
|||
Color color2 = Color.Black; |
|||
CieXyz ciexyz2 = color2; |
|||
Assert.Equal(0, ciexyz2.X, 3); |
|||
Assert.Equal(0, ciexyz2.Y, 3); |
|||
Assert.Equal(0, ciexyz2.Z, 3); |
|||
|
|||
// Gray
|
|||
Color color3 = Color.Gray; |
|||
CieXyz ciexyz3 = color3; |
|||
Assert.Equal(20.518, ciexyz3.X, 3); |
|||
Assert.Equal(21.586, ciexyz3.Y, 3); |
|||
Assert.Equal(23.507, ciexyz3.Z, 3); |
|||
|
|||
// Cyan
|
|||
Color color4 = Color.Cyan; |
|||
CieXyz ciexyz4 = color4; |
|||
Assert.Equal(53.810f, ciexyz4.X, 3); |
|||
Assert.Equal(78.740f, ciexyz4.Y, 3); |
|||
Assert.Equal(106.970f, ciexyz4.Z, 3); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="CieXyz"/> to <see cref="Color"/>.
|
|||
/// Comparison values obtained from
|
|||
/// http://colormine.org/convert/rgb-to-xyz
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void CieXyzToColor() |
|||
{ |
|||
// Dark moderate pink.
|
|||
CieXyz ciexyz = new CieXyz(13.337f, 9.297f, 14.727f); |
|||
Color color = ciexyz; |
|||
|
|||
Assert.Equal(128, color.R); |
|||
Assert.Equal(64, color.G); |
|||
Assert.Equal(106, color.B); |
|||
|
|||
// Ochre
|
|||
CieXyz ciexyz2 = new CieXyz(31.787f, 26.147f, 4.885f); |
|||
Color color2 = ciexyz2; |
|||
|
|||
Assert.Equal(204, color2.R); |
|||
Assert.Equal(119, color2.G); |
|||
Assert.Equal(34, color2.B); |
|||
|
|||
// Black
|
|||
CieXyz ciexyz3 = new CieXyz(0, 0, 0); |
|||
Color color3 = ciexyz3; |
|||
|
|||
Assert.Equal(0, color3.R); |
|||
Assert.Equal(0, color3.G); |
|||
Assert.Equal(0, color3.B); |
|||
|
|||
//// Check others.
|
|||
//Random random = new Random(0);
|
|||
//for (int i = 0; i < 1000; i++)
|
|||
//{
|
|||
// Color color4 = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble());
|
|||
// CieXyz ciexyz4 = color4;
|
|||
// Assert.Equal(color4, (Color)ciexyz4);
|
|||
//}
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="Color"/> to <see cref="Hsv"/>.
|
|||
/// </summary>
|
|||
[Fact] |
|||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", |
|||
Justification = "Reviewed. Suppression is OK here.")] |
|||
public void ColorToHsv() |
|||
{ |
|||
// Black
|
|||
Color b = Color.Black; |
|||
Hsv h = b; |
|||
|
|||
Assert.Equal(0, h.H, 1); |
|||
Assert.Equal(0, h.S, 1); |
|||
Assert.Equal(0, h.V, 1); |
|||
|
|||
// White
|
|||
Color color = Color.White; |
|||
Hsv hsv = color; |
|||
|
|||
Assert.Equal(0f, hsv.H, 1); |
|||
Assert.Equal(0f, hsv.S, 1); |
|||
Assert.Equal(1f, hsv.V, 1); |
|||
|
|||
// Dark moderate pink.
|
|||
Color color2 = new Color(128, 64, 106); |
|||
Hsv hsv2 = color2; |
|||
|
|||
Assert.Equal(320.6f, hsv2.H, 1); |
|||
Assert.Equal(0.5f, hsv2.S, 1); |
|||
Assert.Equal(0.502f, hsv2.V, 2); |
|||
|
|||
// Ochre.
|
|||
Color color3 = new Color(204, 119, 34); |
|||
Hsv hsv3 = color3; |
|||
|
|||
Assert.Equal(30f, hsv3.H, 1); |
|||
Assert.Equal(0.833f, hsv3.S, 3); |
|||
Assert.Equal(0.8f, hsv3.V, 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="Hsv"/> to <see cref="Color"/>.
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void HsvToColor() |
|||
{ |
|||
// Dark moderate pink.
|
|||
Hsv hsv = new Hsv(320.6f, 0.5f, 0.502f); |
|||
Color color = hsv; |
|||
|
|||
Assert.Equal(color.R, 128); |
|||
Assert.Equal(color.G, 64); |
|||
Assert.Equal(color.B, 106); |
|||
|
|||
// Ochre
|
|||
Hsv hsv2 = new Hsv(30, 0.833f, 0.8f); |
|||
Color color2 = hsv2; |
|||
|
|||
Assert.Equal(color2.R, 204); |
|||
Assert.Equal(color2.G, 119); |
|||
Assert.Equal(color2.B, 34); |
|||
|
|||
// White
|
|||
Hsv hsv3 = new Hsv(0, 0, 1); |
|||
Color color3 = hsv3; |
|||
|
|||
Assert.Equal(color3.B, 255); |
|||
Assert.Equal(color3.G, 255); |
|||
Assert.Equal(color3.R, 255); |
|||
|
|||
// Check others.
|
|||
//Random random = new Random(0);
|
|||
//for (int i = 0; i < 1000; i++)
|
|||
//{
|
|||
// Color color4 = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble());
|
|||
// Hsv hsv4 = color4;
|
|||
// Assert.Equal(color4, (Color)hsv4);
|
|||
//}
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="Color"/> to <see cref="Hsl"/>.
|
|||
/// </summary>
|
|||
[Fact] |
|||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", |
|||
Justification = "Reviewed. Suppression is OK here.")] |
|||
public void ColorToHsl() |
|||
{ |
|||
// Black
|
|||
Color b = Color.Black; |
|||
Hsl h = b; |
|||
|
|||
Assert.Equal(0, h.H, 1); |
|||
Assert.Equal(0, h.S, 1); |
|||
Assert.Equal(0, h.L, 1); |
|||
|
|||
// White
|
|||
Color color = Color.White; |
|||
Hsl hsl = color; |
|||
|
|||
Assert.Equal(0f, hsl.H, 1); |
|||
Assert.Equal(0f, hsl.S, 1); |
|||
Assert.Equal(1f, hsl.L, 1); |
|||
|
|||
// Dark moderate pink.
|
|||
Color color2 = new Color(128, 64, 106); |
|||
Hsl hsl2 = color2; |
|||
|
|||
Assert.Equal(320.6f, hsl2.H, 1); |
|||
Assert.Equal(0.33f, hsl2.S, 1); |
|||
Assert.Equal(0.376f, hsl2.L, 2); |
|||
|
|||
// Ochre.
|
|||
Color color3 = new Color(204, 119, 34); |
|||
Hsl hsl3 = color3; |
|||
|
|||
Assert.Equal(30f, hsl3.H, 1); |
|||
Assert.Equal(0.714f, hsl3.S, 3); |
|||
Assert.Equal(0.467f, hsl3.L, 3); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="Hsl"/> to <see cref="Color"/>.
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void HslToColor() |
|||
{ |
|||
// Dark moderate pink.
|
|||
Hsl hsl = new Hsl(320.6f, 0.33f, 0.376f); |
|||
Color color = hsl; |
|||
|
|||
Assert.Equal(color.R, 128); |
|||
Assert.Equal(color.G, 64); |
|||
Assert.Equal(color.B, 106); |
|||
|
|||
// Ochre
|
|||
Hsl hsl2 = new Hsl(30, 0.714f, 0.467f); |
|||
Color color2 = hsl2; |
|||
|
|||
Assert.Equal(color2.R, 204); |
|||
Assert.Equal(color2.G, 119); |
|||
Assert.Equal(color2.B, 34); |
|||
|
|||
// White
|
|||
Hsl hsl3 = new Hsl(0, 0, 1); |
|||
Color color3 = hsl3; |
|||
|
|||
Assert.Equal(color3.R, 255); |
|||
Assert.Equal(color3.G, 255); |
|||
Assert.Equal(color3.B, 255); |
|||
|
|||
// Check others.
|
|||
//Random random = new Random(0);
|
|||
//for (int i = 0; i < 1000; i++)
|
|||
//{
|
|||
// Color color4 = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble());
|
|||
// Hsl hsl4 = color4;
|
|||
// Assert.Equal(color4, (Color)hsl4);
|
|||
//}
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="Color"/> to <see cref="Cmyk"/>.
|
|||
/// </summary>
|
|||
[Fact] |
|||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", |
|||
Justification = "Reviewed. Suppression is OK here.")] |
|||
public void ColorToCmyk() |
|||
{ |
|||
// White
|
|||
Color color = Color.White; |
|||
Cmyk cmyk = color; |
|||
|
|||
Assert.Equal(0, cmyk.C, 1); |
|||
Assert.Equal(0, cmyk.M, 1); |
|||
Assert.Equal(0, cmyk.Y, 1); |
|||
Assert.Equal(0, cmyk.K, 1); |
|||
|
|||
// Black
|
|||
Color color2 = Color.Black; |
|||
Cmyk cmyk2 = color2; |
|||
Assert.Equal(0, cmyk2.C, 1); |
|||
Assert.Equal(0, cmyk2.M, 1); |
|||
Assert.Equal(0, cmyk2.Y, 1); |
|||
Assert.Equal(1, cmyk2.K, 1); |
|||
|
|||
// Gray
|
|||
Color color3 = Color.Gray; |
|||
Cmyk cmyk3 = color3; |
|||
Assert.Equal(0f, cmyk3.C, 1); |
|||
Assert.Equal(0f, cmyk3.M, 1); |
|||
Assert.Equal(0f, cmyk3.Y, 1); |
|||
Assert.Equal(0.498, cmyk3.K, 2); // Checked with other online converters.
|
|||
|
|||
// Cyan
|
|||
Color color4 = Color.Cyan; |
|||
Cmyk cmyk4 = color4; |
|||
Assert.Equal(1, cmyk4.C, 1); |
|||
Assert.Equal(0f, cmyk4.M, 1); |
|||
Assert.Equal(0f, cmyk4.Y, 1); |
|||
Assert.Equal(0f, cmyk4.K, 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="Cmyk"/> to <see cref="Color"/>.
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void CmykToColor() |
|||
{ |
|||
// Dark moderate pink.
|
|||
Cmyk cmyk = new Cmyk(0f, .5f, .171f, .498f); |
|||
Color color = cmyk; |
|||
|
|||
Assert.Equal(color.R, 128); |
|||
Assert.Equal(color.G, 64); |
|||
Assert.Equal(color.B, 106); |
|||
|
|||
// Ochre
|
|||
Cmyk cmyk2 = new Cmyk(0, .416f, .833f, .199f); |
|||
Color color2 = cmyk2; |
|||
|
|||
Assert.Equal(color2.R, 204); |
|||
Assert.Equal(color2.G, 119); |
|||
Assert.Equal(color2.B, 34); |
|||
|
|||
// White
|
|||
Cmyk cmyk3 = new Cmyk(0, 0, 0, 0); |
|||
Color color3 = cmyk3; |
|||
|
|||
Assert.Equal(color3.R, 255); |
|||
Assert.Equal(color3.G, 255); |
|||
Assert.Equal(color3.B, 255); |
|||
|
|||
// Check others.
|
|||
//Random random = new Random(0);
|
|||
//for (int i = 0; i < 1000; i++)
|
|||
//{
|
|||
// Color color4 = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble());
|
|||
// Cmyk cmyk4 = color4;
|
|||
// Assert.Equal(color4, (Color)cmyk4);
|
|||
//}
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="Color"/> to <see cref="CieLab"/>.
|
|||
/// Comparison values obtained from
|
|||
/// http://colormine.org/convert/rgb-to-lab
|
|||
/// </summary>
|
|||
[Fact] |
|||
public void ColorToCieLab() |
|||
{ |
|||
// White
|
|||
Color color = Color.White; |
|||
CieLab cielab = color; |
|||
|
|||
Assert.Equal(100, cielab.L, 3); |
|||
Assert.Equal(0.005, cielab.A, 3); |
|||
Assert.Equal(-0.010, cielab.B, 3); |
|||
|
|||
// Black
|
|||
Color color2 = Color.Black; |
|||
CieLab cielab2 = color2; |
|||
Assert.Equal(0, cielab2.L, 3); |
|||
Assert.Equal(0, cielab2.A, 3); |
|||
Assert.Equal(0, cielab2.B, 3); |
|||
|
|||
// Gray
|
|||
Color color3 = Color.Gray; |
|||
CieLab cielab3 = color3; |
|||
Assert.Equal(53.585, cielab3.L, 3); |
|||
Assert.Equal(0.003, cielab3.A, 3); |
|||
Assert.Equal(-0.006, cielab3.B, 3); |
|||
|
|||
// Cyan
|
|||
Color color4 = Color.Cyan; |
|||
CieLab cielab4 = color4; |
|||
Assert.Equal(91.117, cielab4.L, 3); |
|||
Assert.Equal(-48.080, cielab4.A, 3); |
|||
Assert.Equal(-14.138, cielab4.B, 3); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests the implicit conversion from <see cref="CieLab"/> to <see cref="Color"/>.
|
|||
/// </summary>
|
|||
/// Comparison values obtained from
|
|||
/// http://colormine.org/convert/rgb-to-lab
|
|||
[Fact] |
|||
public void CieLabToColor() |
|||
{ |
|||
// Dark moderate pink.
|
|||
CieLab cielab = new CieLab(36.5492f, 33.3173f, -12.0615f); |
|||
Color color = cielab; |
|||
|
|||
Assert.Equal(color.R, 128); |
|||
Assert.Equal(color.G, 64); |
|||
Assert.Equal(color.B, 106); |
|||
|
|||
// Ochre
|
|||
CieLab cielab2 = new CieLab(58.1758f, 27.3399f, 56.8240f); |
|||
Color color2 = cielab2; |
|||
|
|||
Assert.Equal(color2.R, 204); |
|||
Assert.Equal(color2.G, 119); |
|||
Assert.Equal(color2.B, 34); |
|||
|
|||
// Black
|
|||
CieLab cielab3 = new CieLab(0, 0, 0); |
|||
Color color3 = cielab3; |
|||
|
|||
Assert.Equal(color3.R, 0); |
|||
Assert.Equal(color3.G, 0); |
|||
Assert.Equal(color3.B, 0); |
|||
|
|||
// Check others.
|
|||
//Random random = new Random(0);
|
|||
//for (int i = 0; i < 1000; i++)
|
|||
//{
|
|||
// Color color4 = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble());
|
|||
// CieLab cielab4 = color4;
|
|||
// Assert.Equal(color4, (Color)cielab4);
|
|||
//}
|
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue