mirror of https://github.com/SixLabors/ImageSharp
33 changed files with 1246 additions and 1990 deletions
@ -1,288 +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 ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Numerics; |
|||
using Colors.Spaces; |
|||
|
|||
/// <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, 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.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); |
|||
} |
|||
|
|||
/// <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) < Constants.Epsilon) |
|||
{ |
|||
return new Color(v, v, v, 1); |
|||
} |
|||
|
|||
float h = (Math.Abs(color.H - 360) < Constants.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) > Constants.Epsilon) |
|||
{ |
|||
if (Math.Abs(s) < Constants.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; |
|||
} |
|||
} |
|||
} |
|||
@ -1,166 +0,0 @@ |
|||
// <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 ImageSharp.Colors.Spaces |
|||
{ |
|||
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>
|
|||
/// Min range used for clamping
|
|||
/// </summary>
|
|||
private static readonly Vector4 VectorMin = Vector4.Zero; |
|||
|
|||
/// <summary>
|
|||
/// Max range used for clamping
|
|||
/// </summary>
|
|||
private static readonly Vector4 VectorMax = new Vector4(255); |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private readonly 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), VectorMin, VectorMax); |
|||
} |
|||
|
|||
/// <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 this.backingVector.GetHashCode(); |
|||
} |
|||
|
|||
/// <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); |
|||
} |
|||
} |
|||
} |
|||
@ -1,189 +0,0 @@ |
|||
// <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 ImageSharp.Colors.Spaces |
|||
{ |
|||
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>
|
|||
/// Min range used for clamping
|
|||
/// </summary>
|
|||
private static readonly Vector4 VectorMin = Vector4.Zero; |
|||
|
|||
/// <summary>
|
|||
/// Max range used for clamping
|
|||
/// </summary>
|
|||
private static readonly Vector4 VectorMax = Vector4.One; |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private readonly 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), VectorMin, VectorMax); |
|||
} |
|||
|
|||
/// <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) <= Constants.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 this.backingVector.GetHashCode(); |
|||
} |
|||
|
|||
/// <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.backingVector.Equals(other.backingVector); |
|||
} |
|||
|
|||
/// <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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// <copyright file="CieConstants.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
/// <summary>
|
|||
/// Constants use for Cie conversion calculations
|
|||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html"/>
|
|||
/// </summary>
|
|||
internal static class CieConstants |
|||
{ |
|||
/// <summary>
|
|||
/// 216F / 24389F
|
|||
/// </summary>
|
|||
public const float Epsilon = 0.008856452F; |
|||
|
|||
/// <summary>
|
|||
/// 24389F / 27F
|
|||
/// </summary>
|
|||
public const float Kappa = 903.2963F; |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// <copyright file="ColorConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using System; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
|||
/// </summary>
|
|||
public partial class ColorConverter |
|||
{ |
|||
/// <summary>
|
|||
/// Performs chromatic adaptation of given XYZ color.
|
|||
/// Target white point is <see cref="WhitePoint"/>.
|
|||
/// </summary>
|
|||
public CieXyz Adapt(CieXyz color, CieXyz sourceWhitePoint) |
|||
{ |
|||
Guard.NotNull(color, nameof(color)); |
|||
Guard.NotNull(sourceWhitePoint, nameof(sourceWhitePoint)); |
|||
|
|||
if (!this.IsChromaticAdaptationPerformed) |
|||
{ |
|||
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); |
|||
} |
|||
|
|||
return this.ChromaticAdaptation.Transform(color, sourceWhitePoint, this.WhitePoint); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
// <copyright file="ColorConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using Implementation; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
|||
/// </summary>
|
|||
public partial class ColorConverter |
|||
{ |
|||
/// <summary>
|
|||
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLab"/>
|
|||
/// </summary>
|
|||
/// <param name="color">The color to convert.</param>
|
|||
/// <returns>The <see cref="CieLab"/></returns>
|
|||
public CieLab ToCieLab(CieXyz color) |
|||
{ |
|||
Guard.NotNull(color, nameof(color)); |
|||
|
|||
// Adaptation
|
|||
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed |
|||
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint) |
|||
: color; |
|||
|
|||
// Conversion
|
|||
CieXyzToCieLabConverter converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint); |
|||
return converter.Convert(adapted); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
// <copyright file="ColorConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using Implementation; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
|||
/// </summary>
|
|||
public partial class ColorConverter |
|||
{ |
|||
private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter(); |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="CieLab"/> into a <see cref="CieXyz"/>
|
|||
/// </summary>
|
|||
/// <param name="color">The color to convert.</param>
|
|||
/// <returns>The <see cref="CieXyz"/></returns>
|
|||
public CieXyz ToCieXyz(CieLab color) |
|||
{ |
|||
Guard.NotNull(color, nameof(color)); |
|||
|
|||
// Conversion
|
|||
|
|||
CieXyz unadapted = CieLabToCieXyzConverter.Convert(color); |
|||
|
|||
// Adaptation
|
|||
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed |
|||
? unadapted |
|||
: this.Adapt(unadapted, color.WhitePoint); |
|||
|
|||
return adapted; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="Lms"/> into a <see cref="CieXyz"/>
|
|||
/// </summary>
|
|||
/// <param name="color">The color to convert.</param>
|
|||
/// <returns>The <see cref="CieXyz"/></returns>
|
|||
public CieXyz ToCieXyz(Lms color) |
|||
{ |
|||
Guard.NotNull(color, nameof(color)); |
|||
|
|||
// Conversion
|
|||
return this.cachedCieXyzAndLmsConverter.Convert(color); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
// <copyright file="ColorConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using Implementation; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
|||
/// </summary>
|
|||
public partial class ColorConverter |
|||
{ |
|||
/// <summary>
|
|||
/// Converts a <see cref="CieXyz"/> into a <see cref="Lms"/>
|
|||
/// </summary>
|
|||
/// <param name="color">The color to convert.</param>
|
|||
/// <returns>The <see cref="Lms"/></returns>
|
|||
public Lms ToLms(CieXyz color) |
|||
{ |
|||
Guard.NotNull(color, nameof(color)); |
|||
|
|||
// Conversion
|
|||
return this.cachedCieXyzAndLmsConverter.Convert(color); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
// <copyright file="ColorConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using System.Numerics; |
|||
using Implementation; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
|||
/// </summary>
|
|||
public partial class ColorConverter |
|||
{ |
|||
private Matrix4x4 transformationMatrix; |
|||
private CieXyzAndLmsConverter cachedCieXyzAndLmsConverter; |
|||
|
|||
/// <summary>
|
|||
/// The default whitepoint used for converting to CieLab
|
|||
/// </summary>
|
|||
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ColorConverter"/> class.
|
|||
/// </summary>
|
|||
public ColorConverter() |
|||
{ |
|||
// Note the order here this is important.
|
|||
this.WhitePoint = DefaultWhitePoint; |
|||
this.LmsAdaptationMatrix = CieXyzAndLmsConverter.DefaultTransformationMatrix; |
|||
this.ChromaticAdaptation = new VonKriesChromaticAdaptation(this.cachedCieXyzAndLmsConverter, this.cachedCieXyzAndLmsConverter); |
|||
this.TargetLabWhitePoint = CieLab.DefaultWhitePoint; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space.
|
|||
/// When null, no adaptation will be performed.
|
|||
/// </summary>
|
|||
public CieXyz WhitePoint { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information)
|
|||
/// Defaults to: <see cref="CieLab.DefaultWhitePoint"/>.
|
|||
/// </summary>
|
|||
public CieXyz TargetLabWhitePoint { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed.
|
|||
/// </summary>
|
|||
public IChromaticAdaptation ChromaticAdaptation { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets transformation matrix used in conversion to <see cref="Lms"/>,
|
|||
/// also used in the default Von Kries Chromatic Adaptation method.
|
|||
/// </summary>
|
|||
public Matrix4x4 LmsAdaptationMatrix |
|||
{ |
|||
get { return this.transformationMatrix; } |
|||
set |
|||
{ |
|||
this.transformationMatrix = value; |
|||
|
|||
if (this.cachedCieXyzAndLmsConverter == null) |
|||
{ |
|||
this.cachedCieXyzAndLmsConverter = new CieXyzAndLmsConverter(value); |
|||
} |
|||
else |
|||
{ |
|||
this.cachedCieXyzAndLmsConverter.TransformationMatrix = value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether chromatic adaptation has been performed.
|
|||
/// </summary>
|
|||
private bool IsChromaticAdaptationPerformed => this.ChromaticAdaptation != null; |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
// <copyright file="IChromaticAdaptation.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Chromatic adaptation.
|
|||
/// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M]
|
|||
/// which is dependent on the source reference white (XWS, YWS, ZWS) and the destination reference white (XWD, YWD, ZWD).
|
|||
/// </summary>
|
|||
public interface IChromaticAdaptation |
|||
{ |
|||
/// <summary>
|
|||
/// Performs a linear transformation of a source color in to the destination color.
|
|||
/// </summary>
|
|||
/// <remarks>Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).</remarks>
|
|||
/// <param name="sourceColor">The source color.</param>
|
|||
/// <param name="sourceWhitePoint">The source white point.</param>
|
|||
/// <param name="targetWhitePoint">The target white point.</param>
|
|||
/// <returns>The <see cref="CieXyz"/></returns>
|
|||
CieXyz Transform(CieXyz sourceColor, CieXyz sourceWhitePoint, CieXyz targetWhitePoint); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// <copyright file="IColorConversion.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
/// <summary>
|
|||
/// Converts color between two color spaces.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The input color type.</typeparam>
|
|||
/// <typeparam name="TResult">The result color type.</typeparam>
|
|||
public interface IColorConversion<in T, out TResult> |
|||
{ |
|||
/// <summary>
|
|||
/// Performs the conversion from the <see cref="T"/> input to an instance of the output <see cref="TResult"/> type.
|
|||
/// </summary>
|
|||
/// <param name="input">The input color instance.</param>
|
|||
/// <returns>The <see cref="TResult"/></returns>
|
|||
TResult Convert(T input); |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
// <copyright file="CieLabToCieXyzConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion.Implementation |
|||
{ |
|||
using System; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts from <see cref="CieLab"/> to <see cref="CieXyz"/>.
|
|||
/// </summary>
|
|||
public class CieLabToCieXyzConverter : IColorConversion<CieLab, CieXyz> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public CieXyz Convert(CieLab input) |
|||
{ |
|||
Guard.NotNull(input, nameof(input)); |
|||
|
|||
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
|
|||
float l = input.L, a = input.A, b = input.B; |
|||
float fy = (l + 16) / 116F; |
|||
float fx = a / 500F + fy; |
|||
float fz = fy - b / 200F; |
|||
|
|||
float fx3 = (float)Math.Pow(fx, 3D); |
|||
float fz3 = (float)Math.Pow(fz, 3D); |
|||
|
|||
float xr = fx3 > CieConstants.Epsilon ? fx3 : (116F * fx - 16F) / CieConstants.Kappa; |
|||
float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? (float)Math.Pow((l + 16F) / 116F, 3D) : l / CieConstants.Kappa; |
|||
float zr = fz3 > CieConstants.Epsilon ? fz3 : (116F * fz - 16F) / CieConstants.Kappa; |
|||
|
|||
float wx = input.WhitePoint.X, wy = input.WhitePoint.Y, wz = input.WhitePoint.Z; |
|||
|
|||
// Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white)
|
|||
xr = xr.Clamp(0, 1F); |
|||
yr = yr.Clamp(0, 1F); |
|||
zr = zr.Clamp(0, 1F); |
|||
|
|||
float x = xr * wx; |
|||
float y = yr * wy; |
|||
float z = zr * wz; |
|||
|
|||
return new CieXyz(x, y, z); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
// <copyright file="CieXyzToCieLabConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion.Implementation |
|||
{ |
|||
using System; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts from <see cref="CieXyz"/> to <see cref="CieLab"/>.
|
|||
/// </summary>
|
|||
public class CieXyzToCieLabConverter : IColorConversion<CieXyz, CieLab> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
|
|||
/// </summary>
|
|||
public CieXyzToCieLabConverter() |
|||
: this(CieLab.DefaultWhitePoint) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
|
|||
/// </summary>
|
|||
/// <param name="labWhitePoint">The target reference lab white point</param>
|
|||
public CieXyzToCieLabConverter(CieXyz labWhitePoint) |
|||
{ |
|||
this.LabWhitePoint = labWhitePoint; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the target reference whitepoint. When not set, <see cref="CieLab.DefaultWhitePoint"/> is used.
|
|||
/// </summary>
|
|||
public CieXyz LabWhitePoint { get; } |
|||
|
|||
/// <inheritdoc />
|
|||
public CieLab Convert(CieXyz input) |
|||
{ |
|||
Guard.NotNull(input, nameof(input)); |
|||
|
|||
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
|
|||
float wx = this.LabWhitePoint.X, wy = this.LabWhitePoint.Y, wz = this.LabWhitePoint.Z; |
|||
|
|||
float xr = input.X / wx, yr = input.Y / wy, zr = input.Z / wz; |
|||
|
|||
float fx = xr > CieConstants.Epsilon ? (float)Math.Pow(xr, 0.333333333333333D) : (CieConstants.Kappa * xr + 16F) / 116F; |
|||
float fy = yr > CieConstants.Epsilon ? (float)Math.Pow(yr, 0.333333333333333D) : (CieConstants.Kappa * yr + 16F) / 116F; |
|||
float fz = zr > CieConstants.Epsilon ? (float)Math.Pow(zr, 0.333333333333333D) : (CieConstants.Kappa * zr + 16F) / 116F; |
|||
|
|||
float l = (116F * fy) - 16F; |
|||
float a = 500F * (fx - fy); |
|||
float b = 200F * (fy - fz); |
|||
|
|||
return new CieLab(l, a, b, this.LabWhitePoint); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
// <copyright file="CieXyzAndLmsConverter.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 ImageSharp.Colors.Conversion.Implementation |
|||
{ |
|||
using Spaces; |
|||
|
|||
public class CieXyzAndLmsConverter : IColorConversion<CieXyz, Lms>, IColorConversion<Lms, CieXyz> |
|||
{ |
|||
/// <summary>
|
|||
/// Default transformation matrix used, when no other is set. (Bradford)
|
|||
/// <see cref="LmsAdaptationMatrix"/>
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 DefaultTransformationMatrix = LmsAdaptationMatrix.Bradford; |
|||
|
|||
private Matrix4x4 inverseTransformationMatrix; |
|||
private Matrix4x4 transformationMatrix; |
|||
|
|||
/// <summary>
|
|||
/// Transformation matrix used for the conversion (definition of the cone response domain).
|
|||
/// <see cref="LmsAdaptationMatrix"/>
|
|||
/// </summary>
|
|||
public Matrix4x4 TransformationMatrix |
|||
{ |
|||
get { return this.transformationMatrix; } |
|||
internal set |
|||
{ |
|||
this.transformationMatrix = value; |
|||
Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyzAndLmsConverter"/> class.
|
|||
/// </summary>
|
|||
public CieXyzAndLmsConverter() |
|||
: this(DefaultTransformationMatrix) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyzAndLmsConverter"/> class.
|
|||
/// </summary>
|
|||
/// <param name="transformationMatrix">
|
|||
/// Definition of the cone response domain (see <see cref="LmsAdaptationMatrix"/>),
|
|||
/// if not set <see cref="DefaultTransformationMatrix"/> will be used.
|
|||
/// </param>
|
|||
public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix) |
|||
{ |
|||
this.TransformationMatrix = transformationMatrix; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public Lms Convert(CieXyz input) |
|||
{ |
|||
Guard.NotNull(input, nameof(input)); |
|||
|
|||
Vector3 vector = Vector3.Transform(input.Vector, this.transformationMatrix); |
|||
return new Lms(vector); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public CieXyz Convert(Lms input) |
|||
{ |
|||
Vector3 vector = Vector3.Transform(input.Vector, this.inverseTransformationMatrix); |
|||
return new CieXyz(vector); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
// <copyright file="LmsAdaptationMatrix.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace ImageSharp.Colors.Conversion.Implementation |
|||
{ |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// AdaptionMatrix3X3 used for transformation from XYZ to LMS, defining the cone response domain.
|
|||
/// Used in <see cref="IChromaticAdaptation"/>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// AdaptionMatrix3X3 data obtained from:
|
|||
/// Two New von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization
|
|||
/// S. Bianco, R. Schettini
|
|||
/// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy
|
|||
/// http://www.ivl.disco.unimib.it/papers2003/CRA-CAT.pdf
|
|||
/// </remarks>
|
|||
public static class LmsAdaptationMatrix |
|||
{ |
|||
/// <summary>
|
|||
/// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65)
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 VonKriesHPEAdjusted |
|||
= new Matrix4x4() |
|||
{ |
|||
M11 = 0.40024F, M12 = 0.7076F, M13 = -0.08081F, |
|||
M21 = -0.2263F, M22 = 1.16532F, M23 = 0.0457F, |
|||
M31 = 0, M32 = 0, M33 = 0.91822F, |
|||
M44 = 1F // Important for inverse transforms.
|
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy)
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 VonKriesHPE |
|||
= new Matrix4x4 |
|||
{ |
|||
M11 = 0.3897F, M12 = 0.6890F, M13 = -0.0787F, |
|||
M21 = -0.2298F, M22 = 1.1834F, M23 = 0.0464F, |
|||
M31 = 0, M32 = 0, M33 = 1F, |
|||
M44 = 1F |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// XYZ scaling chromatic adaptation transform matrix
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 XYZScaling = Matrix4x4.Identity; |
|||
|
|||
/// <summary>
|
|||
/// Bradford chromatic adaptation transform matrix (used in CMCCAT97)
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 Bradford |
|||
= new Matrix4x4 |
|||
{ |
|||
M11 = 0.8951F, M12 = 0.2664F, M13 = -0.1614F, |
|||
M21 = -0.7502F, M22 = 1.7135F, M23 = 0.0367F, |
|||
M31 = 0.0389F, M32 = -0.0685F, M33 = 1.0296F, |
|||
M44 = 1F |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Spectral sharpening and the Bradford transform
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 BradfordSharp |
|||
= new Matrix4x4 |
|||
{ |
|||
M11 = 1.2694F, M12 = -0.0988F, M13 = -0.1706F, |
|||
M21 = -0.8364F, M22 = 1.8006F, M23 = 0.0357F, |
|||
M31 = 0.0297F, M32 = -0.0315F, M33 = 1.0018F, |
|||
M44 = 1F |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// CMCCAT2000 (fitted from all available color data sets)
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 CMCCAT2000 |
|||
= new Matrix4x4 |
|||
{ |
|||
M11 = 0.7982F, M12 = 0.3389F, M13 = -0.1371F, |
|||
M21 = -0.5918F, M22 = 1.5512F, M23 = 0.0406F, |
|||
M31 = 0.0008F, M32 = 0.239F, M33 = 0.9753F, |
|||
M44 = 1F |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// CAT02 (optimized for minimizing CIELAB differences)
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 CAT02 |
|||
= new Matrix4x4 |
|||
{ |
|||
M11 = 0.7328F, M12 = 0.4296F, M13 = -0.1624F, |
|||
M21 = -0.7036F, M22 = 1.6975F, M23 = 0.0061F, |
|||
M31 = 0.0030F, M32 = 0.0136F, M33 = 0.9834F, |
|||
M44 = 1F |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
// <copyright file="VonKriesChromaticAdaptation.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using System.Numerics; |
|||
|
|||
using Implementation; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Basic implementation of the von Kries chromatic adaptation model
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Transformation described here:
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
|
|||
/// </remarks>
|
|||
public class VonKriesChromaticAdaptation : IChromaticAdaptation |
|||
{ |
|||
private readonly IColorConversion<CieXyz, Lms> conversionToLms; |
|||
|
|||
private readonly IColorConversion<Lms, CieXyz> conversionToXyz; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
|||
/// </summary>
|
|||
public VonKriesChromaticAdaptation() |
|||
: this(new CieXyzAndLmsConverter()) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
|||
/// </summary>
|
|||
/// <param name="transformationMatrix">
|
|||
/// The transformation matrix used for the conversion (definition of the cone response domain).
|
|||
/// <see cref="LmsAdaptationMatrix"/>
|
|||
/// </param>
|
|||
public VonKriesChromaticAdaptation(Matrix4x4 transformationMatrix) |
|||
: this(new CieXyzAndLmsConverter(transformationMatrix)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
|||
/// </summary>
|
|||
/// <param name="converter"></param>
|
|||
private VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) |
|||
: this(converter, converter) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
|||
/// </summary>
|
|||
/// <param name="conversionToLms">The <see cref="Lms"/> color converter.</param>
|
|||
/// <param name="conversionToCieXyz">The <see cref="CieXyz"/> color converter.</param>
|
|||
public VonKriesChromaticAdaptation(IColorConversion<CieXyz, Lms> conversionToLms, IColorConversion<Lms, CieXyz> conversionToCieXyz) |
|||
{ |
|||
Guard.NotNull(conversionToLms, nameof(conversionToLms)); |
|||
Guard.NotNull(conversionToCieXyz, nameof(conversionToCieXyz)); |
|||
|
|||
this.conversionToLms = conversionToLms; |
|||
this.conversionToXyz = conversionToCieXyz; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public CieXyz Transform(CieXyz sourceColor, CieXyz sourceWhitePoint, CieXyz targetWhitePoint) |
|||
{ |
|||
Guard.NotNull(sourceColor, nameof(sourceColor)); |
|||
Guard.NotNull(sourceWhitePoint, nameof(sourceWhitePoint)); |
|||
Guard.NotNull(targetWhitePoint, nameof(targetWhitePoint)); |
|||
|
|||
if (sourceWhitePoint.Equals(targetWhitePoint)) |
|||
{ |
|||
return sourceColor; |
|||
} |
|||
|
|||
Lms sourceColorLms = this.conversionToLms.Convert(sourceColor); |
|||
Lms sourceWhitePointLms = this.conversionToLms.Convert(sourceWhitePoint); |
|||
Lms targetWhitePointLms = this.conversionToLms.Convert(targetWhitePoint); |
|||
|
|||
Vector3 vector = new Vector3(targetWhitePointLms.L / sourceWhitePointLms.L, targetWhitePointLms.M / sourceWhitePointLms.M, targetWhitePointLms.S / sourceWhitePointLms.S); |
|||
|
|||
Lms targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.Vector)); |
|||
return this.conversionToXyz.Convert(targetColorLms); |
|||
} |
|||
} |
|||
} |
|||
@ -1,207 +0,0 @@ |
|||
// <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 ImageSharp.Colors.Spaces |
|||
{ |
|||
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>
|
|||
/// Min range used for clamping
|
|||
/// </summary>
|
|||
private static readonly Vector3 VectorMin = Vector3.Zero; |
|||
|
|||
/// <summary>
|
|||
/// Max range used for clamping
|
|||
/// </summary>
|
|||
private static readonly Vector3 VectorMax = new Vector3(360, 1, 1); |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private readonly 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), VectorMin, VectorMax); |
|||
} |
|||
|
|||
/// <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) < Constants.Epsilon) |
|||
{ |
|||
return new Hsl(0, s, l); |
|||
} |
|||
|
|||
if (Math.Abs(r - max) < Constants.Epsilon) |
|||
{ |
|||
h = (g - b) / chroma; |
|||
} |
|||
else if (Math.Abs(g - max) < Constants.Epsilon) |
|||
{ |
|||
h = 2 + ((b - r) / chroma); |
|||
} |
|||
else if (Math.Abs(b - max) < Constants.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 this.backingVector.GetHashCode(); |
|||
} |
|||
|
|||
/// <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.backingVector.Equals(other.backingVector); |
|||
} |
|||
|
|||
/// <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; |
|||
} |
|||
} |
|||
} |
|||
@ -1,200 +0,0 @@ |
|||
// <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 ImageSharp.Colors.Spaces |
|||
{ |
|||
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>
|
|||
/// Min range used for clamping
|
|||
/// </summary>
|
|||
private static readonly Vector3 VectorMin = Vector3.Zero; |
|||
|
|||
/// <summary>
|
|||
/// Max range used for clamping
|
|||
/// </summary>
|
|||
private static readonly Vector3 VectorMax = new Vector3(360, 1, 1); |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private readonly 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), VectorMin, VectorMax); |
|||
} |
|||
|
|||
/// <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) < Constants.Epsilon) |
|||
{ |
|||
return new Hsv(0, s, v); |
|||
} |
|||
|
|||
if (Math.Abs(r - max) < Constants.Epsilon) |
|||
{ |
|||
h = (g - b) / chroma; |
|||
} |
|||
else if (Math.Abs(g - max) < Constants.Epsilon) |
|||
{ |
|||
h = 2 + ((b - r) / chroma); |
|||
} |
|||
else if (Math.Abs(b - max) < Constants.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 this.backingVector.GetHashCode(); |
|||
} |
|||
|
|||
/// <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.backingVector.Equals(other.backingVector); |
|||
} |
|||
|
|||
/// <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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// <copyright file="IColorVector.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Color represented as a vector in its color space
|
|||
/// </summary>
|
|||
public interface IColorVector |
|||
{ |
|||
/// <summary>
|
|||
/// The vector representation of the color
|
|||
/// </summary>
|
|||
Vector3 Vector { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// <copyright file="ICompanding.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
/// <summary>
|
|||
/// Pair of companding functions for <see cref="IRgbWorkingSpace"/>.
|
|||
/// Used for conversion to <see cref="CieXyz"/> and backwards.
|
|||
/// See also: <seealso cref="IRgbWorkingSpace.Companding"/>
|
|||
/// </summary>
|
|||
public interface ICompanding |
|||
{ |
|||
/// <summary>
|
|||
/// Companded channel is made linear with respect to the energy.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// For more info see:
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
|
|||
/// </remarks>
|
|||
float InverseCompanding(float channel); |
|||
|
|||
/// <summary>
|
|||
/// Uncompanded channel (linear) is made nonlinear (depends on the RGB color system).
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// For more info see:
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html
|
|||
/// </remarks>
|
|||
float Companding(float channel); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// <copyright file="IRgbWorkingSpace.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
/// <summary>
|
|||
/// Encasulates the RGB working color space
|
|||
/// </summary>
|
|||
public interface IRgbWorkingSpace |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the reference white of the color space
|
|||
/// </summary>
|
|||
CieXyz WhitePoint { get; } |
|||
|
|||
/// <summary>
|
|||
/// Chromaticity coordinates of the primaries
|
|||
/// </summary>
|
|||
// RGBPrimariesChromaticityCoordinates ChromaticityCoordinates { get; }
|
|||
|
|||
/// <summary>
|
|||
/// The companding function associated with the RGB color system.
|
|||
/// Used for conversion to XYZ and backwards.
|
|||
/// See this for more information:
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html
|
|||
/// </summary>
|
|||
ICompanding Companding { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
/// <summary>
|
|||
/// The well known standard illuminants.
|
|||
/// Standard illuminants provide a basis for comparing images or colors recorded under different lighting
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Coefficients taken from:
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
|
|||
/// <br />
|
|||
/// Descriptions taken from:
|
|||
/// http://en.wikipedia.org/wiki/Standard_illuminant
|
|||
/// </remarks>
|
|||
public static class Illuminants |
|||
{ |
|||
/// <summary>
|
|||
/// Incandescent / Tungsten
|
|||
/// </summary>
|
|||
public static readonly CieXyz A = new CieXyz(1.09850F, 1F, 0.35585F); |
|||
|
|||
/// <summary>
|
|||
/// Direct sunlight at noon (obsoleteF)
|
|||
/// </summary>
|
|||
public static readonly CieXyz B = new CieXyz(0.99072F, 1F, 0.85223F); |
|||
|
|||
/// <summary>
|
|||
/// Average / North sky Daylight (obsoleteF)
|
|||
/// </summary>
|
|||
public static readonly CieXyz C = new CieXyz(0.98074F, 1F, 1.18232F); |
|||
|
|||
/// <summary>
|
|||
/// Horizon Light. ICC profile PCS
|
|||
/// </summary>
|
|||
public static readonly CieXyz D50 = new CieXyz(0.96422F, 1F, 0.82521F); |
|||
|
|||
/// <summary>
|
|||
/// Mid-morning / Mid-afternoon Daylight
|
|||
/// </summary>
|
|||
public static readonly CieXyz D55 = new CieXyz(0.95682F, 1F, 0.92149F); |
|||
|
|||
/// <summary>
|
|||
/// Noon Daylight: TelevisionF, sRGB color space
|
|||
/// </summary>
|
|||
public static readonly CieXyz D65 = new CieXyz(0.95047F, 1F, 1.08883F); |
|||
|
|||
/// <summary>
|
|||
/// North sky Daylight
|
|||
/// </summary>
|
|||
public static readonly CieXyz D75 = new CieXyz(0.94972F, 1F, 1.22638F); |
|||
|
|||
/// <summary>
|
|||
/// Equal energy
|
|||
/// </summary>
|
|||
public static readonly CieXyz E = new CieXyz(1F, 1F, 1F); |
|||
|
|||
/// <summary>
|
|||
/// Cool White Fluorescent
|
|||
/// </summary>
|
|||
public static readonly CieXyz F2 = new CieXyz(0.99186F, 1F, 0.67393F); |
|||
|
|||
/// <summary>
|
|||
/// D65 simulatorF, Daylight simulator
|
|||
/// </summary>
|
|||
public static readonly CieXyz F7 = new CieXyz(0.95041F, 1F, 1.08747F); |
|||
|
|||
/// <summary>
|
|||
/// Philips TL84F, Ultralume 40
|
|||
/// </summary>
|
|||
public static readonly CieXyz F11 = new CieXyz(1.00962F, 1F, 0.64350F); |
|||
} |
|||
} |
|||
@ -0,0 +1,156 @@ |
|||
// <copyright file="Lms.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// LMS is a color space represented by the response of the three types of cones of the human eye,
|
|||
/// named after their responsivity (sensitivity) at long, medium and short wavelengths.
|
|||
/// <see href="https://en.wikipedia.org/wiki/LMS_color_space"/>
|
|||
/// </summary>
|
|||
public struct Lms : IColorVector, IEquatable<Lms>, IAlmostEquatable<Lms, float> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="Lms"/> that has Y, Cb, and Cr values set to zero.
|
|||
/// </summary>
|
|||
public static readonly Lms Empty = default(Lms); |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private readonly Vector3 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Lms"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="l">L represents the responsivity at long wavelengths.</param>
|
|||
/// <param name="m">M represents the responsivity at medium wavelengths.</param>
|
|||
/// <param name="s">S represents the responsivity at short wavelengths.</param>
|
|||
public Lms(float l, float m, float s) |
|||
: this(new Vector3(l, m, s)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Lms"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector representing the x, y, z components.</param>
|
|||
public Lms(Vector3 vector) |
|||
: this() |
|||
{ |
|||
// Not clamping as documentation about this space seems to indicate "usual" ranges
|
|||
this.backingVector = vector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the L long component.
|
|||
/// <remarks>A value usually ranging between -1 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float L => this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the M medium component.
|
|||
/// <remarks>A value usually ranging between -1 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float M => this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the S short component.
|
|||
/// <remarks>A value usually ranging between -1 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float S => this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="Lms"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.Equals(Empty); |
|||
|
|||
/// <inheritdoc />
|
|||
public Vector3 Vector => this.backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Lms"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Lms"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Lms"/> 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 ==(Lms left, Lms right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Lms"/> objects for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Lms"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Lms"/> 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 !=(Lms left, Lms right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return this.backingVector.GetHashCode(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "Lms [ Empty ]"; |
|||
} |
|||
|
|||
return $"Lms [ L={this.L:#0.##}, M={this.M:#0.##}, S={this.S:#0.##} ]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is Lms) |
|||
{ |
|||
return this.Equals((Lms)obj); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(Lms other) |
|||
{ |
|||
return this.backingVector.Equals(other.backingVector); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool AlmostEquals(Lms other, float precision) |
|||
{ |
|||
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); |
|||
|
|||
return result.X <= precision |
|||
&& result.Y <= precision |
|||
&& result.Z <= precision; |
|||
} |
|||
} |
|||
} |
|||
@ -1,165 +0,0 @@ |
|||
// <copyright file="YCbCr.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Represents an YCbCr (luminance, blue chroma, red chroma) color conforming to the full range standard used in digital imaging systems.
|
|||
/// <see href="http://en.wikipedia.org/wiki/YCbCr"/>
|
|||
/// </summary>
|
|||
public struct YCbCr : IEquatable<YCbCr> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="YCbCr"/> that has Y, Cb, and Cr values set to zero.
|
|||
/// </summary>
|
|||
public static readonly YCbCr Empty = default(YCbCr); |
|||
|
|||
/// <summary>
|
|||
/// Min range used for clamping
|
|||
/// </summary>
|
|||
private static readonly Vector3 VectorMin = Vector3.Zero; |
|||
|
|||
/// <summary>
|
|||
/// Vector which is used in clamping to the max value
|
|||
/// </summary>
|
|||
private static readonly Vector3 VectorMax = new Vector3(255); |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private readonly Vector3 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="YCbCr"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="y">The y luminance component.</param>
|
|||
/// <param name="cb">The cb chroma component.</param>
|
|||
/// <param name="cr">The cr chroma component.</param>
|
|||
public YCbCr(byte y, byte cb, byte cr) |
|||
: this() |
|||
{ |
|||
this.backingVector = Vector3.Clamp(new Vector3(y, cb, cr), VectorMin, VectorMax); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the Y luminance component.
|
|||
/// <remarks>A value ranging between 0 and 255.</remarks>
|
|||
/// </summary>
|
|||
public byte Y => (byte)this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the Cb chroma component.
|
|||
/// <remarks>A value ranging between 0 and 255.</remarks>
|
|||
/// </summary>
|
|||
public byte Cb => (byte)this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the Cr chroma component.
|
|||
/// <remarks>A value ranging between 0 and 255.</remarks>
|
|||
/// </summary>
|
|||
public byte Cr => (byte)this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="YCbCr"/> 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="YCbCr"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Color"/> to convert.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="YCbCr"/>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="YCbCr"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="YCbCr"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="YCbCr"/> 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 ==(YCbCr left, YCbCr right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="YCbCr"/> objects for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="YCbCr"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="YCbCr"/> 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 !=(YCbCr left, YCbCr right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return this.backingVector.GetHashCode(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "YCbCr [ Empty ]"; |
|||
} |
|||
|
|||
return $"YCbCr [ Y={this.Y}, Cb={this.Cb}, Cr={this.Cr} ]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is YCbCr) |
|||
{ |
|||
return this.Equals((YCbCr)obj); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(YCbCr other) |
|||
{ |
|||
return this.backingVector.Equals(other.backingVector); |
|||
} |
|||
} |
|||
} |
|||
@ -1,493 +0,0 @@ |
|||
// <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 ImageSharp.Tests |
|||
{ |
|||
using System; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using ImageSharp.Colors.Spaces; |
|||
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); |
|||
Assert.Equal(128, yCbCr.Cb); |
|||
Assert.Equal(128, yCbCr.Cr); |
|||
|
|||
// Black
|
|||
Color color2 = Color.Black; |
|||
YCbCr yCbCr2 = color2; |
|||
Assert.Equal(0, yCbCr2.Y); |
|||
Assert.Equal(128, yCbCr2.Cb); |
|||
Assert.Equal(128, yCbCr2.Cr); |
|||
|
|||
// Gray
|
|||
Color color3 = Color.Gray; |
|||
YCbCr yCbCr3 = color3; |
|||
Assert.Equal(128, yCbCr3.Y); |
|||
Assert.Equal(128, yCbCr3.Cb); |
|||
Assert.Equal(128, yCbCr3.Cr); |
|||
} |
|||
|
|||
/// <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);
|
|||
//}
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using ImageSharp.Colors.Conversion; |
|||
using System.Collections.Generic; |
|||
using ImageSharp.Colors.Spaces; |
|||
using Xunit; |
|||
|
|||
/// <summary>
|
|||
/// Tests <see cref="CieXyz"/>-<see cref="CieLab"/> conversions.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Test data generated using:
|
|||
/// http://www.brucelindbloom.com/index.html?ColorCalculator.html
|
|||
/// </remarks>
|
|||
public class CieXyzAndCieLabConversionTest |
|||
{ |
|||
private static readonly IEqualityComparer<float> FloatComparerLabPrecision = new ApproximateFloatComparer(4); |
|||
private static readonly IEqualityComparer<float> FloatComparerXyzPrecision = new ApproximateFloatComparer(6); |
|||
|
|||
/// <summary>
|
|||
/// Tests conversion from <see cref="CieLab"/> to <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>).
|
|||
/// </summary>
|
|||
[Theory] |
|||
[InlineData(100, 0, 0, 0.95047, 1, 1.08883)] |
|||
[InlineData(0, 0, 0, 0, 0, 0)] |
|||
[InlineData(0, 431.0345, 0, 0.95047, 0, 0)] |
|||
[InlineData(100, -431.0345, 172.4138, 0, 1, 0)] |
|||
[InlineData(0, 0, -172.4138, 0, 0, 1.08883)] |
|||
[InlineData(45.6398, 39.8753, 35.2091, 0.216938, 0.150041, 0.048850)] |
|||
[InlineData(77.1234, -40.1235, 78.1120, 0.358530, 0.517372, 0.076273)] |
|||
[InlineData(10, -400, 20, 0, 0.011260, 0)] |
|||
public void Convert_Lab_to_XYZ(float l, float a, float b, float x, float y, float z) |
|||
{ |
|||
// Arrange
|
|||
CieLab input = new CieLab(l, a, b, Illuminants.D65); |
|||
ColorConverter converter = new ColorConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; |
|||
|
|||
// Act
|
|||
CieXyz output = converter.ToCieXyz(input); |
|||
|
|||
// Assert
|
|||
Assert.Equal(output.X, x, FloatComparerXyzPrecision); |
|||
Assert.Equal(output.Y, y, FloatComparerXyzPrecision); |
|||
Assert.Equal(output.Z, z, FloatComparerXyzPrecision); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests conversion from <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>) to <see cref="CieLab"/>.
|
|||
/// </summary>
|
|||
[Theory] |
|||
[InlineData(0.95047, 1, 1.08883, 100, 0, 0)] |
|||
[InlineData(0, 0, 0, 0, 0, 0)] |
|||
[InlineData(0.95047, 0, 0, 0, 431.0345, 0)] |
|||
[InlineData(0, 1, 0, 100, -431.0345, 172.4138)] |
|||
[InlineData(0, 0, 1.08883, 0, 0, -172.4138)] |
|||
[InlineData(0.216938, 0.150041, 0.048850, 45.6398, 39.8753, 35.2091)] |
|||
public void Convert_XYZ_to_Lab(float x, float y, float z, float l, float a, float b) |
|||
{ |
|||
// Arrange
|
|||
CieXyz input = new CieXyz(x, y, z); |
|||
ColorConverter converter = new ColorConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; |
|||
|
|||
// Act
|
|||
CieLab output = converter.ToCieLab(input); |
|||
|
|||
// Assert
|
|||
Assert.Equal(output.L, l, FloatComparerLabPrecision); |
|||
Assert.Equal(output.A, a, FloatComparerLabPrecision); |
|||
Assert.Equal(output.B, b, FloatComparerLabPrecision); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using ImageSharp.Colors.Conversion; |
|||
using System.Collections.Generic; |
|||
using ImageSharp.Colors.Spaces; |
|||
using Xunit; |
|||
|
|||
/// <summary>
|
|||
/// Tests <see cref="CieXyz"/>-<see cref="CieLab"/> conversions.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Test data generated using original colorful library.
|
|||
/// </remarks>
|
|||
public class CieXyzAndLmsConversionTest |
|||
{ |
|||
private static readonly IEqualityComparer<float> FloatComparer = new ApproximateFloatComparer(6); |
|||
|
|||
/// <summary>
|
|||
/// Tests conversion from <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>) to <see cref="Lms"/>.
|
|||
/// </summary>
|
|||
[Theory] |
|||
[InlineData(0.941428535, 1.040417467, 1.089532651, 0.95047, 1, 1.08883)] |
|||
[InlineData(0, 0, 0, 0, 0, 0)] |
|||
[InlineData(0.850765697, -0.713042594, 0.036973283, 0.95047, 0, 0)] |
|||
[InlineData(0.2664, 1.7135, -0.0685, 0, 1, 0)] |
|||
[InlineData(-0.175737162, 0.039960061, 1.121059368, 0, 0, 1.08883)] |
|||
[InlineData(0.2262677362, 0.0961411609, 0.0484570397, 0.216938, 0.150041, 0.048850)] |
|||
public void Convert_Lms_to_CieXyz(float l, float m, float s, float x, float y, float z) |
|||
{ |
|||
// Arrange
|
|||
Lms input = new Lms(x, y, z); |
|||
ColorConverter converter = new ColorConverter(); |
|||
|
|||
// Act
|
|||
CieXyz output = converter.ToCieXyz(input); |
|||
|
|||
// Assert
|
|||
Assert.Equal(output.X, l, FloatComparer); |
|||
Assert.Equal(output.Y, m, FloatComparer); |
|||
Assert.Equal(output.Z, s, FloatComparer); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests conversion from <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>) to <see cref="Lms"/>.
|
|||
/// </summary>
|
|||
[Theory] |
|||
[InlineData(0.95047, 1, 1.08883, 0.941428535, 1.040417467, 1.089532651)] |
|||
[InlineData(0, 0, 0, 0, 0, 0)] |
|||
[InlineData(0.95047, 0, 0, 0.850765697, -0.713042594, 0.036973283)] |
|||
[InlineData(0, 1, 0, 0.2664, 1.7135, -0.0685)] |
|||
[InlineData(0, 0, 1.08883, -0.175737162, 0.039960061, 1.121059368)] |
|||
[InlineData(0.216938, 0.150041, 0.048850, 0.2262677362, 0.0961411609, 0.0484570397)] |
|||
public void Convert_CieXyz_to_Lms(float x, float y, float z, float l, float m, float s) |
|||
{ |
|||
// Arrange
|
|||
CieXyz input = new CieXyz(x, y, z); |
|||
ColorConverter converter = new ColorConverter(); |
|||
|
|||
// Act
|
|||
Lms output = converter.ToLms(input); |
|||
|
|||
// Assert
|
|||
Assert.Equal(output.L, l, FloatComparer); |
|||
Assert.Equal(output.M, m, FloatComparer); |
|||
Assert.Equal(output.S, s, FloatComparer); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using ImageSharp.Colors.Conversion; |
|||
using ImageSharp.Colors.Conversion.Implementation; |
|||
using ImageSharp.Colors.Spaces; |
|||
|
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using System.Collections.Generic; |
|||
using Xunit; |
|||
|
|||
public class ColorConverterAdaptTest |
|||
{ |
|||
private static readonly IEqualityComparer<float> FloatComparer = new ApproximateFloatComparer(4); |
|||
|
|||
[Theory] |
|||
[InlineData(0, 0, 0, 0, 0, 0)] |
|||
[InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)] |
|||
public void Adapt_CieXyz_D65_To_D50_XyzScaling(float x1, float y1, float z1, float x2, float y2, float z2) |
|||
{ |
|||
// Arrange
|
|||
CieXyz input = new CieXyz(x1, y1, z1); |
|||
CieXyz expectedOutput = new CieXyz(x2, y2, z2); |
|||
ColorConverter converter = new ColorConverter |
|||
{ |
|||
ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XYZScaling), |
|||
WhitePoint = Illuminants.D50 |
|||
}; |
|||
|
|||
// Action
|
|||
CieXyz output = converter.Adapt(input, Illuminants.D65); |
|||
|
|||
// Assert
|
|||
Assert.Equal(output.X, expectedOutput.X, FloatComparer); |
|||
Assert.Equal(output.Y, expectedOutput.Y, FloatComparer); |
|||
Assert.Equal(output.Z, expectedOutput.Z, FloatComparer); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue