Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify Color Space Conversion APIs #2739

Merged
merged 53 commits into from
Jul 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
fac508c
Can translate between profiles.
JimBobSquarePants Mar 7, 2024
3d23690
Merge branch 'main' into js/colorspace-converter
JimBobSquarePants Mar 7, 2024
8bc794a
Add CieLch
JimBobSquarePants Mar 11, 2024
6ad5e02
Merge branch 'main' into js/colorspace-converter
JimBobSquarePants Mar 13, 2024
3703cb1
Wire up Rgb working spaces
JimBobSquarePants Mar 19, 2024
66bbc6a
Remove clamping
JimBobSquarePants Mar 20, 2024
cb71ae2
Complete Rgb conversion
JimBobSquarePants Mar 20, 2024
80dc3f5
Don't over allocate
JimBobSquarePants Mar 20, 2024
07e354d
Add YCbCr conversion
JimBobSquarePants Apr 2, 2024
ad6e16d
Merge branch 'main' into js/colorspace-converter
JimBobSquarePants Apr 3, 2024
e24146b
Add Add LUV and LCHLUV
JimBobSquarePants Apr 4, 2024
7da1cae
Add XYY
JimBobSquarePants Apr 4, 2024
b8c73e1
Add CMYK
JimBobSquarePants Apr 4, 2024
55de3cc
Add more LAB tests
JimBobSquarePants Apr 4, 2024
51c3094
More LAB tests
JimBobSquarePants Apr 4, 2024
611c78d
More tests
JimBobSquarePants Apr 4, 2024
27b6084
LAB YCBCR tests
JimBobSquarePants Apr 4, 2024
55bf49c
Merge branch 'main' into js/colorspace-converter
JimBobSquarePants Apr 17, 2024
05b6cfb
Create Hsl.cs
JimBobSquarePants Apr 17, 2024
bb6502c
Create Hsv.cs
JimBobSquarePants Apr 17, 2024
698d76e
Create HunterLab.cs
JimBobSquarePants Apr 17, 2024
e7a2445
Cleanup
JimBobSquarePants Apr 17, 2024
2fbabe8
Complete HunterLab and add more tests
JimBobSquarePants Apr 18, 2024
f37ac8e
Add LMS and Lch tests
JimBobSquarePants Apr 24, 2024
00162b6
Create CieLchAndCieXyyConversionTests.cs
JimBobSquarePants Apr 24, 2024
ec14dc2
Create CieLchuvAndCmykConversionTests.cs
JimBobSquarePants Apr 29, 2024
4efb121
Create CieLuvAndCieXyyConversionTests.cs
JimBobSquarePants Apr 29, 2024
f3f1d5a
Create CieLuvAndHslConversionTests.cs
JimBobSquarePants May 1, 2024
cd6052e
Create CieLuvAndHsvConversionTests.cs
JimBobSquarePants May 1, 2024
b983cd3
Create CieLuvAndHunterLabConversionTests.cs
JimBobSquarePants May 1, 2024
9d1cb16
Add CIeLuv tests
JimBobSquarePants May 7, 2024
cc49c7a
Add Xyy and HSV/HSL tests
JimBobSquarePants May 7, 2024
9b99301
Update CieXyzAndCieLabConversionTest.cs
JimBobSquarePants May 7, 2024
3ac5f70
Complete CieXyy tests
JimBobSquarePants May 8, 2024
ba066f9
Fix namespace add first LCH test
JimBobSquarePants May 8, 2024
e67a671
Add some XYZ tests
JimBobSquarePants May 8, 2024
183a821
Merge branch 'main' into js/colorspace-converter
JimBobSquarePants May 10, 2024
9fdf204
Complete conversion tests
JimBobSquarePants May 10, 2024
a7edf1b
Some renaming and docs
JimBobSquarePants May 10, 2024
bf13f00
Naming and fixes
JimBobSquarePants May 12, 2024
72dbc13
Complete conversion tests
JimBobSquarePants May 16, 2024
c16582b
Complete tests
JimBobSquarePants May 19, 2024
355bd04
Update benchmarks
JimBobSquarePants May 20, 2024
fb50f5b
Replace old converter
JimBobSquarePants May 20, 2024
cb56bae
Remove old implementation
JimBobSquarePants May 20, 2024
b458ff2
Optimize and cleanup per review
JimBobSquarePants May 21, 2024
ff752a2
Sequential layout plus faster equality checks.
JimBobSquarePants May 22, 2024
cceeb2d
Update Rgb.cs
JimBobSquarePants May 22, 2024
9c72069
Merge branch 'main' into js/colorspace-converter
JimBobSquarePants Jun 11, 2024
87de6b9
Merge branch 'main' into js/colorspace-converter
JimBobSquarePants Jun 18, 2024
f4f1eed
Merge branch 'main' into js/colorspace-converter
JimBobSquarePants Jun 19, 2024
7838848
Merge branch 'main' into js/colorspace-converter
JimBobSquarePants Jul 3, 2024
71d49ae
Merge branch 'main' into js/colorspace-converter
JimBobSquarePants Jul 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.ColorProfiles;

/// <summary>
/// Enumerate the possible sources of the white point used in chromatic adaptation.
/// </summary>
public enum ChromaticAdaptionWhitePointSource
{
/// <summary>
/// The white point of the source color space.
/// </summary>
WhitePoint,

/// <summary>
/// The white point of the source working space.
/// </summary>
RgbWorkingSpace
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.ColorSpaces.Conversion;
namespace SixLabors.ImageSharp.ColorProfiles;

/// <summary>
/// Constants use for Cie conversion calculations
Expand All @@ -12,10 +12,10 @@ internal static class CieConstants
/// <summary>
/// 216F / 24389F
/// </summary>
public const float Epsilon = 0.008856452F;
public const float Epsilon = 216f / 24389f;

/// <summary>
/// 24389F / 27F
/// </summary>
public const float Kappa = 903.2963F;
public const float Kappa = 24389f / 27f;
}
178 changes: 178 additions & 0 deletions src/ImageSharp/ColorProfiles/CieLab.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace SixLabors.ImageSharp.ColorProfiles;

/// <summary>
/// Represents a CIE L*a*b* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly struct CieLab : IProfileConnectingSpace<CieLab, CieXyz>
{
/// <summary>
/// Initializes a new instance of the <see cref="CieLab"/> struct.
/// </summary>
/// <param name="l">The lightness dimension.</param>
/// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab(float l, float a, float b)
{
// Not clamping as documentation about this space only indicates "usual" ranges
this.L = l;
this.A = a;
this.B = b;
}

/// <summary>
/// Initializes a new instance of the <see cref="CieLab"/> struct.
/// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab(Vector3 vector)
: this()
{
this.L = vector.X;
this.A = vector.Y;
this.B = vector.Z;
}

/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public float L { get; }

/// <summary>
/// Gets the a color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public float A { get; }

/// <summary>
/// Gets the b color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public float B { get; }

/// <summary>
/// Compares two <see cref="CieLab"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="CieLab"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLab"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieLab left, CieLab right) => left.Equals(right);

/// <summary>
/// Compares two <see cref="CieLab"/> objects for inequality
/// </summary>
/// <param name="left">The <see cref="CieLab"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieLab"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right);

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static CieLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
{
// Conversion algorithm described here:
// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
CieXyz whitePoint = options.TargetWhitePoint;
float wx = whitePoint.X, wy = whitePoint.Y, wz = whitePoint.Z;

float xr = source.X / wx, yr = source.Y / wy, zr = source.Z / wz;

const float inv116 = 1 / 116F;

float fx = xr > CieConstants.Epsilon ? MathF.Pow(xr, 0.3333333F) : ((CieConstants.Kappa * xr) + 16F) * inv116;
float fy = yr > CieConstants.Epsilon ? MathF.Pow(yr, 0.3333333F) : ((CieConstants.Kappa * yr) + 16F) * inv116;
float fz = zr > CieConstants.Epsilon ? MathF.Pow(zr, 0.3333333F) : ((CieConstants.Kappa * zr) + 16F) * inv116;

float l = (116F * fy) - 16F;
float a = 500F * (fx - fy);
float b = 200F * (fy - fz);

return new CieLab(l, a, b);
}

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan<CieXyz> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));

for (int i = 0; i < source.Length; i++)
{
CieXyz xyz = source[i];
destination[i] = FromProfileConnectingSpace(options, in xyz);
}
}

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz ToProfileConnectingSpace(ColorConversionOptions options)
{
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
float l = this.L, a = this.A, b = this.B;
float fy = (l + 16) / 116F;
float fx = (a / 500F) + fy;
float fz = fy - (b / 200F);

float fx3 = Numerics.Pow3(fx);
float fz3 = Numerics.Pow3(fz);

float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa;
float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? Numerics.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa;
float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa;

CieXyz whitePoint = options.WhitePoint;
Vector3 wxyz = new(whitePoint.X, whitePoint.Y, whitePoint.Z);
Vector3 xyzr = new(xr, yr, zr);

return new(xyzr * wxyz);
}

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan<CieLab> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));

for (int i = 0; i < source.Length; i++)
{
CieLab lab = source[i];
destination[i] = lab.ToProfileConnectingSpace(options);
}
}

/// <inheritdoc/>
public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource()
=> ChromaticAdaptionWhitePointSource.WhitePoint;

/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B);

/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");

/// <inheritdoc/>
public override bool Equals(object? obj) => obj is CieLab other && this.Equals(other);

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieLab other)
=> this.AsVector3Unsafe() == other.AsVector3Unsafe();

private Vector3 AsVector3Unsafe() => Unsafe.As<CieLab, Vector3>(ref Unsafe.AsRef(in this));
}
Loading
Loading